diff --git a/.gitignore b/.gitignore index 742c5b4584..545de1cc28 100644 --- a/.gitignore +++ b/.gitignore @@ -268,7 +268,6 @@ modules/blog/app/Volo.BlogTestApp/Logs/*.* modules/blogging/app/Volo.BloggingTestApp/Logs/*.* modules/blogging/app/Volo.BloggingTestApp/wwwroot/files/*.* modules/docs/app/VoloDocs.Web/Logs/*.* -samples/BookStore/src/Acme.BookStore.Web/Logs/*.* templates/module/app/MyCompanyName.MyProjectName.DemoApp/Logs/*.* templates/mvc/src/MyCompanyName.MyProjectName.Web/Logs/*.* diff --git a/abp_io/README.md b/abp_io/README.md index 2095b52ef6..813329cbea 100644 --- a/abp_io/README.md +++ b/abp_io/README.md @@ -1,2 +1,2 @@ -## abp.op +## abp.io Source code of the official web site: https://abp.io diff --git a/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj b/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj index b0c9926b35..78d1a7ac96 100644 --- a/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj +++ b/abp_io/src/Volo.AbpWebSite.Domain/Volo.AbpWebSite.Domain.csproj @@ -12,7 +12,7 @@ - + diff --git a/abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json b/abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json index 89e3176a69..c5e2fd7c51 100644 --- a/abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json +++ b/abp_io/src/Volo.AbpWebSite.Web/publish-ignore.json @@ -3,6 +3,7 @@ "TemplateFiles/": {}, "Logs/": {}, "wwwroot/files/": {}, + "wwwroot/downloads/": {}, "appsettings.json": {}, "web.config": {} } diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj b/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj index 254d8b65c5..10004ae55e 100644 --- a/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo.Utils.SolutionTemplating.csproj @@ -15,7 +15,7 @@ - + diff --git a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs index 0901d0250b..d8a622fe83 100644 --- a/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs +++ b/abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs @@ -21,9 +21,9 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps var lines = file.GetLines(); for (var i = 0; i < lines.Length; i++) { - if (lines[i].Contains("ProjectReference") && lines[i].Contains("MyCompanyName.MyProjectName.EntityFrameworkCore")) + if (lines[i].Contains("ProjectReference") && lines[i].Contains("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations")) { - lines[i] = lines[i].Replace("EntityFrameworkCore", "MongoDB"); + lines[i] = lines[i].Replace("EntityFrameworkCore.DbMigrations", "MongoDB"); file.SetLines(lines); return; } diff --git a/build-all.ps1 b/build-all.ps1 index 8393efd9fb..7a23e66eda 100644 --- a/build-all.ps1 +++ b/build-all.ps1 @@ -20,8 +20,7 @@ $solutionPaths = ( "modules/background-jobs", "modules/client-simulation", "abp_io", - "templates/module", - "templates/service", + "templates/mvc-module", "templates/mvc", "samples/MicroserviceDemo" ) diff --git a/common.props b/common.props index 3c6c9bda58..1677ac8e8b 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ latest - 0.17.0 + 0.18.0 $(NoWarn);CS1591 https://abp.io/assets/abp_nupkg.png https://abp.io diff --git a/docs/docs-langs.json b/docs/docs-langs.json new file mode 100644 index 0000000000..851c67d605 --- /dev/null +++ b/docs/docs-langs.json @@ -0,0 +1,14 @@ +{ + "Languages":[ + { + "DisplayName" : "English", + "Code" : "en", + "IsDefault": true + }, + { + "DisplayName" : "简体中文", + "Code" : "zh-Hans", + "IsDefault": false + } + ] +} \ No newline at end of file diff --git a/docs/en/AspNetCore/Auto-API-Controllers.md b/docs/en/AspNetCore/Auto-API-Controllers.md index db7fb63642..d08f13003e 100644 --- a/docs/en/AspNetCore/Auto-API-Controllers.md +++ b/docs/en/AspNetCore/Auto-API-Controllers.md @@ -1,6 +1,6 @@ # Auto API Controllers -Once you create an [application service](Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc. +Once you create an [application service](../Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc. ABP can **automagically** configures your application services as MVC API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible fully customize it. @@ -16,7 +16,9 @@ public class BookStoreWebModule : AbpModule { Configure(options => { - options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly); + options + .ConventionalControllers + .Create(typeof(BookStoreApplicationModule).Assembly); }); } } @@ -106,7 +108,7 @@ public class PersonAppService : ApplicationService ### TypePredicate Option -You can further filter classes to become an API controller by providing the `TypePedicate` option: +You can further filter classes to become an API controller by providing the `TypePredicate` option: ````csharp services.Configure(options => diff --git a/docs/en/AspNetCore/Client-Side-Package-Management.md b/docs/en/AspNetCore/Client-Side-Package-Management.md index 3c1785daa5..0d98fe1dc5 100644 --- a/docs/en/AspNetCore/Client-Side-Package-Management.md +++ b/docs/en/AspNetCore/Client-Side-Package-Management.md @@ -22,14 +22,14 @@ The benefit of a **standard package** is: Depending on a standard package is easy. Just add it to your **package.json** file like you normally do. Example: - - { - ... - "dependencies": { - "@abp/bootstrap": "^1.0.0" - } - } - +```` +{ + ... + "dependencies": { + "@abp/bootstrap": "^1.0.0" + } +} +```` It's suggested to depend on a standard package instead of directly depending on a third-party package. diff --git a/docs/en/Best-Practices/Entities.md b/docs/en/Best-Practices/Entities.md index 03843c5278..1c76469cb7 100644 --- a/docs/en/Best-Practices/Entities.md +++ b/docs/en/Best-Practices/Entities.md @@ -63,7 +63,7 @@ public class Issue : FullAuditedAggregateRoot //Using Guid as the key/iden protected Issue() { - /* This conctructor is for ORMs to be used while getting the entity from database. + /* This constructor is for ORMs to be used while getting the entity from database. * - No need to initialize the Labels collection since it will be overrided from the database. - It's protected since proxying and deserialization tools diff --git a/docs/en/CLI.md b/docs/en/CLI.md new file mode 100644 index 0000000000..d210560910 --- /dev/null +++ b/docs/en/CLI.md @@ -0,0 +1,137 @@ +# ABP CLI + +ABP CLI (Command Line Interface) is a command line tool to perform some common operations for ABP based solutions. + +## Installation + +ABP CLI is a [dotnet global tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools). Install it using a command line window: + +````bash +dotnet tool install -g Volo.Abp.Cli +```` + +To update an existing installation: + +````bash +dotnet tool update -g Volo.Abp.Cli +```` + +## Commands + +### new + +Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md). + +Basic usage: + +````bash +abp new [options] +```` + +Example: + +````bash +abp new Acme.BookStore +```` + +* Acme.BookStore is the solution name here. +* Common convention is to name a solution is like *YourCompany.YourProject*. However, you can use different naming like *YourProject* (single level namespacing) or *YourCompany.YourProduct.YourModule* (three levels namespacing). + +#### Options + +* `--template` or `-t`: Specifies the template name. Default template name is `mvc`. Available templates: + * `mvc` (default): ASP.NET Core [MVC application template](Startup-Templates/Mvc.md). Additional options: + * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: + * `ef`: Entity Framework Core. + * `mongodb`: MongoDB. + * `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios. + * `mvc-module`: ASP.NET Core [MVC module template](Startup-Templates/Mvc-Module.md). Additional options: + * `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI). +* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory. + +### add-package + +Adds a new ABP package to a project by, + +* Adding related nuget package as a dependency to the project. +* Adding `[DependsOn(...)]` attribute to the module class in the project (see the [module development document](Module-Development-Basics.md)). + +> Notice that the added module may require additional configuration which is generally indicated in the documentation of the related package. + +Basic usage: + +````bash +abp add-package [options] +```` + +Example: + +```` +abp add-package Volo.Abp.MongoDB +```` + +* This example adds the Volo.Abp.MongoDB package to the project. + +#### Options + +* `--project` or `-p`: Specifies the project (.csproj) file path. If not specified, CLI tries to find a .csproj file in the current directory. + +### add-module + +Adds a multi-package module to a solution by finding all packages of the module, finding related projects in the solution and adding each package to the corresponding project in the solution. + +> A business module generally consists of several packages (because of layering, different database providr options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module. + +Basic usage: + +````bash +abp add-module [options] +```` + +Example: + +```bash +abp add-module Volo.Blogging +``` + +* This example add the Volo.Blogging module to the solution. + +#### Options + +* `--solution` or `-s`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory. +* `--skip-db-migrations`: For EF Core database provider, it automatically adds a new code first migration (`Add-Migration`) and updates the database (`Update-Database`) if necessary. Specify this option to skip this operation. + +### 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 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. + +### help + +Writes basic usage information of the CLI. + +Usage: + +````bash +abp help [command-name] +```` + +Examples: + +````bash +abp help # Shows a general help. +abp help new # Shows help about the "new" command. +```` + diff --git a/docs/en/Data-Seeding.md b/docs/en/Data-Seeding.md new file mode 100644 index 0000000000..0b424e7e0d --- /dev/null +++ b/docs/en/Data-Seeding.md @@ -0,0 +1,3 @@ +# Data Seeding + +TODO \ No newline at end of file diff --git a/docs/en/Exception-Handling.md b/docs/en/Exception-Handling.md index 144c6016dd..93bad2b6ed 100644 --- a/docs/en/Exception-Handling.md +++ b/docs/en/Exception-Handling.md @@ -29,7 +29,7 @@ Error Message is an instance of the `RemoteServiceErrorResponse` class. The simp } ```` -There are **optional fields** those can be filled based upon the exception that has occured. +There are **optional fields** those can be filled based upon the exception that has occurred. ##### Error Code @@ -70,7 +70,7 @@ Error **details** in an optional field of the JSON error message. Thrown `Except "code": "App:010046", "message": "Your request is not valid, please correct and try again!", "validationErrors": [{ - "message": "Username should be minimum lenght of 3.", + "message": "Username should be minimum length of 3.", "members": ["userName"] }, { diff --git a/docs/en/Getting-Started-AspNetCore-MVC-Template.md b/docs/en/Getting-Started-AspNetCore-MVC-Template.md index cb24eb543f..152452cccb 100644 --- a/docs/en/Getting-Started-AspNetCore-MVC-Template.md +++ b/docs/en/Getting-Started-AspNetCore-MVC-Template.md @@ -2,39 +2,44 @@ This tutorials explains how to create a new ASP.NET Core MVC web application using the startup template, configure and run it. -### Creating a new project +### Creating a New Project -Go to [the template creation page](https://abp.io/Templates), enter a project name and create your project as shown below: +This tutorial uses **ABP CLI** to create a new project. See the [Get Started](https://abp.io/get-started) page for other options. -![bookstore-create--template](images/bookstore-create-template.png) +Install the ABP CLI using a command line window, if you've not installed before: -When you click the *create* button, a new Visual Studio solution is created and downloaded with the name you have provided. +````bash +dotnet tool install -g Volo.Abp.Cli +```` -#### Pre Requirements +Use `abp new` command in an empty folder to create your project: -The downloaded project requires; +```` +abp new Acme.BookStore +```` -* [Visual Studio 2017 (v15.7.0+)](https://visualstudio.microsoft.com/tr/downloads/) -* [.NET Core 2.1.1+](https://www.microsoft.com/net/download/dotnet-core/) +> You can use different level of namespaces; e.g. BookStore, Acme.BookStore or Acme.Retail.BookStore. -### The Solution Structure +`new` command creates a **layered MVC application** with **Entity Framework Core** as the database provider. However, it has additional options. See the [CLI documentation](CLI.md) for all available options. -Extract the zip file downloaded and open in **Visual Studio 2017 (15.9.0+)**: +#### Pre Requirements -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) +The created solution requires; -The solution has a layered structure (based on Domain Driven Design) where; +* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/) +* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/) -* ``.Domain`` is the domain layer. -* ``.Application`` is the application layer. -* ``.Web`` is the presentation layer. -* ``.EntityFrameworkCore`` is the EF Core integration package. +### The Solution Structure + +Open the solution in **Visual Studio**: + +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png) -EF Core database migrations are separated to a project named `.EntityFrameworkCore.DbMigrations`. +The solution has a layered structure (based on [Domain Driven Design](Domain-Driven-Design.md)) and contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database. -The solution also contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database. +> See [MVC application template document](Startup-Templates/Mvc.md) to understand the solution structure in details. -### Creating the Database +### Database Connection String Check the **connection string** in the `appsettings.json` file under the `.Web` project: @@ -46,9 +51,29 @@ Check the **connection string** in the `appsettings.json` file under the `.Web` } ```` -The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use another DBMS if you want. +The solution is configured to use **Entity Framework Core** with **MS SQL Server**. EF Core supports [various](https://docs.microsoft.com/en-us/ef/core/providers/) database providers, so you can use another DBMS if you want. Change the connection string if you need. + +### Create Database & Apply Database Migrations + +You have two options to create the database. -Right click to the `.Web` project and select **Set as StartUp Project**: +#### Using the DbMigrator Application + +The solution contains a console application (named `Acme.BookStore.DbMigrator` in this sample) that can create database, apply migrations and seed initial data. It is useful on development as well as on production environment. + +> `.DbMigrator` project has its own `appsettings.json`. So, if you have changed the connection string above, you should also change this one. + +Right click to the `.DbMigrator` project and select **Set as StartUp Project**: + +![set-as-startup-project](images/set-as-startup-project.png) + +Hit F5 (or Ctrl+F5) to run the application. It will have an output like shown below: + +![set-as-startup-project](images/db-migrator-app.png) + +#### Using EF Core Update-Database Command + +Ef Core has `Update-Database` command which creates database if necessary and applies pending migrations. Right click to the `.Web` project and select **Set as StartUp Project**: ![set-as-startup-project](images/set-as-startup-project.png) @@ -58,17 +83,19 @@ Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` This will create a new database based on the configured connection string. +> Using the `.Migrator` tool is the suggested way, because it also seeds the initial data to be able to properly run the web application. + ### Running the Application -You can now run the application which will open the **home** page: +Ensure that the `.Web` project is the startup project. Run the application which will open the **home** page in your browser: ![bookstore-homepage](images/bookstore-homepage.png) Click the **Login** button, enter `admin` as the username and `1q2w3E*` as the password to login to the application. -The startup template includes the **identity management** module. Once you login, the Identity management menu will be available where you can manage **roles**, **users** and their **permissions**. +The startup template includes the **identity management** and **tenant management** modules. Once you login, the Administration menu will be available where you can manage **tenants**, **roles**, **users** and their **permissions**. User management page is shown below: -![bookstore-user-management](images/bookstore-user-management.png) +![bookstore-user-management](images/bookstore-user-management-v2.png) ### What's Next? diff --git a/docs/en/Index.md b/docs/en/Index.md index b1bccab0ea..d911bf66e2 100644 --- a/docs/en/Index.md +++ b/docs/en/Index.md @@ -6,7 +6,7 @@ Explore the left navigation menu to deep dive in the documentation. ## Project Status -ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in early preview stage and not ready to use in production. The documentation is still in progress and it is incomplete yet. +ABP is the **next generation** of the open source [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework. It's currently in early preview stage and not ready to use in production. The documentation is still in progress and it is far from complete. For short-term and production level applications, it's suggested to use [ASP.NET Boilerplate](https://aspnetboilerplate.com/) framework which has rich feature set, mature, actively maintained and up-to-date. diff --git a/docs/en/Microservice-Architecture.md b/docs/en/Microservice-Architecture.md index e30c070a01..ff85873ee7 100644 --- a/docs/en/Microservice-Architecture.md +++ b/docs/en/Microservice-Architecture.md @@ -19,7 +19,7 @@ One of the major goals of the ABP framework is to provide a convenient infrastru ## Microservice for New Applications -One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challanges of the microservice architecture. +One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challenges of the microservice architecture. However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts. diff --git a/docs/en/Module-Development-Basics.md b/docs/en/Module-Development-Basics.md index 6a0561a0c5..8149ec2cb1 100644 --- a/docs/en/Module-Development-Basics.md +++ b/docs/en/Module-Development-Basics.md @@ -112,7 +112,7 @@ Lastly, you can override ``OnApplicationShutdown`` method if you want to execute ## Module Dependencies -In a modular application, it's not unusual for one module to depend upon another module(s). An Abp module must declare ``[DependsOn]`` attribute if it does have a dependcy upon another module, as shown below: +In a modular application, it's not unusual for one module to depend upon another module(s). An Abp module must declare ``[DependsOn]`` attribute if it does have a dependency upon another module, as shown below: ````C# [DependsOn(typeof(AbpAspNetCoreMvcModule))] diff --git a/docs/en/Modules/Index.md b/docs/en/Modules/Index.md index 9dc5da7a99..2771c9a8a9 100644 --- a/docs/en/Modules/Index.md +++ b/docs/en/Modules/Index.md @@ -21,6 +21,6 @@ There are some **free and open source** application modules developed and mainta * **Permission Management**: Used to persist permissions. * **Setting Management**: Used to persist settings. * **Tenant Management**: Used to manage tenants for a [multi-tenant](../Multi-Tenancy.md) application. -* **Users**: Used the abstract users, so other modules can depend on this instead of the Identity module. +* **Users**: Used to abstract users, so other modules can depend on this instead of the Identity module. Documenting the modules is in the progress. See [this repository](https://github.com/abpframework/abp/tree/master/modules) for source code of all modules. \ No newline at end of file diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md index ee0648f483..f5510a82db 100644 --- a/docs/en/Multi-Tenancy.md +++ b/docs/en/Multi-Tenancy.md @@ -56,7 +56,7 @@ namespace MyCompany.MyProject } ```` -IMultiTenant requires to define a **TenantId** property in the implementing entity (See entity documentation (TODO: link) for more about entities). +IMultiTenant requires to define a **TenantId** property in the implementing entity (See [entity documentation](Entities.md) for more about entities). #### Obtain Current Tenant's Id diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index 3e4a46a4f5..e6b55cb3de 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -48,8 +48,8 @@ In this example; Generic Repositories provides some standard CRUD features out of the box: -* Providers `Insert` method to save a new entity. -* Providers `Update` and `Delete` methods to update or delete an entity by entity object or it's id. +* Provides `Insert` method to save a new entity. +* Provides `Update` and `Delete` methods to update or delete an entity by entity object or it's id. * Provides `Delete` method to delete multiple entities by a filter. * Implements `IQueryable`, so you can use LINQ and extension methods like `FirstOrDefault`, `Where`, `OrderBy`, `ToList` and so on... * Have **sync** and **async** versions for all methods. diff --git a/docs/en/Samples/Microservice-Demo.md b/docs/en/Samples/Microservice-Demo.md index 4edebfbe03..bc3c8c0933 100644 --- a/docs/en/Samples/Microservice-Demo.md +++ b/docs/en/Samples/Microservice-Demo.md @@ -168,7 +168,7 @@ These are the actual applications those have user interfaces to interact to the - **AuthServer.Host**: Host the IdentityServer4 to provide an authentication service to other services and applications. It is a single-sign server and contains the login page. - **BackendAdminApp.Host**: This is a backend admin application that host UI for Identity and Product management modules. -- **PubicWebSite.Host**: As public web site that contains a simple product list page and blog module UI. +- **PublicWebSite.Host**: As public web site that contains a simple product list page and blog module UI. - **ConsoleClientDemo**: A simple console application to demonstrate the usage of services from a C# application. ### Gateways / BFFs (Backend for Frontend) @@ -1471,4 +1471,4 @@ ABP provides automatic audit logging which saves every request in detail (who is All of the services and applications are configured to write audit logs. Audit logs are saved to the MsDemo_Identity SQL database. So, you can query all audit logs of all applications from a single point. -An Audit Log record has a `CorrelationId` property that can be used to track a request. When a service calls another service in a single web request, they both save audit logs with the same `CorrelationId`. See the `AbpAuditLogs` table in the database. \ No newline at end of file +An Audit Log record has a `CorrelationId` property that can be used to track a request. When a service calls another service in a single web request, they both save audit logs with the same `CorrelationId`. See the `AbpAuditLogs` table in the database. diff --git a/docs/en/Startup-Templates/Index.md b/docs/en/Startup-Templates/Index.md new file mode 100644 index 0000000000..cb7e020663 --- /dev/null +++ b/docs/en/Startup-Templates/Index.md @@ -0,0 +1,11 @@ +# Startup Templates + +While you can start with an empty project and add needed packages manually, startup templates makes easy and comfortable to start a new solution with the ABP framework. + +Click to the name from the list below to see the documentation of the related startup template: + +* [**mvc**](Mvc.md): ASP.NET Core MVC application template. +* [**mvc-module**](Mvc-Module.md): ASP.NET Core MVC module/service template. + + + diff --git a/docs/en/Startup-Templates/Mvc-Module.md b/docs/en/Startup-Templates/Mvc-Module.md new file mode 100644 index 0000000000..b641d78bac --- /dev/null +++ b/docs/en/Startup-Templates/Mvc-Module.md @@ -0,0 +1,6 @@ +# MVC Module Startup Template + +TODO + + + diff --git a/docs/en/Startup-Templates/Mvc.md b/docs/en/Startup-Templates/Mvc.md new file mode 100644 index 0000000000..b44105a4f6 --- /dev/null +++ b/docs/en/Startup-Templates/Mvc.md @@ -0,0 +1,183 @@ +# MVC Application Startup Template + +## Introduction + +This template provides a layered (or tiered, based on the preference) application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. + +## How to Start With + +You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here. + +First, install the ABP CLI as described in [its document](../CLI.md). Then use the `abp new` command in an empty folder to create a new solution: + +````bash +abp new Acme.BookStore -t mvc +```` + +* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. See the [CLI document](../CLI.md) for different naming styles. +* This example specified the template name (`-t` or `--template` option). However, `mvc` is the default template and used even if you don't specify it. + +### Specify Database Provider + +This template supports the following database providers: + +- `ef`: Entity Framework Core (default) +- `mongodb`: MongoDB + +Use the `-d` (or `--database-provider`) to specify the database provider: + +````bash +abp new Acme.BookStore -t mvc -d mongodb +```` + +### Create a Tiered Solution + +`--tiered` option is used to create a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios. + +````bash +abp new Acme.BookStore --tiered +```` + +See the "Tiered Structure" section below for the tiered approach. + +## Solution Structure + +Based on the options you've specified, you will get a slightly different solution structure. + +### Default Structure + +If you don't specify any option, you will have a solution like shown below: + +![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png) + +Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before. + +-------------------- + +**TODO: Add a graphic to illustrate dependencies between projects.** + +------------------ + +Each section below will explain the related project. + +#### .Domain Project + +This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects of the solution. + +A `Book` entity and a `IBookRepository` interface are good candidates for this project. + +* Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project. + +#### .Domain.Shared Project + +This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution. + +A `BookType` enum and a `BookConts` class (which may have some constant fields for the `Book` entity, like `MaxNameLength`) are good candidates for this project. + +This project has no dependency to other projects in the solution. + +#### .Application.Contracts Project + +This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package. + +* Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects in the application service interfaces and DTOs. + +#### .Application Project + +This project contains the [application service](../Application-Services.md) implementations of the interfaces defined in the `.Application.Contracts` project. + +* Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs. +* Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic. + +#### .EntityFrameworkCore Project. + +This is the integration project for the EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project. + +* Depends on the `.Domain` project to be able to reference to entities and repository interfaces. + +> This project is available only if you are using EF Core as the database provider. + +#### .EntityFrameworkCore.DbMigrations Project + +Contains EF Cor database migrations for the solution. It has a separated `DbContext` to dedicated to manage migrations. + +ABP is a modular framework and with an ideal design, each module has its own `DbContext` class. This is where the migration `DbContext` comes into play and unifies all `DbContext` configurations into a single model to maintain a single database schema. + +Notice that the migration `DbContext` is only used for database migrations and *not used on runtime*. + +* Depends on the `.EntityFrameworkCore` project since it re-uses the configuration defined for the `DbContext` of the application. + +> This project is available only if you are using EF Core as the database provider. + +#### .DbMigrator Project + +This is a console application which simplifies to execute database migrations on development and production environments. When you this application; + +* Creates the database if necessary. +* Applies database migrations. +* Seeds initial data. + +> This project has its own `appsettings.json` file. So, if you want to change the database connection string, remember to change this file. + +Especially, seeding initial data is important at this point. ABP has a modular data seed infrastructure. See [its documentation](../Data-Seeding.md) for more about the data seeding. + +While creating database & applying migrations seems only necessary for relational databases, this projects comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds initial data which is necessary for the application. + +* Depends on the `.EntityFrameworkCore.DbMigrations` project (for EF Core) since it needs to access to the migrations. +* Depends on the `.Application.Contracts` project to be able to access permission definitions, because initial data seeder grants permissions for the admin user. + +#### .HttpApi Project + +This project is used to define your API Controllers. + +Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../AspNetCore/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it. + +* Depends on the `.Application.Contracts` project to be able to inject the application service interfaces. + +#### .HttpApi.Client Project + +This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications. + +`.HttpApi.Client.ConsoleTestApp` project is a console application created to demonstrate the usage of the client proxies. + +Most of time you don't need to manually create C# client proxies, thanks to ABP's [Cynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature. + +* Depends on the `.Application.Contracts` project to be able to share the same application service interfaces and DTOs with the remote service. + +#### .Web Project + +This project contains the User Interface (UI) of the application. It contains razor pages, javascript files, css files, images and so on... + +* Depends on the `.HttpApi` since UI layer needs to use APIs and application service interfaces of the solution. + +> If you check the source code of the `.Web.csproj` file, you will see the references to the `.Application` and the `.EntityFrameworkCore.DbMigrations` projects. +> +> These references are actually not needed on development, because UI layer normally doesn't depend on the EF Core or the Application implementation. This startup templates are ready for the tiered deployment, where API layer is hosted in a separate server than the UI layer. +> +> However, if you don't choose the `--tiered` option, these references will be in the .Web project to be able to host the Web, API and application layers in a single application endpoint. +> +> This gives you to ability to use domain entities in your presentation layer. However, this is considered as a bad practice according to the DDD. + +#### Test Projects + +The solution has multiple test projects, one for each layer: + +* `.Domain.Tests` is used to test the domain layer. +* `.Application.Tests` is used to test the application layer. +* `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories. +* `.Web.Tests` is used to test the UI. +* `.TestBase` is a base (shared) project for all tests. + +In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application. + +### Tiered Structure + +TODO + +### Other Database Providers + +TODO + +#### MongoDB + +TODO \ No newline at end of file diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md index a841b6ad65..288f7fe075 100644 --- a/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md +++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-I.md @@ -2,43 +2,45 @@ ### About this Tutorial -In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider (as it comes pre-configured with the [startup template](https://abp.io/Templates)). +In this tutorial series, you will build an application that is used to manage a list of books & their authors. **Entity Framework Core** (EF Core) will be used as the ORM provider as it is the default database provider. -This is the first part of the tutorial series. See all parts: +This is the first part of the ASP.NET Core MVC tutorial series. See all parts: - **Part I: Create the project and a book list page (this tutorial)** - [Part II: Create, Update and Delete books](Part-II.md) - [Part III: Integration Tests](Part-III.md) -You can download the **source code** of the application [from here](https://github.com/volosoft/abp/tree/master/samples/BookStore). +You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore). ### Creating the Project -Go to the [startup template page](https://abp.io/Templates) and download a new project named `Acme.BookStore`, create the database and run the application by following the [template document](../../Getting-Started-AspNetCore-MVC-Template.md). +Create a new project named `Acme.BookStore`, create the database and run the application by following the [Getting Started document](../../Getting-Started-AspNetCore-MVC-Template.md). ### Solution Structure -This is the how the layered solution structure looks after it's created from the startup template: +This is how the layered solution structure looks after it's created: -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png) + +> You can see [MVC application template document](../../Startup-Templates/Mvc.md) to understand the solution structure in details. However, you will understand the basics with this tutorial. ### Create the Book Entity -Define [entities](../../Entities.md) in the **domain layer** (`Acme.BookStore.Domain` project) of the solution. The main entity of the application is the `Book`: +Domain layer in the startup template is separated into two projects: + +- `Acme.BookStore.Domain` contains your [entities](../../Entities.md), [domain services](../../Domain-Services.md) and other core domain objects. +- `Acme.BookStore.Domain.Shared` contains constants, enums or other domain related objects those can be shared with clients. + +Define [entities](../../Entities.md) in the **domain layer** (`Acme.BookStore.Domain` project) of the solution. The main entity of the application is the `Book`. Create a class, named `Book`, in the `Acme.BookStore.Domain` project as shown below: ````C# using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using Volo.Abp.Domain.Entities.Auditing; namespace Acme.BookStore { - [Table("Books")] public class Book : AuditedAggregateRoot { - [Required] - [StringLength(128)] public string Name { get; set; } public BookType Type { get; set; } @@ -50,22 +52,22 @@ namespace Acme.BookStore } ```` -* ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is one of the **Domain Driven Design (DDD)** concepts. See [entity document](../../Entities.md) for more details and best practices. +* ABP has two fundamental base classes for entities: `AggregateRoot` and `Entity`. **Aggregate Root** is one of the **Domain Driven Design (DDD)** concepts. See [entity document](../../Entities.md) for details and best practices. * `Book` entity inherits `AuditedAggregateRoot` which adds some auditing properties (`CreationTime`, `CreatorId`, `LastModificationTime`... etc.) on top of the `AggregateRoot` class. * `Guid` is the **primary key type** of the `Book` entity. * Used **data annotation attributes** in this code for EF Core mappings. Alternatively you could use EF Core's [fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling) instead. #### BookType Enum -The `BookType` enum used above is defined as below: +Define the `BookType` enum in the `Acme.BookStore.Domain.Shared` project: ````C# namespace Acme.BookStore { - public enum BookType : byte + public enum BookType { Undefined, - Advanture, + Adventure, Biography, Dystopia, Fantastic, @@ -91,14 +93,15 @@ EF Core requires you to relate entities with your DbContext. The easiest way to #### Configure Your Book Entity -Open BookStoreDbContextModelCreatingExtensions.cs file from the `Acme.BookStore.EntityFrameworkCore` project, add following code to the end of ConfigureBookStore method to configure Book entity: +Open `BookStoreDbContextModelCreatingExtensions.cs` file in the `Acme.BookStore.EntityFrameworkCore` project and add following code to the end of the `ConfigureBookStore` method to configure the Book entity: ````C# - builder.Entity(b => - { - b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema); - b.ConfigureExtraProperties(); - }); +builder.Entity(b => +{ + b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema); + b.ConfigureAuditedAggregateRoot(); //auto configure for the base class props + b.Property(x => x.Name).IsRequired().HasMaxLength(128); +}); ```` #### Add New Migration & Update the Database @@ -115,26 +118,27 @@ PM> Update-Database #### Add Sample Data -`Update-Database` command created the `Books` table in the database. Open your database and enter a few sample rows, so you can show them on the page: +`Update-Database` command created the `AppBooks` table in the database. Open your database and enter a few sample rows, so you can show them on the page: ![bookstore-books-table](images/bookstore-books-table.png) ### Create the Application Service -The next step is to create an [application service](../../Application-Services.md) to manage (create, list, update, delete...) the books. +The next step is to create an [application service](../../Application-Services.md) to manage (create, list, update, delete...) the books. Application layer in the startup template is separated into two projects: + +* `Acme.BookStore.Application.Contracts` mainly contains your DTOs and application service interfaces. +* `Acme.BookStore.Application` contains the implementations of your application services. #### BookDto -Create a DTO class named `BookDto` into the `Acme.BookStore.Application` project: +Create a DTO class named `BookDto` into the `Acme.BookStore.Application.Contracts` project: ````C# using System; using Volo.Abp.Application.Dtos; -using Volo.Abp.AutoMapper; namespace Acme.BookStore { - [AutoMapFrom(typeof(Book))] public class BookDto : AuditedEntityDto { public string Name { get; set; } @@ -151,20 +155,34 @@ namespace Acme.BookStore * **DTO** classes are used to **transfer data** between the *presentation layer* and the *application layer*. See the [Data Transfer Objects document](../../Data-Transfer-Objects.md) for more details. * `BookDto` is used to transfer book data to the presentation layer in order to show the book information on the UI. * `BookDto` is derived from the `AuditedEntityDto` which has audit properties just like the `Book` class defined above. -* `[AutoMapFrom(typeof(Book))]` is used to create AutoMapper mapping from the `Book` class to the `BookDto` class. In this way, you get automatic convertion of `Book` objects to `BookDto` objects (instead of manually copy all properties). + +It will be needed to convert `Book` entities to `BookDto` objects while returning books to the presentation layer. [AutoMapper](https://automapper.org) library can automate this conversion when you define the proper mapping. Startup template comes with AutoMapper configured, so you can just define the mapping in the `BookStoreApplicationAutoMapperProfile` class in the `Acme.BookStore.Application` project: + +````csharp +using AutoMapper; + +namespace Acme.BookStore +{ + public class BookStoreApplicationAutoMapperProfile : Profile + { + public BookStoreApplicationAutoMapperProfile() + { + CreateMap(); + } + } +} +```` #### CreateUpdateBookDto -Create a DTO class named `CreateUpdateBookDto` into the `Acme.BookStore.Application` project: +Create a DTO class named `CreateUpdateBookDto` into the `Acme.BookStore.Application.Contracts` project: ````c# using System; using System.ComponentModel.DataAnnotations; -using Volo.Abp.AutoMapper; namespace Acme.BookStore { - [AutoMapTo(typeof(Book))] public class CreateUpdateBookDto { [Required] @@ -184,11 +202,17 @@ namespace Acme.BookStore ```` * This DTO class is used to get book information from the user interface while creating or updating a book. -* It defines data annotation attributes (like `[Required]`) to define validations for the properties. DTOs are automatically validated by ABP. +* It defines data annotation attributes (like `[Required]`) to define validations for the properties. DTOs are [automatically validated](../../Validation.md) by the ABP framework. + +Like done for the `BookDto` above, create a mapping from the `CreateUpdateBookDto` object to the `Book` entity: + +````csharp +CreateMap(); +```` #### IBookAppService -Define an interface named `IBookAppService` for the book application service: +Define an interface named `IBookAppService` in the `Acme.BookStore.Application.Contracts` project: ````C# using System; @@ -210,13 +234,13 @@ namespace Acme.BookStore } ```` -* Defining interfaces for application services is not required by the framework. However, it's suggested as best practice. -* `IAsyncCrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead, you could inherit from the empty `IApplicationService` interface and define your own methods. -* There are some variations of the `IAsyncCrudAppService` where you can use a single DTO or separated DTOs for each method. +* Defining interfaces for application services is not required by the framework. However, it's suggested as a best practice. +* `IAsyncCrudAppService` defines common **CRUD** methods: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` and `DeleteAsync`. It's not required to extend it. Instead, you could inherit from the empty `IApplicationService` interface and define your own methods manually. +* There are some variations of the `IAsyncCrudAppService` where you can use separated DTOs for each method. #### BookAppService -Implement the `IBookAppService` as named `BookAppService`: +Implement the `IBookAppService` as named `BookAppService` in the `Acme.BookStore.Application` project: ````C# using System; @@ -241,23 +265,23 @@ namespace Acme.BookStore ```` * `BookAppService` is derived from `AsyncCrudAppService<...>` which implements all the CRUD methods defined above. -* `BookAppService` injects `IRepository` which is the default repository created for the `Book` entity. ABP automatically creates repositories for each aggregate root (or entity). See the [repository document](../../Repositories.md). -* `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as object mapping provider. You defined mappings using the `AutoMapFrom` and the `AutoMapTo` attributes above. See the [AutoMapper integration document](../../AutoMapper-Integration.md) for details. +* `BookAppService` injects `IRepository` which is the default repository for the `Book` entity. ABP automatically creates default repositories for each aggregate root (or entity). See the [repository document](../../Repositories.md). +* `BookAppService` uses `IObjectMapper` to convert `Book` objects to `BookDto` objects and `CreateUpdateBookDto` objects to `Book` objects. The Startup template uses the [AutoMapper](http://automapper.org/) library as the object mapping provider. You defined the mappings before, so it will work as expected. ### Auto API Controllers -You normally create **Controllers** to expose application services as **HTTP API** endpoints. Thus allowing browser or 3rd-party clients to call them via AJAX. - -ABP can [**automagically**](../../AspNetCore/Auto-API-Controllers.md) configures your application services as MVC API Controllers by convention. +You normally create **Controllers** to expose application services as **HTTP API** endpoints. Thus allowing browser or 3rd-party clients to call them via AJAX. ABP can [**automagically**](../../AspNetCore/Auto-API-Controllers.md) configures your application services as MVC API Controllers by convention. #### Swagger UI -The startup template is configured to run the [swagger UI](https://swagger.io/tools/swagger-ui/) using the [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) library. Run the application and enter `http://localhost:53929/swagger/` as URL on your browser. +The startup template is configured to run the [swagger UI](https://swagger.io/tools/swagger-ui/) using the [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) library. Run the application and enter `https://localhost:XXXX/swagger/` (replace XXXX by your own port) as URL on your browser. You will see some built-in service endpoints as well as the `Book` service and its REST-style endpoints: ![bookstore-swagger](images/bookstore-swagger.png) +Swagger has a nice UI to test APIs. You can try to execute the `[GET] /api/app/book` API to get a list of books. + ### Dynamic JavaScript Proxies It's common to call HTTP API endpoints via AJAX from the **JavaScript** side. You can use `$.ajax` or another tool to call the endpoints. However, ABP offers a better way. @@ -266,7 +290,7 @@ ABP **dynamically** creates JavaScript **proxies** for all API endpoints. So, yo #### Testing in the Browser Developer Console -You can easily test the JavaScript proxy using your favorite browser's **Developer Console** now. Run the application again, open your browser's **developer tools** (shortcut: F12), switch to the **Console** tab, type the following code and press enter: +You can easily test the JavaScript proxies using your favorite browser's **Developer Console** now. Run the application, open your browser's **developer tools** (shortcut: F12), switch to the **Console** tab, type the following code and press enter: ````js acme.bookStore.book.getList({}).done(function (result) { console.log(result); }); @@ -275,7 +299,7 @@ acme.bookStore.book.getList({}).done(function (result) { console.log(result); }) * `acme.bookStore` is the namespace of the `BookAppService` converted to [camelCase](https://en.wikipedia.org/wiki/Camel_case). * `book` is the conventional name for the `BookAppService` (removed AppService postfix and converted to camelCase). * `getList` is the conventional name for the `GetListAsync` method defined in the `AsyncCrudAppService` base class (removed Async postfix and converted to camelCase). -* `{}` argument is used to send an empty object to the `GetListAsync` method which normally expects an object of type `PagedAndSortedResultRequestDto` which is used to send paging and sorting options to the server. +* `{}` argument is used to send an empty object to the `GetListAsync` method which normally expects an object of type `PagedAndSortedResultRequestDto` that is used to send paging and sorting options to the server (all properties are optional, so you can send an empty object). * `getList` function returns a `promise`. So, you can pass a callback to the `done` (or `then`) function to get the result from the server. Running this code produces the following output: @@ -298,7 +322,7 @@ You should see a message in the console something like that: successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7 ```` -Check the `books` table in the database to see the new book row. You can try `get`, `update` and `delete` functions too. +Check the `Books` table in the database to see the new book row. You can try `get`, `update` and `delete` functions yourself. ### Create the Books Page @@ -306,20 +330,21 @@ It's time to create something visible and usable! Instead of classic MVC, we wil Create a new `Books` folder under the `Pages` folder of the `Acme.BookStore.Web` project and add a new Razor Page named `Index.cshtml`: -![bookstore-add-index-page](images/bookstore-add-index-page.png) +![bookstore-add-index-page](images/bookstore-add-index-page-v2.png) Open the `Index.cshtml` and change the content as shown below: ````html @page -@using Acme.BookStore.Pages.Books -@inherits Acme.BookStore.Pages.BookStorePageBase +@using Acme.BookStore.Web.Pages.Books +@inherits Acme.BookStore.Web.Pages.BookStorePageBase @model IndexModel

Books

```` -* Change the default inhertitance of the Razor View Page Model so it **inherits** from the `BookStorePageBase` class (instead of `PageModel`). The `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages. +* This code changes the default inheritance of the Razor View Page Model so it **inherits** from the `BookStorePageBase` class (instead of `PageModel`). The `BookStorePageBase` class which comes with the startup template and provides some shared properties/methods used by all pages. +* Ensure that the `IndexModel` (*Index.cshtml.cs)* has the `Acme.BookStore.Pages.Books` namespace, or update it in the `Index.cshtml`. #### Add Books Page to the Main Menu @@ -334,17 +359,16 @@ context.Menu.AddItem( #### Localizing the Menu Items -Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain` project: +Localization texts are located under the `Localization/BookStore` folder of the `Acme.BookStore.Domain.Shared` project: -![bookstore-localization-files](images/bookstore-localization-files.png) +![bookstore-localization-files](images/bookstore-localization-files-v2.png) -Open the `en.json` file and add localization texts for `Menu:BookStore` and `Menu:Books` keys: +Open the `en.json` file and add localization texts for `Menu:BookStore` and `Menu:Books` keys to the end of the file: ````json { "culture": "en", "texts": { - //... "Menu:BookStore": "Book Store", "Menu:Books": "Books" } @@ -352,9 +376,9 @@ Open the `en.json` file and add localization texts for `Menu:BookStore` and `Men ```` * ABP's localization system is built on [ASP.NET Core's standard localization](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) system and extends it in many ways. See the [localization document](../../Localization.md) for details. -* Localization key names are arbitrary, you can set any name. We prefer to add `Menu` namespace for menu items to distinguish from other texts. If a text is not defined in the localization file, it **fallbacks** to the localization key (ASP.NET Core's standard behavior). +* Localization key names are arbitrary. You can set any name. We prefer to add `Menu:` prefix for menu items to distinguish from other texts. If a text is not defined in the localization file, it **fallbacks** to the localization key (ASP.NET Core's standard behavior). -Run the application and see the menu items are added to the top bar: +Run the application and see the new menu item has been added to the top bar: ![bookstore-menu-items](images/bookstore-menu-items.png) @@ -362,17 +386,16 @@ When you click to the Books menu item, you are redirected to the new Books page. #### Book List -We will use the [Datatables.net](https://datatables.net/) JQuery plugin to show list of tables on the page. Datatables can completely work via AJAX, so it is fast and provides a good user experience. Datatables plugin is configured in the startup template, so you can directly use it in any page without including any style or script file to your page. +We will use the [Datatables.net](https://datatables.net/) JQuery plugin to show list of tables on the page. Datatables can completely work via AJAX, it is fast and provides a good user experience. Datatables plugin is configured in the startup template, so you can directly use it in any page without including any style or script file to your page. -##### Index.cshtml Changes +##### Index.cshtml Change the `Pages/Books/Index.cshtml` as following: ````html @page -@using Acme.BookStore.Pages.Books -@inherits Acme.BookStore.Pages.BookStorePageBase -@model IndexModel +@inherits Acme.BookStore.Web.Pages.BookStorePageBase +@model Acme.BookStore.Web.Pages.Books.IndexModel @section scripts { @@ -405,7 +428,7 @@ Change the `Pages/Books/Index.cshtml` as following: Create `index.js` JavaScript file under the `Pages/Books/` folder: -![bookstore-index-js-file](images/bookstore-index-js-file.png) +![bookstore-index-js-file](images/bookstore-index-js-file-v2.png) `index.js` content is shown below: @@ -425,7 +448,7 @@ $(function () { ```` * `abp.libs.datatables.createAjax` is a helper function to adapt ABP's dynamic JavaScript API proxies to Datatable's format. -* `abp.libs.datatables.normalizeConfiguration` is another helper function. There's no requirment to use it, but it simplifies the datatables configuration by providing conventional values for missing options. +* `abp.libs.datatables.normalizeConfiguration` is another helper function. There's no requirement to use it, but it simplifies the datatables configuration by providing conventional values for missing options. * `acme.bookStore.book.getList` is the function to get list of books (you have seen it before). * See [Datatable's documentation](https://datatables.net/manual/) for more configuration options. diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md index eeb14db8bb..180133d713 100644 --- a/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md +++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-II.md @@ -2,13 +2,13 @@ ### About this Tutorial -This is the second part of the tutorial series. See all parts: +This is the second part of the ASP.NET Core MVC tutorial series. See all parts: * [Part I: Create the project and a book list page](Part-I.md) * **Part II: Create, Update and Delete books (this tutorial)** * [Part III: Integration Tests](Part-III.md) -You can download the **source code** of the application [from here](https://github.com/volosoft/abp/tree/master/samples/BookStore). +You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore). ### Creating a New Book @@ -20,7 +20,7 @@ In this section, you will learn how to create a new modal dialog form to create Create a new razor page, named `CreateModal.cshtml` under the `Pages/Books` folder of the `Acme.BookStore.Web` project: -![bookstore-add-create-dialog](images/bookstore-add-create-dialog.png) +![bookstore-add-create-dialog](images/bookstore-add-create-dialog-v2.png) ##### CreateModal.cshtml.cs @@ -30,7 +30,7 @@ Open the `CreateModal.cshtml.cs` file (`CreateModalModel` class) and replace wit using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -namespace Acme.BookStore.Pages.Books +namespace Acme.BookStore.Web.Pages.Books { public class CreateModalModel : BookStorePageModelBase { @@ -63,9 +63,9 @@ Open the `CreateModal.cshtml` file and paste the code below: ````html @page -@inherits Acme.BookStore.Pages.BookStorePageBase +@inherits Acme.BookStore.Web.Pages.BookStorePageBase @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal -@model Acme.BookStore.Pages.Books.CreateModalModel +@model Acme.BookStore.Web.Pages.Books.CreateModalModel @{ Layout = null; } @@ -83,7 +83,7 @@ Open the `CreateModal.cshtml` file and paste the code below: * This modal uses `abp-dynamic-form` tag helper to automatically create the form from the `CreateBookViewModel` class. * `abp-model` attribute indicates the model object, the `Book` property in this case. * `data-ajaxForm` attribute makes the form submitting via AJAX, instead of a classic page post. - * `abp-form-content` tag helper is a placeholder to render the form controls (this is optional and needed only if you added some other content in the `abp-dynamic-form` tag, just like in this view). + * `abp-form-content` tag helper is a placeholder to render the form controls (this is optional and needed only if you added some other content in the `abp-dynamic-form` tag, just like in this page). #### Add the "New book" Button @@ -138,12 +138,12 @@ Create a new razor page, named `EditModal.cshtml` under the `Pages/Books` folder Open the `EditModal.cshtml.cs` file (`EditModalModel` class) and replace with the following code: -````C# +````csharp using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -namespace Acme.BookStore.Pages.Books +namespace Acme.BookStore.Web.Pages.Books { public class EditModalModel : BookStorePageModelBase { @@ -153,7 +153,7 @@ namespace Acme.BookStore.Pages.Books [BindProperty] public CreateUpdateBookDto Book { get; set; } - + private readonly IBookAppService _bookAppService; public EditModalModel(IBookAppService bookAppService) @@ -180,38 +180,26 @@ namespace Acme.BookStore.Pages.Books * Mapped `BookDto` (received from the `BookAppService.GetAsync`) to `CreateUpdateBookDto` in the `GetAsync` method. * The `OnPostAsync` simply uses `BookAppService.UpdateAsync` to update the entity. -#### CreateUpdateBookDto +#### BookDto to CreateUpdateBookDto Mapping -In order to perform `BookDto` to `CreateUpdateBookDto` object mapping, change the `CreateUpdateBookDto` class as shown below: +In order to perform `BookDto` to `CreateUpdateBookDto` object mapping, open the `BookStoreWebAutoMapperProfile.cs` in the `Acme.BookStore.Web` project and change it as shown below: -````C# -using System; -using System.ComponentModel.DataAnnotations; -using Volo.Abp.AutoMapper; +````csharp +using AutoMapper; -namespace Acme.BookStore +namespace Acme.BookStore.Web { - [AutoMapTo(typeof(Book))] - [AutoMapFrom(typeof(BookDto))] - public class CreateUpdateBookDto + public class BookStoreWebAutoMapperProfile : Profile { - [Required] - [StringLength(128)] - public string Name { get; set; } - - [Required] - public BookType Type { get; set; } = BookType.Undefined; - - [Required] - public DateTime PublishDate { get; set; } - - [Required] - public float Price { get; set; } + public BookStoreWebAutoMapperProfile() + { + CreateMap(); + } } } ```` -* Just added the `[AutoMapFrom(typeof(BookDto))]` attribute to create the mapping. +* Just added `CreateMap();` as the mapping definition. #### EditModal.cshtml @@ -219,8 +207,8 @@ Replace `EditModal.cshtml` content with the following content: ````html @page -@inherits Acme.BookStore.Pages.BookStorePageBase -@using Acme.BookStore.Pages.Books +@inherits Acme.BookStore.Web.Pages.BookStorePageBase +@using Acme.BookStore.Web.Pages.Books @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @model EditModalModel @{ @@ -231,7 +219,7 @@ Replace `EditModal.cshtml` content with the following content: - + @@ -240,7 +228,7 @@ Replace `EditModal.cshtml` content with the following content: This page is very similar to the `CreateModal.cshtml` except; -* It includes an `abp-input` for the `Id` property to store id of the editing book. +* It includes an `abp-input` for the `Id` property to store id of the editing book (which is a hidden input). * It uses `Books/EditModal` as the post URL and *Update* text as the modal header. #### Add "Actions" Dropdown to the Table @@ -420,6 +408,12 @@ $(function () { }); ```` +Open the `en.json` in the `Acme.BookStore.Domain.Shared` project and add the following line: + +````json +"BookDeletionConfirmationMessage": "Are you sure to delete the book {0}?" +```` + Run the application and try to delete a book. ### Next Part diff --git a/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md b/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md index b70521dad0..7b97fcf483 100644 --- a/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md +++ b/docs/en/Tutorials/AspNetCore-Mvc/Part-III.md @@ -2,22 +2,21 @@ ### About this Tutorial -This is the third part of the tutorial series. See all parts: +This is the third part of the ASP.NET Core MVC tutorial series. See all parts: - [Part I: Create the project and a book list page](Part-I.md) - [Part II: Create, Update and Delete books](Part-II.md) - **Part III: Integration Tests (this tutorial)** -You can download the **source code** of the application [from here](https://github.com/volosoft/abp/tree/master/samples/BookStore). +You can access to the **source code** of the application from [the GitHub repository](https://github.com/volosoft/abp/tree/master/samples/BookStore). ### Test Projects in the Solution -There are two test projects in the solution: +There are multiple test projects in the solution: -* `Acme.BookStore.Application.Tests` is for unit & integration tests. You can write tests for application service methods. It uses **EF Core SQLite in-memory** database. -* `Acme.BookStore.Web.Tests` is for full stack integration tests including the web layer. So, you can write tests for UI pages too. +![bookstore-test-projects-v2](images/bookstore-test-projects-v2.png) -Test projects use the following libraries for testing: +Each project is used to test the related application project. Test projects use the following libraries for testing: * [xunit](https://xunit.github.io/) as the main test framework. * [Shoudly](http://shouldly.readthedocs.io/en/latest/) as an assertion library. @@ -25,79 +24,40 @@ Test projects use the following libraries for testing: ### Adding Test Data -Startup template contains the `BookStoreTestDataBuilder` class in the `Acme.BookStore.Application.Tests` project that creates some data to run tests on. It's shown below: +Startup template contains the `BookStoreTestDataSeedContributor` class in the `Acme.BookStore.TestBase` project that creates some data to run tests on. -````C# -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Identity; -using Volo.Abp.Threading; - -namespace Acme.BookStore -{ - public class BookStoreTestDataBuilder : ITransientDependency - { - private readonly IIdentityDataSeeder _identityDataSeeder; - - public BookStoreTestDataBuilder(IIdentityDataSeeder identityDataSeeder) - { - _identityDataSeeder = identityDataSeeder; - } - - public void Build() - { - AsyncHelper.RunSync(BuildInternalAsync); - } - - public async Task BuildInternalAsync() - { - await _identityDataSeeder.SeedAsync("1q2w3E*"); - } - } -} -```` - -* It simply uses `IIdentityDataSeeder` which is implemented by the identity module and creates an admin role and admin user. You can use them in your tests. -* You can add new test data in the `BuildInternalAsync` method. - -Change the `BookStoreTestDataBuilder` class as show below: +Change the `BookStoreTestDataSeedContributor` class as show below: ````C# using System; using System.Threading.Tasks; +using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; -using Volo.Abp.Identity; -using Volo.Abp.Threading; +using Volo.Abp.Guids; namespace Acme.BookStore { - public class BookStoreTestDataBuilder : ITransientDependency + public class BookStoreTestDataSeedContributor + : IDataSeedContributor, ITransientDependency { - private readonly IIdentityDataSeeder _identityDataSeeder; private readonly IRepository _bookRepository; + private readonly IGuidGenerator _guidGenerator; - public BookStoreTestDataBuilder( - IIdentityDataSeeder identityDataSeeder, - IRepository bookRepository) + public BookStoreTestDataSeedContributor( + IRepository bookRepository, + IGuidGenerator guidGenerator) { - _identityDataSeeder = identityDataSeeder; _bookRepository = bookRepository; + _guidGenerator = guidGenerator; } - public void Build() - { - AsyncHelper.RunSync(BuildInternalAsync); - } - - public async Task BuildInternalAsync() + public async Task SeedAsync(DataSeedContext context) { - await _identityDataSeeder.SeedAsync("1q2w3E*"); - await _bookRepository.InsertAsync( new Book { - Id = Guid.NewGuid(), + Id = _guidGenerator.Create(), Name = "Test book 1", Type = BookType.Fantastic, PublishDate = new DateTime(2015, 05, 24), @@ -108,7 +68,7 @@ namespace Acme.BookStore await _bookRepository.InsertAsync( new Book { - Id = Guid.NewGuid(), + Id = _guidGenerator.Create(), Name = "Test book 2", Type = BookType.Science, PublishDate = new DateTime(2014, 02, 11), @@ -120,7 +80,8 @@ namespace Acme.BookStore } ```` -* Injected `IRepository` and used it in the `BuildInternalAsync` to create two book entities. +* Injected `IRepository` and used it in the `SeedAsync` to create two book entities as the test data. +* Used `IGuidGenerator` service to create GUIDs. While `Guid.NewGuid()` would perfectly work for testing, `IGuidGenerator` has additional features especially important while using real databases (see the [Guid generation document](../../Guid-Generation.md) for more). ### Testing the BookAppService @@ -210,6 +171,8 @@ public async Task Should_Not_Create_A_Book_Without_Name() * Since the `Name` is empty, ABP throws an `AbpValidationException`. -### Testing Web Pages +Open the **Test Explorer Window** (use Test -> Windows -> Test Explorer menu if it is not visible) and **Run All** tests: + +![bookstore-appservice-tests](images/bookstore-appservice-tests.png) -TODO \ No newline at end of file +Congratulations, green icons show that tests have been successfully passed! \ No newline at end of file diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png new file mode 100644 index 0000000000..fcadfd7a82 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png deleted file mode 100644 index 44398e6b4c..0000000000 Binary files a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png and /dev/null differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png new file mode 100644 index 0000000000..2606bbab32 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png deleted file mode 100644 index 2452ca546e..0000000000 Binary files a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png and /dev/null differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png new file mode 100644 index 0000000000..942d2832a6 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png new file mode 100644 index 0000000000..1ba9a48600 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png deleted file mode 100644 index e36c5dfff7..0000000000 Binary files a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png and /dev/null differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png new file mode 100644 index 0000000000..79314dd2dc Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png deleted file mode 100644 index ca1da1c7ec..0000000000 Binary files a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png and /dev/null differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png new file mode 100644 index 0000000000..8701164d75 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png deleted file mode 100644 index f9511d7b11..0000000000 Binary files a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png and /dev/null differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png deleted file mode 100644 index 48b5881e96..0000000000 Binary files a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png and /dev/null differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png new file mode 100644 index 0000000000..307e3516a5 Binary files /dev/null and b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png differ diff --git a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png b/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png deleted file mode 100644 index b47636cd68..0000000000 Binary files a/docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png and /dev/null differ diff --git a/docs/en/Value-Types.md b/docs/en/Value-Types.md new file mode 100644 index 0000000000..a0e340063c --- /dev/null +++ b/docs/en/Value-Types.md @@ -0,0 +1,3 @@ +## Value Types + +TODO \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 396b92afd3..eaee22e59a 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -41,6 +41,10 @@ } ] }, + { + "text": "CLI", + "path": "CLI.md" + }, { "text": "Fundamentals", "items": [ diff --git a/docs/en/images/bookstore-user-management-v2.png b/docs/en/images/bookstore-user-management-v2.png new file mode 100644 index 0000000000..dd95740754 Binary files /dev/null and b/docs/en/images/bookstore-user-management-v2.png differ diff --git a/docs/en/images/bookstore-user-management.png b/docs/en/images/bookstore-user-management.png deleted file mode 100644 index bc3c176557..0000000000 Binary files a/docs/en/images/bookstore-user-management.png and /dev/null differ diff --git a/docs/en/images/bookstore-visual-studio-solution-v2.png b/docs/en/images/bookstore-visual-studio-solution-v2.png deleted file mode 100644 index 48b5881e96..0000000000 Binary files a/docs/en/images/bookstore-visual-studio-solution-v2.png and /dev/null differ diff --git a/docs/en/images/bookstore-visual-studio-solution-v3.png b/docs/en/images/bookstore-visual-studio-solution-v3.png new file mode 100644 index 0000000000..307e3516a5 Binary files /dev/null and b/docs/en/images/bookstore-visual-studio-solution-v3.png differ diff --git a/docs/en/images/bookstore-visual-studio-solution.png b/docs/en/images/bookstore-visual-studio-solution.png deleted file mode 100644 index b47636cd68..0000000000 Binary files a/docs/en/images/bookstore-visual-studio-solution.png and /dev/null differ diff --git a/docs/en/images/db-migrator-app.png b/docs/en/images/db-migrator-app.png new file mode 100644 index 0000000000..ace6abb226 Binary files /dev/null and b/docs/en/images/db-migrator-app.png differ diff --git a/docs/zh-Hans/AspNetCore/Auto-API-Controllers.md b/docs/zh-Hans/AspNetCore/Auto-API-Controllers.md index afe8ea700b..f31b4d67cb 100644 --- a/docs/zh-Hans/AspNetCore/Auto-API-Controllers.md +++ b/docs/zh-Hans/AspNetCore/Auto-API-Controllers.md @@ -16,7 +16,9 @@ public class BookStoreWebModule : AbpModule { Configure(options => { - options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly); + options + .ConventionalControllers + .Create(typeof(BookStoreApplicationModule).Assembly); }); } } diff --git a/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md b/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md index e0db48397e..0c6017e6e7 100644 --- a/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md +++ b/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md @@ -5,4 +5,4 @@ * **推荐** 定义 **public getter 和 setter** 的DTO成员 . * **推荐** 使用 **data annotations** **验证** service输入DTO的属性. * **不推荐** 在DTO中添加任何 **逻辑**, 在必要的时候可以实现 `IValidatableObject` 接口. -* **推荐** 为所有的DTO标记 **[Serializable]** Attribute. 因为它们已经是可序列化的, 开人发员可能会希望进行二进制序列化. \ No newline at end of file +* **推荐** 为所有的DTO标记 **[Serializable]** Attribute. 因为它们已经是可序列化的, 开发人员可能会希望进行二进制序列化. diff --git a/docs/zh-Hans/Best-Practices/Entities.md b/docs/zh-Hans/Best-Practices/Entities.md index 637ecaef6d..fd709ef35f 100644 --- a/docs/zh-Hans/Best-Practices/Entities.md +++ b/docs/zh-Hans/Best-Practices/Entities.md @@ -64,7 +64,7 @@ public class Issue : FullAuditedAggregateRoot //使用Guid作为键/标识 protected Issue() { /* 此构造函数是提供给ORM用来从数据库中获取实体. - * - 无需初始化Lanels集合 + * - 无需初始化Labels集合 因为它会被来自数据库的值覆盖. - It's protected since proxying and deserialization tools 可能不适用于私有构造函数. @@ -150,4 +150,4 @@ public class IssueLabel : Entity ### 参考文献 * Effective Aggregate Design by Vaughn Vernon - http://dddcommunity.org/library/vernon_2011 \ No newline at end of file + http://dddcommunity.org/library/vernon_2011 diff --git a/docs/zh-Hans/CLI.md b/docs/zh-Hans/CLI.md new file mode 100644 index 0000000000..d210560910 --- /dev/null +++ b/docs/zh-Hans/CLI.md @@ -0,0 +1,137 @@ +# ABP CLI + +ABP CLI (Command Line Interface) is a command line tool to perform some common operations for ABP based solutions. + +## Installation + +ABP CLI is a [dotnet global tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools). Install it using a command line window: + +````bash +dotnet tool install -g Volo.Abp.Cli +```` + +To update an existing installation: + +````bash +dotnet tool update -g Volo.Abp.Cli +```` + +## Commands + +### new + +Generates a new solution based on the ABP [startup templates](Startup-Templates/Index.md). + +Basic usage: + +````bash +abp new [options] +```` + +Example: + +````bash +abp new Acme.BookStore +```` + +* Acme.BookStore is the solution name here. +* Common convention is to name a solution is like *YourCompany.YourProject*. However, you can use different naming like *YourProject* (single level namespacing) or *YourCompany.YourProduct.YourModule* (three levels namespacing). + +#### Options + +* `--template` or `-t`: Specifies the template name. Default template name is `mvc`. Available templates: + * `mvc` (default): ASP.NET Core [MVC application template](Startup-Templates/Mvc.md). Additional options: + * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: + * `ef`: Entity Framework Core. + * `mongodb`: MongoDB. + * `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios. + * `mvc-module`: ASP.NET Core [MVC module template](Startup-Templates/Mvc-Module.md). Additional options: + * `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI). +* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory. + +### add-package + +Adds a new ABP package to a project by, + +* Adding related nuget package as a dependency to the project. +* Adding `[DependsOn(...)]` attribute to the module class in the project (see the [module development document](Module-Development-Basics.md)). + +> Notice that the added module may require additional configuration which is generally indicated in the documentation of the related package. + +Basic usage: + +````bash +abp add-package [options] +```` + +Example: + +```` +abp add-package Volo.Abp.MongoDB +```` + +* This example adds the Volo.Abp.MongoDB package to the project. + +#### Options + +* `--project` or `-p`: Specifies the project (.csproj) file path. If not specified, CLI tries to find a .csproj file in the current directory. + +### add-module + +Adds a multi-package module to a solution by finding all packages of the module, finding related projects in the solution and adding each package to the corresponding project in the solution. + +> A business module generally consists of several packages (because of layering, different database providr options or other reasons). Using `add-module` command dramatically simplifies adding a module to a solution. However, each module may require some additional configurations which is generally indicated in the documentation of the related module. + +Basic usage: + +````bash +abp add-module [options] +```` + +Example: + +```bash +abp add-module Volo.Blogging +``` + +* This example add the Volo.Blogging module to the solution. + +#### Options + +* `--solution` or `-s`: Specifies the solution (.sln) file path. If not specified, CLI tries to find a .sln file in the current directory. +* `--skip-db-migrations`: For EF Core database provider, it automatically adds a new code first migration (`Add-Migration`) and updates the database (`Update-Database`) if necessary. Specify this option to skip this operation. + +### 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 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. + +### help + +Writes basic usage information of the CLI. + +Usage: + +````bash +abp help [command-name] +```` + +Examples: + +````bash +abp help # Shows a general help. +abp help new # Shows help about the "new" command. +```` + diff --git a/docs/zh-Hans/Data-Seeding.md b/docs/zh-Hans/Data-Seeding.md new file mode 100644 index 0000000000..0b424e7e0d --- /dev/null +++ b/docs/zh-Hans/Data-Seeding.md @@ -0,0 +1,3 @@ +# Data Seeding + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/Exception-Handling.md b/docs/zh-Hans/Exception-Handling.md index ecb4acf5e7..a251c50f61 100644 --- a/docs/zh-Hans/Exception-Handling.md +++ b/docs/zh-Hans/Exception-Handling.md @@ -70,7 +70,7 @@ ABP提供了用于处理Web应用程序异常的标准模型. "code": "App:010046", "message": "Your request is not valid, please correct and try again!", "validationErrors": [{ - "message": "Username should be minimum lenght of 3.", + "message": "Username should be minimum length of 3.", "members": ["userName"] }, { diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md index cf9ac3f66f..db6e73f35c 100644 --- a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md +++ b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md @@ -77,11 +77,11 @@ namespace BasicAspNetCoreApplication { public class Startup { - public IServiceProvider ConfigureServices(ServiceConfigurationContext context) + public IServiceProvider ConfigureServices(IServiceCollection services) { - context.Services.AddApplication(); + services.AddApplication(); - return context.Services.BuildServiceProviderFromFactory(); + return services.BuildServiceProviderFromFactory(); } public void Configure(IApplicationBuilder app) diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md index e8ce4fa71a..cef3b1f003 100644 --- a/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md +++ b/docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md @@ -2,37 +2,42 @@ ### 创建新项目 -访问[模板创建页面](https://abp.io/Templates), 输入项目名称并创建项目, 如下所示: +本教程使用 **ABP CLI** 创建一个新项目. 更多选项, 请参阅[入门](https://cn.abp.io/get-started)页面. -![bookstore-create--template](images/bookstore-create-template.png) +如果你之前未安装,请使用命令行安装ABP CLI: -单击 *create* 按钮时,将使用你提供的名称创建一个新的Visual Studio解决方案并开始下载. +````bash +dotnet tool install -g Volo.Abp.Cli +```` + +在空文件夹中使用 `abp new` 命令来创建项目: + +```` +abp new Acme.BookStore +```` + +> 你可以使用不同级别的命名空间; 例如BookStore, Acme.BookStore或Acme.Retail.BookStore. + +`new` 命令创建**分层MVC应用程序**, **Entity Framework Core**作为数据库提供程序. 但是,它还有其他选择. 有关所有可用选项,请参见[CLI文档](CLI.md) #### 预先要求 -下载的项目需要: +创建项目的要求: * [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/) -* [.NET Core 2.1.1+](https://www.microsoft.com/net/download/dotnet-core/) +* [.NET Core 2.2+](https://www.microsoft.com/net/download/dotnet-core/) ### 解决方案结构 -下载后解压文件并在 **Visual Studio 2017(15.7.0 +)** 中打开: - -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) - -该解决方案具有分层结构(基于域驱动设计), 其中: +在**Visual Studio**中打开解决方案: -* ``.Domain`` 为领域层. -* ``.Application`` 为应用层. -* ``.Web`` 为是表示层. -* ``.EntityFrameworkCore`` 是EF Core集成. +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png) -EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项目中. +该解决方案具有分层结构(基于[Domain Driven Design](Domain-Driven-Design.md)), 并包含配置好的的单元&集成测试项目,可与**EF Core**和**SQLite**数据库内存一起使用. -解决方案还包含配置好的的单元&集成测试项目, 以便与 **EF Core** 和 **SQLite内存中** 数据库配合使用. +> 请参阅[MVC应用程序模板文档](Startup-Templates/Mvc.md)以详细了解解决方案结构. -### 创建数据库 +### 数据库连接字符串 查看`.Web`项目下`appsettings.json`文件中的 **连接字符串**: @@ -44,9 +49,29 @@ EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项 } ```` -解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/en-us/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. +解决方案使用 **Entity Framework Core** 和 **MS SQL Server**. EF Core支持[各种](https://docs.microsoft.com/en-us/ef/core/providers/)数据库提供程序,因此你可以根据实际需要使用其他DBMS. 如果需要,请更改连接字符串. + +### 创建数据库并应用数据库迁移 + +你有两个选项来创建数据库. -右键单击`.Web`项目并**设置启动项目** +#### 使用DbMigrator应用程序 + +该解决方案包含一个控制台应用程序(在此示例中名为`Acme.BookStore.DbMigrator`),可以创建数据库,应用迁移和初始化数据. 它对开发和生产环境都很有用. + +> `.DbMigrator`项目有自己的`appsettings.json`. 因此,如果你更改了上面的连接字符串,则还应更改此字符串. + +右键单击`.DbMigrator`项目并选择 **设置为启动项目**: + +![set-as-startup-project](images/set-as-startup-project.png) + +按F5(或Ctrl + F5)运行应用程序. 它将具有如下所示的输出: + +![set-as-startup-project](images/db-migrator-app.png) + +#### 使用EF Core Update-Database命令 + +Ef Core具有`Update-Database`命令, 可根据需要创建数据库并应用挂起的迁移. 右键单击`.Web`项目并选择**设置为启动项目**: ![set-as-startup-project](images/set-as-startup-project.png) @@ -56,6 +81,8 @@ EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项 这将基于配置的连接字符串创建新数据库. +> 使用`.Migrator`工具是建议的方法, 因为它还能初始化初始数据能够正确运行Web应用程序. + ### 运行应用程序 你现在可以运行应用程序,它将会打开**home**页面: @@ -64,9 +91,9 @@ EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项 单击 **登录** 按钮, 输入用户名`admin`, 密码`1q2w3E*`, 登录应用程序. -启动模板包括 **身份管理(identity management)** 模块. 登录后将提供身份管理菜单,你可以在其中管理**角色**,**用户**及其**权限**. +启动模板包括**身份管理**和**租户管理**模块. 登录后,将显示"管理"菜单, 你可以在其中管理**租户**,**角色**,**用户**和**权限**. 用户管理页面如下所示: -![bookstore-user-management](images/bookstore-user-management.png) +![bookstore-user-management](images/bookstore-user-management-v2.png) ### 下一步是什么? diff --git a/docs/zh-Hans/Index.md b/docs/zh-Hans/Index.md index e14bd67fa5..fbe003b8e7 100644 --- a/docs/zh-Hans/Index.md +++ b/docs/zh-Hans/Index.md @@ -1,68 +1,32 @@ # ABP 文档 -> 翻译来自[cnAbp](https://github.com/cnabp)组织,中文网会持续跟进翻译,目前Abp vNext的英文文档还未完成,大家对整体框架没有深入的理解,翻译难免存在一些问题.敬请见谅.😀 +> 中文文档翻译来自[cnAbp](https://github.com/cnabp)组织,Abp中文网会持续跟进翻译,目前Abp vNext的英文文档还未完成,大家对整体框架没有深入的理解,翻译难免存在一些问题.敬请见谅.😀 -## 目录 +ABP是一个**开源应用程序框架**,专注于基于ASP.NET Core的Web应用程序开发,但也支持开发其他类型的应用程序. -* 入门 - * 从启动模板开始 - * [ASP.NET Core MVC 模板](Getting-Started-AspNetCore-MVC-Template.md) - * 从空项目开始 - * [使用Console Application](Getting-Started-Console-Application.md) - * [使用 ASP.NET Core Web Application](Getting-Started-AspNetCore-Application.md) -* 教程 - * 应用开发 - * [使用 ASP.NET Core MVC](Tutorials/AspNetCore-Mvc/Part-I.md) -* 基础知识 - * [依赖注入](Dependency-Injection.md) - * AutoFac 集成 - * [虚拟文件系统](Virtual-File-System.md) - * [本地化](Localization.md) - * [异常处理](Exception-Handling.md) - * 验证 - * 授权 - * 缓存 - * 审计 - * 设置管理 - * 对象映射 - * AutoMapper 集成 -* 事件 - * 本地 Event Bus - * 分布式 Event Bus - * RabbitMQ 集成 -* 服务 - * 对象序列化 - * JSON序列化 - * 邮件 - * GUIDs - * 线程 - * 定时 -* [多租户](Multi-Tenancy.md) -* 模块开发 - * [基础](Module-Development-Basics.md) - * 模块插件 - * [最佳实践](Best-Practices/Index.md) -* 领域驱动设计 - * 领域层 - * [实体&聚合根](Entities.md) - * 值对象 - * [仓储](Repositories.md) - * 领域服务 - * 规约 - * 应用服务层 - * 应用服务 - * 数据传输对象(DTO) - * 工作单元 -* ASP.NET Core MVC - * API 版本控制 - * 用户界面 - * [客户端包管理](AspNetCore/Client-Side-Package-Management.md) - * [捆绑&压缩](AspNetCore/Bundling-Minification.md) - * [Tag Helpers](Tag-Helpers.md) - * [主题](AspNetCore/Theming.md) -* 后台服务 - * [后台作业](Background-Jobs.md) -* 数据访问 - * [Entity Framework Core 集成](Entity-Framework-Core.md) - * [MongoDB 集成](MongoDB.md) -* 测试 +浏览左侧导航菜单以深入了解文档. + +## 项目状态 + +ABP是开源[ASP.NET Boilerplate](https://aspnetboilerplate.com/)框架的**下一代框架**, 它目前处于早期预览阶段,尚未准备好在生产中使用. 文档仍在进行中,远未完成. + +对于短期和生产级应用程序, 建议使用[ASP.NET Boilerplate](https://aspnetboilerplate.com/)框架,该框架具有丰富的功能集,成熟,积极维护和最新. + +## 入门 + +使用ABP开发新项目的最简单方法是使用启动模板: + +* [ASP.NET Core MVC 模板](Getting-Started-AspNetCore-MVC-Template.md) + +如果您想从头开始(使用空项目),请手动安装ABP框架并使用以下教程: + +* [控制台应用程序](Getting-Started-Console-Application.md) +* [ASP.NET Core Web 应用程序](Getting-Started-AspNetCore-Application.md) + +## 源码 + +ABP托管在GitHub上, 参见[源代码](https://github.com/abpframework/abp). + +## 贡献代码 + +ABP是一个社区驱动的开源项目.如果你想成为该项目的一部分,请参阅[贡献指南](Contribution/Index.md). diff --git a/docs/zh-Hans/Samples/Microservice-Demo.md b/docs/zh-Hans/Samples/Microservice-Demo.md index c87d7e5406..fdf183d1b6 100644 --- a/docs/zh-Hans/Samples/Microservice-Demo.md +++ b/docs/zh-Hans/Samples/Microservice-Demo.md @@ -116,11 +116,29 @@ ABP框架的主要目标之一就是提供[便捷的基础设施来创建微服 * 在`samples\MicroserviceDemo`文件夹中的命令行运行`dotnet restore`命令. * 在Visual Studio中构建解决方案. -#### 还原数据库 +#### 创建数据库 -在`samples\MicroserviceDemo\databases`文件夹中打开`MsDemo_Identity.zip`和`MsDemo_ProductManagement.zip`并恢复到SQL Server. +MongoDB 数据库是动态创建的,但是你需要创建 SQL server 数据库的结构。其实你可以很轻松的创建数据库,因为这个解决方案配置了使用 Entity Core Code First 来做迁移。 -> 请注意:这些数据库在解决方案中具有EF Core迁移,但它们没有种子数据,尤其是IdentityServer4所需的配置. 因此,恢复数据库要容易得多. +这个解决方案中有两个 SQL server 数据库。 + +##### MsDemo_Identity 数据库 + +* 右键 `AuthServer.Host` 项目,然后点击 `设置为启动项目`. +* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台) +* 选择 `AuthServer.Host` 成为 **默认项目**. +* 执行 `Update-Database` 命令. + +![microservice-sample-update-database-authserver](../images/microservice-sample-update-database-authserver.png) + +##### MsDemo_ProductManagement + +* 右键 `ProductService.Host` 项目,然后点击 `设置为启动项目`. +* 打开 **程序包管理器控制台** (工具 -> NuGet 包管理器 -> 程序包管理器控制台) +* 选择 `ProductService.Host` 成为 **默认项目**. +* 执行 `Update-Database` 命令. + +![microservice-sample-update-database-products](../images/microservice-sample-update-database-products.png) #### 运行项目 @@ -148,7 +166,7 @@ Visual Studio解决方案由多个项目组成,每个项目在系统中具有不 - **AuthServer.Host**: 托管IdentityServer4以向其他服务和应用程序提供身份验证服务. 它是一个单点登录服务器,包含登录页面. - **BackendAdminApp.Host**: 这是一个后端管理应用程序,用于托管身份和产品管理模块的UI. -- **PubicWebSite.Host**: 作为包含简单产品列表页面和博客模块UI的公共网站. +- **PublicWebSite.Host**: 作为包含简单产品列表页面和博客模块UI的公共网站. - **ConsoleClientDemo**: 一个简单的控制台应用程序,用于演示C#应用程序中使用服务. ### 网关/BFF(前端后端) @@ -276,8 +294,7 @@ context.Services.AddAuthentication(options => * 它将"Cookies"身份验证添加为主要身份验证类型. * "oidc"身份验证配置为使用AuthServer应用程序作为身份验证服务器. -* 它需要额外的身份范围(scopes) *role*, *email* and *phone*. -* It requires the API resource scopes *BackendAdminAppGateway*, *IdentityService* and *ProductService* because it will use these services as APIs. +* 它需要额外的身份范围(scopes) *role*, *email* 和 *phone*. * 它需要API资源范围 *BackendAdminAppGateway*, *IdentityService* 和 *ProductService*,因为它将这些服务用作API. IdentityServer客户端设置存储在`appsettings.json`文件中: @@ -577,8 +594,6 @@ Ocelot需要知道微服务的真实URL才能重定向HTTP请求. 此网关的 `ReRoutes`是一个URL映射数组. `GlobalConfiguration`部分中的`BaseUrl`是该网关的URL(Ocelot需要知道自己的URL). 参见 [ocelot文档](https://ocelot.readthedocs.io/en/latest/features/configuration.html) 更好地了解配置. -Ocelot is a finalizer ASP.NET Core middleware and should be written as the last item in the pipeline: - Ocelot是一个终结ASP.NET核心中间件,应该写成管道中的最后一项: ````csharp @@ -935,7 +950,7 @@ context.Services.AddAuthentication("Bearer") #### IdentityServer Client -此微服务还通过内部网关使用Identity微服务API, 因为在某些情况下它需要查询用户详细信息(username, email, phone, name and surname). 因此,它也是IdentityServer的客户端,并在`appsettings.json`文件中定义了一个部分: +此微服务还通过内部网关使用Identity微服务API, 因为在某些情况下它需要查询用户详细信息(username, email, phone, name 和 surname). 因此,它也是IdentityServer的客户端,并在`appsettings.json`文件中定义了一个部分: ````json "IdentityClients": { @@ -1334,7 +1349,7 @@ public async Task UpdateAsync(Guid id, UpdateProductDto input) AddDistributedEvent(new ProductStockCountChangedEto(Id, StockCount, stockCount)); ```` -`ProductStockCountChangedEto` was defined as shown below: +`ProductStockCountChangedEto` 定义如下: ````csharp [Serializable] @@ -1458,4 +1473,4 @@ ABP提供自动审计日志记录,详细保存每个请求(当前用户,浏览 所有服务和应用程序都配置为编写审核日志. 审核日志将保存到MsDemo_Identity SQL数据库中. 因此,您可以从单个点查询所有应用程序的所有审核日志. -审核日志记录具有`CorrelationId`属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的`CorrelationId`保存审核日志. 请参阅数据库中的`AbpAuditLogs`表. \ No newline at end of file +审核日志记录具有`CorrelationId`属性,可用于跟踪请求. 当服务在单个Web请求中调用另一个服务时,它们都会使用相同的`CorrelationId`保存审核日志. 请参阅数据库中的`AbpAuditLogs`表. diff --git a/docs/zh-Hans/Startup-Templates/Index.md b/docs/zh-Hans/Startup-Templates/Index.md new file mode 100644 index 0000000000..c4c75bbedb --- /dev/null +++ b/docs/zh-Hans/Startup-Templates/Index.md @@ -0,0 +1,8 @@ +# 启动模板 + +虽然你可以从一个空项目开始并手动添加所需的包,但启动模板可以非常轻松,舒适地使用ABP框架启动新的解决方案. + +单击下面列表中的名称以查看相关启动模板的文档: + +* [**mvc**](Mvc.md): ASP.NET Core MVC应用程序模板. +* [**mvc-module**](Mvc-Module.md): ASP.NET Core MVC模块/服务模板. \ No newline at end of file diff --git a/docs/zh-Hans/Startup-Templates/Mvc-Module.md b/docs/zh-Hans/Startup-Templates/Mvc-Module.md new file mode 100644 index 0000000000..9f19f83278 --- /dev/null +++ b/docs/zh-Hans/Startup-Templates/Mvc-Module.md @@ -0,0 +1,6 @@ +# MVC Module 启动模板 + +TODO + + + diff --git a/docs/zh-Hans/Startup-Templates/Mvc.md b/docs/zh-Hans/Startup-Templates/Mvc.md new file mode 100644 index 0000000000..b44105a4f6 --- /dev/null +++ b/docs/zh-Hans/Startup-Templates/Mvc.md @@ -0,0 +1,183 @@ +# MVC Application Startup Template + +## Introduction + +This template provides a layered (or tiered, based on the preference) application structure based on the [Domain Driven Design](../Domain-Driven-Design.md) (DDD) practices. + +## How to Start With + +You can use the [ABP CLI](../CLI.md) to create a new project using this startup template. Alternatively, you can directly create & download from the [Get Started](https://abp.io/get-started) page. CLI approach is used here. + +First, install the ABP CLI as described in [its document](../CLI.md). Then use the `abp new` command in an empty folder to create a new solution: + +````bash +abp new Acme.BookStore -t mvc +```` + +* `Acme.BookStore` is the solution name, like *YourCompany.YourProduct*. See the [CLI document](../CLI.md) for different naming styles. +* This example specified the template name (`-t` or `--template` option). However, `mvc` is the default template and used even if you don't specify it. + +### Specify Database Provider + +This template supports the following database providers: + +- `ef`: Entity Framework Core (default) +- `mongodb`: MongoDB + +Use the `-d` (or `--database-provider`) to specify the database provider: + +````bash +abp new Acme.BookStore -t mvc -d mongodb +```` + +### Create a Tiered Solution + +`--tiered` option is used to create a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios. + +````bash +abp new Acme.BookStore --tiered +```` + +See the "Tiered Structure" section below for the tiered approach. + +## Solution Structure + +Based on the options you've specified, you will get a slightly different solution structure. + +### Default Structure + +If you don't specify any option, you will have a solution like shown below: + +![bookstore-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png) + +Projects are organized in `src` and `test` folders. `src` folder contains the actual application which is layered based on [DDD](../Domain-Driven-Design.md) principles as mentioned before. + +-------------------- + +**TODO: Add a graphic to illustrate dependencies between projects.** + +------------------ + +Each section below will explain the related project. + +#### .Domain Project + +This is the domain layer of the solution. It mainly contains [entities, aggregate roots](../Entities.md), [domain services](../Domain-Services.md), [value types](../Value-Types.md), [repository interfaces](../Repositories.md) and other domain objects of the solution. + +A `Book` entity and a `IBookRepository` interface are good candidates for this project. + +* Depends on the `.Domain.Shared` because it uses constants, enums and other objects defined in that project. + +#### .Domain.Shared Project + +This project contains constants, enums and other objects these are actually a part of the domain layer, but needed to be used by all layers/projects in the solution. + +A `BookType` enum and a `BookConts` class (which may have some constant fields for the `Book` entity, like `MaxNameLength`) are good candidates for this project. + +This project has no dependency to other projects in the solution. + +#### .Application.Contracts Project + +This project mainly contains [application service](../Application-Services.md) **interfaces** and [Data Transfer Objects](../Data-Transfer-Objects.md) (DTO) of the application layer. It does exists to separate interface & implementation of the application layer. In this way, the interface project can be shared to the clients as a contract package. + +* Depends on the `.Domain.Shared` because it may use constants, enums and other shared objects in the application service interfaces and DTOs. + +#### .Application Project + +This project contains the [application service](../Application-Services.md) implementations of the interfaces defined in the `.Application.Contracts` project. + +* Depends on the `.Application.Contracts` project to be able to implement the interfaces and use the DTOs. +* Depends on the `.Domain` project to be able to use domain objects (entities, repository interfaces... etc.) to perform the application logic. + +#### .EntityFrameworkCore Project. + +This is the integration project for the EF Core. It defines the `DbContext` and implements repository interfaces defined in the `.Domain` project. + +* Depends on the `.Domain` project to be able to reference to entities and repository interfaces. + +> This project is available only if you are using EF Core as the database provider. + +#### .EntityFrameworkCore.DbMigrations Project + +Contains EF Cor database migrations for the solution. It has a separated `DbContext` to dedicated to manage migrations. + +ABP is a modular framework and with an ideal design, each module has its own `DbContext` class. This is where the migration `DbContext` comes into play and unifies all `DbContext` configurations into a single model to maintain a single database schema. + +Notice that the migration `DbContext` is only used for database migrations and *not used on runtime*. + +* Depends on the `.EntityFrameworkCore` project since it re-uses the configuration defined for the `DbContext` of the application. + +> This project is available only if you are using EF Core as the database provider. + +#### .DbMigrator Project + +This is a console application which simplifies to execute database migrations on development and production environments. When you this application; + +* Creates the database if necessary. +* Applies database migrations. +* Seeds initial data. + +> This project has its own `appsettings.json` file. So, if you want to change the database connection string, remember to change this file. + +Especially, seeding initial data is important at this point. ABP has a modular data seed infrastructure. See [its documentation](../Data-Seeding.md) for more about the data seeding. + +While creating database & applying migrations seems only necessary for relational databases, this projects comes even if you choose a NoSQL database provider (like MongoDB). In that case, it still seeds initial data which is necessary for the application. + +* Depends on the `.EntityFrameworkCore.DbMigrations` project (for EF Core) since it needs to access to the migrations. +* Depends on the `.Application.Contracts` project to be able to access permission definitions, because initial data seeder grants permissions for the admin user. + +#### .HttpApi Project + +This project is used to define your API Controllers. + +Most of time you don't need to manually define API Controllers since ABP's [Auto API Controllers](../AspNetCore/Auto-API-Controllers.md) feature creates them automagically based on your application layer. However, in case of you need to write API controllers, this is the best place to do it. + +* Depends on the `.Application.Contracts` project to be able to inject the application service interfaces. + +#### .HttpApi.Client Project + +This is a project that defines C# client proxies to use the HTTP APIs of the solution. You can share this library to 3rd-party clients, so they can easily consume your HTTP APIs in their Dotnet applications. + +`.HttpApi.Client.ConsoleTestApp` project is a console application created to demonstrate the usage of the client proxies. + +Most of time you don't need to manually create C# client proxies, thanks to ABP's [Cynamic C# API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) feature. + +* Depends on the `.Application.Contracts` project to be able to share the same application service interfaces and DTOs with the remote service. + +#### .Web Project + +This project contains the User Interface (UI) of the application. It contains razor pages, javascript files, css files, images and so on... + +* Depends on the `.HttpApi` since UI layer needs to use APIs and application service interfaces of the solution. + +> If you check the source code of the `.Web.csproj` file, you will see the references to the `.Application` and the `.EntityFrameworkCore.DbMigrations` projects. +> +> These references are actually not needed on development, because UI layer normally doesn't depend on the EF Core or the Application implementation. This startup templates are ready for the tiered deployment, where API layer is hosted in a separate server than the UI layer. +> +> However, if you don't choose the `--tiered` option, these references will be in the .Web project to be able to host the Web, API and application layers in a single application endpoint. +> +> This gives you to ability to use domain entities in your presentation layer. However, this is considered as a bad practice according to the DDD. + +#### Test Projects + +The solution has multiple test projects, one for each layer: + +* `.Domain.Tests` is used to test the domain layer. +* `.Application.Tests` is used to test the application layer. +* `.EntityFrameworkCore.Tests` is used to test EF Core configuration and custom repositories. +* `.Web.Tests` is used to test the UI. +* `.TestBase` is a base (shared) project for all tests. + +In addition, `.HttpApi.Client.ConsoleTestApp` is a console application (not an automated test project) which demonstrate the usage of HTTP APIs from a Dotnet application. + +### Tiered Structure + +TODO + +### Other Database Providers + +TODO + +#### MongoDB + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md index d3e8af3523..c0f453d878 100644 --- a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md @@ -2,7 +2,7 @@ ### 关于本教程 -本教程中,你会创建一个用于管理书籍和书籍作者的程序.会用到 **Entity Framework Core** (EF Core)作为ORM([启动模板](https://abp.io/Templates)中预配置的ORM). +在本系列教程中, 你将构建一个用于管理书籍及其作者列表的应用程序. **Entity Framework Core**(EF Core)将用作ORM提供者,因为它是默认数据库提供者. 这是本教程所有章节中的第一章,下面是所有的章节: @@ -10,35 +10,37 @@ - [Part II: 创建,编辑,删除书籍](Part-II.md) - [Part III: 集成测试](Part-III.md) -你可以从[这里](https://github.com/volosoft/abp/tree/master/samples/BookStore)下载本程序的源码. +你可以从[GitHub存储库](https://github.com/volosoft/abp/tree/master/samples/BookStore)访问应用程序的**源代码**. ### 创建项目 -打开[启动模板页](https://abp.io/Templates)并下载一个新的项目叫做`Acme.BookStore`.根据[模板文档](../../Getting-Started-AspNetCore-MVC-Template.md)创建数据库并运行这个程序. +创建一个名为`Acme.BookStore`的新项目, 创建数据库并按照[入门文档](../../Getting-Started-AspNetCore-MVC-Template.md)运行应用程序. ### 解决方案的结构 下面的图片展示了从启动模板创建的项目是如何分层的. -![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png) +![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v3.png) + +> 你可以查看[MVC应用程序模板文档](../../Startup-Templates/Mvc.md)以详细了解解决方案结构.但是,你将通过本教程了解基础知识. ### 创建Book实体 -在 **领域层** 定义[实体](../../Entities.md)(`Acme.BookStore.Domain` 项目中).这个项目最主要的实体就是`Book`: +启动模板中的域层分为两个项目: + + - `Acme.BookStore.Domain`包含你的[实体](../../Entities.md), [领域服务](../../Domain-Services.md)和其他核心域对象. + - `Acme.BookStore.Domain.Shared`包含可与客户共享的常量,枚举或其他域相关对象. + +在解决方案的**领域层**(`Acme.BookStore.Domain`项目)中定义[实体](../../Entities.md). 该应用程序的主要实体是`Book`. 在`Acme.BookStore.Domain`项目中创建一个名为`Book`的类,如下所示: ````C# using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using Volo.Abp.Domain.Entities.Auditing; namespace Acme.BookStore { - [Table("Books")] public class Book : AuditedAggregateRoot { - [Required] - [StringLength(128)] public string Name { get; set; } public BookType Type { get; set; } @@ -50,8 +52,8 @@ namespace Acme.BookStore } ```` -* ABP有两个基本的实体基类: `AggregateRoot` 和 `Entity`.**Aggregate Root**是 **领域驱动设计(DDD)** 的概念之一.更多信息和最佳实践请查看[实体文档](../../Entities.md). -* `Book`实体继承了`AuditedAggregateRoot`,`AuditedAggregateRoot`类在`AggregateRoot`类的基础上添加了一些审计属性(`CreationTime`, `CreatorId`, `LastModificationTime`... 等.). +* ABP为实体提供了两个基本的基类: `AggregateRoot`和`Entity`. **Aggregate Root**是**域驱动设计(DDD)** 概念之一. 有关详细信息和最佳做法,请参阅[实体文档](../../Entities.md). +* `Book`实体继承了`AuditedAggregateRoot`,`AuditedAggregateRoot`类在`AggregateRoot`类的基础上添加了一些审计属性(`CreationTime`, `CreatorId`, `LastModificationTime` 等). * `Guid`是`Book`实体的主键类型. * 使用 **数据注解** 为EF Core添加映射.或者你也可以使用 EF Core 自带的[fluent mapping API](https://docs.microsoft.com/en-us/ef/core/modeling). @@ -62,10 +64,10 @@ namespace Acme.BookStore ````C# namespace Acme.BookStore { - public enum BookType : byte + public enum BookType { Undefined, - Advanture, + Adventure, Biography, Dystopia, Fantastic, @@ -91,14 +93,15 @@ public class BookStoreDbContext : AbpDbContext #### 配置你的Book实体 -从`Acme.BookStore.EntityFrameworkCore` 项目中打开 BookStoreDbContextModelCreatingExtensions.cs 文件, 在 ConfigureBookStore 方法最后添加如下代码来配置Book实体: +在`Acme.BookStore.EntityFrameworkCore`项目中打开`BookStoreDbContextModelCreatingExtensions.cs`文件,并将以下代码添加到`ConfigureBookStore`方法的末尾以配置Book实体: ````C# - builder.Entity(b => - { - b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema); - b.ConfigureExtraProperties(); - }); +builder.Entity(b => +{ + b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema); + b.ConfigureAuditedAggregateRoot(); //auto configure for the base class props + b.Property(x => x.Name).IsRequired().HasMaxLength(128); +}); ```` #### 添加新的Migration并更新数据库 @@ -115,26 +118,27 @@ PM> Update-Database #### 添加示例数据 -`Update-Database`命令会在数据库中创建`Books`表.打开这个表添加几行数据,然后就可以把这些数据展示到页面上: +`Update-Database`命令在数据库中创建了`AppBooks`表. 打开数据库并输入几个示例行,以便在页面上显示它们: ![bookstore-books-table](images/bookstore-books-table.png) ### 创建应用服务 -下一步是创建[应用服务](../../Application-Services.md)来管理(创建,列出,更新,删除...)书籍. +下一步是创建[应用服务](../../Application-Services.md)来管理(创建,列出,更新,删除)书籍. 启动模板中的应用程序层分为两个项目: + +* `Acme.BookStore.Application.Contracts`主要包含你的DTO和应用程序服务接口. +* `Acme.BookStore.Application`包含应用程序服务的实现. #### BookDto -在`Acme.BookStore.Application`项目中添加一个名为`BookDto`的DTO类: +在`Acme.BookStore.Application.Contracts`项目中创建一个名为`BookDto`的DTO类: ````C# using System; using Volo.Abp.Application.Dtos; -using Volo.Abp.AutoMapper; namespace Acme.BookStore { - [AutoMapFrom(typeof(Book))] public class BookDto : AuditedEntityDto { public string Name { get; set; } @@ -151,7 +155,23 @@ namespace Acme.BookStore * **DTO**类被用来在 **表示层** 和 **应用层** **传递数据**.查看[DTO文档](../../Data-Transfer-Objects.md)查看更多信息. * 为了在页面上展示书籍信息,`BookDto`被用来将书籍数据传递到表示层. * `BookDto`继承自 `AuditedEntityDto`.跟上面定义的`Book`类一样具有一些审计属性. -* `[AutoMapFrom(typeof(Book))]`用来创建从`Book`类到`BookDto`的AutoMapper映射.使用这种方法.你可以将`Book`对象自动转换成`BookDto`对象(而不是手动复制所有的属性). + +在将书籍返回到表示层时,需要将`Book`实体转换为`BookDto`对象. [AutoMapper](https://automapper.org)库可以在定义了正确的映射时自动执行此转换. 启动模板配置了AutoMapper,因此你只需在`Acme.BookStore.Application`项目的`BookStoreApplicationAutoMapperProfile`类中定义映射: + +````csharp +using AutoMapper; + +namespace Acme.BookStore +{ + public class BookStoreApplicationAutoMapperProfile : Profile + { + public BookStoreApplicationAutoMapperProfile() + { + CreateMap(); + } + } +} +```` #### CreateUpdateBookDto @@ -184,11 +204,17 @@ namespace Acme.BookStore ```` * 这个DTO类被用于在创建或更新书籍的时候从用户界面获取图书信息. -* 类中的属性定义了数据注解(如`[Required]`)用来定义有效性验证.ABP会自动校验DTO的数据有效性. +* 它定义了数据注释属性(如`[Required]`)来定义属性的验证. DTO由ABP框架[自动验证](../../Validation.md). + +就像上面的`BookDto`一样,创建一个从`CreateUpdateBookDto`对象到`Book`实体的映射: + +````csharp +CreateMap(); +```` #### IBookAppService -为应用服务定义一个名为 `IBookAppService` 的接口: +在`Acme.BookStore.Application.Contracts`项目中定义一个名为`IBookAppService`的接口: ````C# using System; @@ -210,15 +236,14 @@ namespace Acme.BookStore } ```` -* 为应用服务定义接口不是必须的,不过,这是推荐的最佳实践. -* `IAsyncCrudAppService`中定义了基础的 **CRUD**方法:`GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync` 和 `DeleteAsync`.不需要扩展它.取而代之,你可以继承空的`IApplicationService`接口定义你自己的方法. -* `IAsyncCrudAppService`有一些变体,你可以为每一个方法使用单个或者多个的DTO.(译者注:意思是类似EntityDto和UpdateEntityDto可以用同一个,也可以分别单独指定 -) +* 框架定义应用程序服务的接口不是必需的. 但是,它被建议作为最佳实践. +* `IAsyncCrudAppService`定义了常见的**CRUD**方法:`GetAsync`,`GetListAsync`,`CreateAsync`,`UpdateAsync`和`DeleteAsync`. 你可以从空的`IApplicationService`接口继承并手动定义自己的方法. +* `IAsyncCrudAppService`有一些变体, 你可以在每个方法中使用单独的DTO,也可以分别单独指定. #### BookAppService -创建 `BookAppService` 并实现 `IBookAppService`接口: +在`Acme.BookStore.Application`项目中实现名为`BookAppService`的`IBookAppService`: ````C# using System; @@ -243,23 +268,23 @@ namespace Acme.BookStore ```` * `BookAppService`继承了`AsyncCrudAppService<...>`.`AsyncCrudAppService<...>`实现了上面定义的CRUD方法. -* `BookAppService`注入了`IRepository`,`IRepository`是默认为`Book`创建的仓储.ABP会自动为每一个聚合根(或实体)创建仓储.参考[仓储文档](../../Repositories.md). -* `BookAppService`使用了 `IObjectMapper` 将`Book`转换成`BookDto`,将`CreateUpdateBookDto`转换成`Book`.启动模板中使用了[AutoMapper](http://automapper.org/)作为对象映射提供程序.你可以像上面那样使用`AutoMapFrom` 和 `AutoMapTo`定义映射.查看[AutoMapper集成文档](../../AutoMapper-Integration.md)获取更多信息. +* `BookAppService`注入`IRepository `,这是`Book`实体的默认仓储. ABP自动为每个聚合根(或实体)创建默认仓储. 请参阅[仓储文档](../../Repositories.md) +* `BookAppService`使用`IObjectMapper`将`Book`对象转换为`BookDto`对象, 将`CreateUpdateBookDto`对象转换为`Book`对象. 启动模板使用[AutoMapper](http://automapper.org/)库作为对象映射提供程序. 你之前定义了映射, 因此它将按预期工作. ### 自动生成API Controllers -你通常需要创建 **Controllers** 将应用服务暴露为 **HTTP API**.这样浏览器或第三方客户端可以通过AJAX的方式访问它们. - -ABP可以通过约定[**自动**](../../AspNetCore/Auto-API-Controllers.md)将应用服务转换成MVC API Controllers. +你通常创建**Controller**以将应用程序服务公开为**HTTP API**端点. 因此允许浏览器或第三方客户端通过AJAX调用它们. ABP可以[**自动**](../../AspNetCore/Auto-API-Controllers.md)按照惯例将你的应用程序服务配置为MVC API控制器. #### Swagger UI -启动模板使用了[Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)库配置了[swagger UI](https://swagger.io/tools/swagger-ui/).运行程序并在浏览器中输入`http://localhost:53929/swagger/`. +启动模板配置为使用[Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)运行[swagger UI](https://swagger.io/tools/swagger-ui/). 运行应用程序并在浏览器中输入`https://localhost:XXXX/swagger/`(用您自己的端口替换XXXX)作为URL. 你会看到一些内置的接口和`Book`的接口,它们都是REST风格的: ![bookstore-swagger](images/bookstore-swagger.png) +Swagger有一个很好的UI来测试API. 你可以尝试执行`[GET] /api/app/book` API来获取书籍列表. + ### 动态JavaScript代理 在Javascript端通过AJAX的方式调用HTTP API接口是很常见的,你可以使用`$.ajax`或这其他的工具来调用接口.当然,ABP中提供了更好的方式. @@ -270,6 +295,8 @@ ABP **自动** 为所有的API接口创建了JavaScript **代理**.因此,你可 你可以使用你钟爱的浏览器的 **开发者控制台** 中轻松测试JavaScript代理.运行程序,并打开浏览器的 **开发者工具**(快捷键:F12),切换到 **Console** 标签,输入下面的代码并回车: +你现在可以使用自己喜欢的浏览器的**开发者控制台**轻松测试JavaScript代理. 运行应用程序, 打开浏览器的**开发者工具**(快捷键:F12),切换到**Console**选项卡,输入以下代码并按回车键. + ````js acme.bookStore.book.getList({}).done(function (result) { console.log(result); }); ```` @@ -277,7 +304,7 @@ acme.bookStore.book.getList({}).done(function (result) { console.log(result); }) * `acme.bookStore`是`BookAppService`的命名空间,转换成了[驼峰命名](https://en.wikipedia.org/wiki/Camel_case). * `book`是`BookAppService`转换后的名字(去除了AppService后缀并转成了驼峰命名). * `getList`是定义在`AsyncCrudAppService`基类中的`GetListAsync`方法转换后的名字(去除了Async后缀并转成了驼峰命名). -* `{}`参数用来传递一个空的对象给`GetListAsync`方法.GetListAsync期望的参数是`PagedAndSortedResultRequestDto`类型的对象,`PagedAndSortedResultRequestDto`类型中定义了分页和排序选项. +* `{}`参数用于将空对象发送到`GetListAsync`方法,该方法通常需要一个类型为`PagedAndSortedResultRequestDto`的对象,用于向服务器发送分页和排序选项(所有属性都是可选的,所以你可以发送一个空对象). * `getList`方法返回了一个`promise`.因此,你可以传递一个回调函数到`done`(或者`then`)方法中来获取服务返回的结果. 运行这段代码会产生下面的输出: @@ -300,7 +327,7 @@ acme.bookStore.book.create({ name: 'Foundation', type: 7, publishDate: '1951-05- successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7 ```` -检查数据库表`books`中的数据,你会发现多了一行新数据,你也可以尝试`get`, `update` 和 `delete`方法. +检查数据库中的`Books`表以查看新书. 你可以自己尝试`get`,`update`和`delete`功能. ### 创建书籍页面 @@ -309,20 +336,21 @@ successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7 在 `Acme.BookStore.Web`项目的`Pages`文件夹下创建一个新的文件夹叫`Books`并添加一个名为`Index.cshtml`的Razor Page. -![bookstore-add-index-page](images/bookstore-add-index-page.png) +![bookstore-add-index-page](images/bookstore-add-index-page-v2.png) 打开`Index.cshtml`并把内容修改成下面这样: ````html @page -@using Acme.BookStore.Pages.Books -@inherits Acme.BookStore.Pages.BookStorePageBase +@using Acme.BookStore.Web.Pages.Books +@inherits Acme.BookStore.Web.Pages.BookStorePageBase @model IndexModel

Books

```` -* 修改Razor View Page Model的默认继承,使页面 **继承** 自`BookStorePageBase`类(代替`PageModel`).`BookStorePageBase`类来自于启动模板,它提供了一些公开的可以被所有的页面使用的属性/方法. +* 此代码更改了Razor View Page Model的默认继承,因此它从`BookStorePageBase`类(而不是`PageModel`)继承.启动模板附带的`BookStorePageBase`类,提供所有页面使用的一些共享属性/方法. +* 确保`IndexModel`(Index.cshtml.cs)具有`Acme.BookStore.Pages.Books`命名空间,或者在`Index.cshtml`中更新它. #### 将Books页面添加到主菜单 @@ -337,17 +365,16 @@ context.Menu.AddItem( #### 本地化菜单 -本地化的文本在`Acme.BookStore.Domain`项目的`Localization/BookStore`文件夹中. +本地化文本位于`Acme.BookStore.Domain.Shared`项目的`Localization/BookStore`文件夹下: -![bookstore-localization-files](images/bookstore-localization-files.png) +![bookstore-localization-files](images/bookstore-localization-files-v2.png) -打开`en.json`文件,为`Menu:BookStore` 和 `Menu:Books`添加本地化文本: +打开`en.json`文件,将`Menu:BookStore`和`Menu:Books`键的本地化文本添加到文件末尾: ````json { "culture": "en", "texts": { - //... "Menu:BookStore": "Book Store", "Menu:Books": "Books" } @@ -355,9 +382,9 @@ context.Menu.AddItem( ```` * ABP的本地化功能建立在[ASP.NET Core's standard localization]((https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization))之上并增加了一些扩展.查看[本地化文档](../../Localization.md). -* 本地化中key的名字是随便定义的,你可以随意命名.我们喜欢为菜单添加`Menu`命名空间,以区别于其他的文本.如果文本没有在本地化文件中定义,就会 **返回** 本地的化的key(ASP.NET Core的标准做法). +* 本地化key是任意的. 你可以设置任何名称. 我们更喜欢为菜单项添加`Menu:`前缀以区别于其他文本. 如果未在本地化文件中定义文本,则它将**返回**到本地化的key(ASP.NET Core的标准行为). -运行程序就会看到菜单已经添加到了顶部: +运行该应用程序,看到新菜单项已添加到顶部栏: ![bookstore-menu-items](images/bookstore-menu-items.png) @@ -365,17 +392,16 @@ context.Menu.AddItem( #### 书籍列表 -我们会在页面上使用JQuery插件[Datatables.net](https://datatables.net/)来展示列表.Datatables可以完全通过AJAX工作,所以它很快而且有良好的用户体验.启动模板中已经配置好了Datatables插件,因此你可以在你的页面中直接使用,不需要引用样式和脚本文件. +我们将使用[Datatables.net](https://datatables.net/)JQuery插件来显示页面上的表格列表. 数据表可以完全通过AJAX工作,速度快,并提供良好的用户体验. Datatables插件在启动模板中配置,因此你可以直接在任何页面中使用它,而需要在页面中引用样式和脚本文件. -##### 修改Index.cshtml +##### Index.cshtml 将`Pages/Books/Index.cshtml`改成下面的样子: ````html @page -@using Acme.BookStore.Pages.Books -@inherits Acme.BookStore.Pages.BookStorePageBase -@model IndexModel +@inherits Acme.BookStore.Web.Pages.BookStorePageBase +@model Acme.BookStore.Web.Pages.Books.IndexModel @section scripts { @@ -408,7 +434,7 @@ context.Menu.AddItem( 在`Pages/Books/`文件夹中创建 `index.js`文件 -![bookstore-index-js-file](images/bookstore-index-js-file.png) +![bookstore-index-js-file](images/bookstore-index-js-file-v2.png) `index.js`的内容如下: @@ -430,7 +456,7 @@ $(function () { * `abp.libs.datatables.createAjax`是帮助ABP的动态JavaScript API代理跟Datatable的格式相适应的辅助方法. * `abp.libs.datatables.normalizeConfiguration`是另一个辅助方法.不是必须的, 但是它通过为缺少的选项提供常规值来简化数据表配置. * `acme.bookStore.book.getList`是获取书籍列表的方法(上面已经介绍过了) -* 查看 [Datatable's 文档](https://datatables.net/manual/) 了解更多配置项. +* 查看 [Datatable文档](https://datatables.net/manual/) 了解更多配置项. 最终的页面如下: diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md index 9e54ac7a2f..d462851f71 100644 --- a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md @@ -2,13 +2,13 @@ ### 关于本教程 -这是本教程所有章节中的第二章.下面是所有的章节: +这是ASP.NET Core MVC教程系列的第二章. 查看其它章节 * [Part I: 创建项目和书籍列表页面](Part-I.md) * **Part II: 创建,编辑,删除书籍(本章)** * [Part III: 集成测试](Part-III.md) -你可以从 [这里](https://github.com/volosoft/abp/tree/master/samples/BookStore) 下载本程序的**源码**. +你可以从[GitHub存储库](https://github.com/volosoft/abp/tree/master/samples/BookStore)访问应用程序的**源代码**. ### 新增 Book 实体 @@ -20,7 +20,7 @@ 在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个 `CreateModal.cshtml` Razor页面: -![bookstore-add-create-dialog](images/bookstore-add-create-dialog.png) +![bookstore-add-create-dialog](images/bookstore-add-create-dialog-v2.png) ##### CreateModal.cshtml.cs @@ -30,7 +30,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -namespace Acme.BookStore.Pages.Books +namespace Acme.BookStore.Web.Pages.Books { public class CreateModalModel : BookStorePageModelBase { @@ -63,9 +63,9 @@ namespace Acme.BookStore.Pages.Books ````html @page -@inherits Acme.BookStore.Pages.BookStorePageBase +@inherits Acme.BookStore.Web.Pages.BookStorePageBase @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal -@model Acme.BookStore.Pages.Books.CreateModalModel +@model Acme.BookStore.Web.Pages.Books.CreateModalModel @{ Layout = null; } @@ -138,12 +138,12 @@ $('#NewBookButton').click(function (e) { 展开 `EditModal.cshtml`,打开 `EditModal.cshtml.cs` 文件( `EditModalModel` 类) 并替换成以下代码: -````C# +````csharp using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -namespace Acme.BookStore.Pages.Books +namespace Acme.BookStore.Web.Pages.Books { public class EditModalModel : BookStorePageModelBase { @@ -180,38 +180,26 @@ namespace Acme.BookStore.Pages.Books * 在 `OnGetAsync` 方法中,将 `BookAppService.GetAsync` 方法返回的 `BookDto` 映射成 `CreateUpdateBookDto` 并赋值给Book属性. * `OnPostAsync` 方法直接使用 `BookAppService.UpdateAsync` 来更新实体. -#### CreateUpdateBookDto +#### BookDto到CreateUpdateBookDto对象映射 -为了执行从 `BookDto` 到 `CreateUpdateBookDto` 的对象映射, 按如下所示修改 `CreateUpdateBookDto`类: +为了执行`BookDto`到`CreateUpdateBookDto`对象映射,请打开`Acme.BookStore.Web`项目中的`BookStoreWebAutoMapperProfile.cs`并更改它,如下所示: -````C# -using System; -using System.ComponentModel.DataAnnotations; -using Volo.Abp.AutoMapper; +````csharp +using AutoMapper; -namespace Acme.BookStore +namespace Acme.BookStore.Web { - [AutoMapTo(typeof(Book))] - [AutoMapFrom(typeof(BookDto))] - public class CreateUpdateBookDto + public class BookStoreWebAutoMapperProfile : Profile { - [Required] - [StringLength(128)] - public string Name { get; set; } - - [Required] - public BookType Type { get; set; } = BookType.Undefined; - - [Required] - public DateTime PublishDate { get; set; } - - [Required] - public float Price { get; set; } + public BookStoreWebAutoMapperProfile() + { + CreateMap(); + } } } ```` -* 仅添加 `[AutoMapFrom(typeof(BookDto))]` 特性就可以创建上述映射关系. +* 刚刚添加了`CreateMap();`作为映射定义. #### EditModal.cshtml @@ -219,8 +207,8 @@ namespace Acme.BookStore ````html @page -@inherits Acme.BookStore.Pages.BookStorePageBase -@using Acme.BookStore.Pages.Books +@inherits Acme.BookStore.Web.Pages.BookStorePageBase +@using Acme.BookStore.Web.Pages.Books @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @model EditModalModel @{ @@ -231,7 +219,7 @@ namespace Acme.BookStore - + @@ -240,8 +228,8 @@ namespace Acme.BookStore 这个页面内容和 `CreateModal.cshtml` 非常相似,除了以下几点: -* 此页面包含了一个 `abp-input` 以保存所编辑book实体的 `Id` 属性值. -* 此页面指定的post地址是 `Books/EditModal` ,并用文本 *Update* 作为 modal 标题. +* 它包含`id`属性的`abp-input`, 用于存储编辑书的id(它是隐藏的Input) +* 此页面指定的post地址是`Books/EditModal`, 并用文本 *Update* 作为 modal 标题. #### 为表格添加 "操作(Actions)" 下拉菜单 @@ -420,6 +408,12 @@ $(function () { }); ```` +打开`Acme.BookStore.Domain.Shared`项目中的`en.json`并添加以下行: + +````json +"BookDeletionConfirmationMessage": "Are you sure to delete the book {0}?" +```` + 运行程序并尝试删除一个book实体. ### 下一章 diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md index 80c28acc91..c31f750832 100644 --- a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md @@ -2,23 +2,21 @@ ### 关于本教程 -这是本教程所有章节中的第三章.下面是所有的章节: +这是ASP.NET Core MVC教程系列的第三章. 查看其它章节 - [Part I: 创建项目和书籍列表页面](Part-I.md) - [Part II: 创建,编辑,删除书籍](Part-II.md) - **Part III: 集成测试(本章)** -你可以从 [这里](https://github.com/volosoft/abp/tree/master/samples/BookStore) 下载本程序的**源码**. +你可以从[GitHub存储库](https://github.com/volosoft/abp/tree/master/samples/BookStore)访问应用程序的**源代码**. ### 解决方案中的测试项目 -本解决方案中有两个测试项目: +解决方案中有多个测试项目: +![bookstore-test-projects-v2](images/bookstore-test-projects-v2.png) -* `Acme.BookStore.Application.Tests` 项目用于单元测试和集成测试.你可以在这个项目中为Application Service方法写测试代码.这个项目使用了 **EF Core SQLite in-memory** 数据库. -* `Acme.BookStore.Web.Tests` 项目用于包含Web层的完整集成测试.所以,你也可以在这里写关于UI页面的测试. - -测试项目使用了以下库: +每个项目用于测试相关的应用程序项目.测试项目使用以下库进行测试: * [xunit](https://xunit.github.io/) 作为主测试框架. * [Shoudly](http://shouldly.readthedocs.io/en/latest/) 作为断言库. @@ -26,79 +24,39 @@ ### 添加测试用数据 -起始模板在 `Acme.BookStore.Application.Tests` 项目中包含了 `BookStoreTestDataBuilder` 类,用于创建一些测试用数据. 相关代码如下所示: - -````C# -using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Identity; -using Volo.Abp.Threading; - -namespace Acme.BookStore -{ - public class BookStoreTestDataBuilder : ITransientDependency - { - private readonly IIdentityDataSeeder _identityDataSeeder; - - public BookStoreTestDataBuilder(IIdentityDataSeeder identityDataSeeder) - { - _identityDataSeeder = identityDataSeeder; - } - - public void Build() - { - AsyncHelper.RunSync(BuildInternalAsync); - } - - public async Task BuildInternalAsync() - { - await _identityDataSeeder.SeedAsync("1q2w3E*"); - } - } -} -```` - -* 这里直接使用了identity模块实现的 `IIdentityDataSeeder` 接口,创建了一个admin角色和admin用户.你可以在测试代码中使用它们. -* 你可以在 `BuildInternalAsync` 方法中添加你自己的测试数据. - -按下方所示修改 `BookStoreTestDataBuilder` 类: +启动模板包含`Acme.BookStore.TestBase`项目中的`BookStoreTestDataSeedContributor`类,它创建一些数据来运行测试. +更改`BookStoreTestDataSeedContributor`类如下所示: ````C# using System; using System.Threading.Tasks; +using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; -using Volo.Abp.Identity; -using Volo.Abp.Threading; +using Volo.Abp.Guids; namespace Acme.BookStore { - public class BookStoreTestDataBuilder : ITransientDependency + public class BookStoreTestDataSeedContributor + : IDataSeedContributor, ITransientDependency { - private readonly IIdentityDataSeeder _identityDataSeeder; private readonly IRepository _bookRepository; + private readonly IGuidGenerator _guidGenerator; - public BookStoreTestDataBuilder( - IIdentityDataSeeder identityDataSeeder, - IRepository bookRepository) + public BookStoreTestDataSeedContributor( + IRepository bookRepository, + IGuidGenerator guidGenerator) { - _identityDataSeeder = identityDataSeeder; _bookRepository = bookRepository; + _guidGenerator = guidGenerator; } - public void Build() + public async Task SeedAsync(DataSeedContext context) { - AsyncHelper.RunSync(BuildInternalAsync); - } - - public async Task BuildInternalAsync() - { - await _identityDataSeeder.SeedAsync("1q2w3E*"); - await _bookRepository.InsertAsync( new Book { - Id = Guid.NewGuid(), + Id = _guidGenerator.Create(), Name = "Test book 1", Type = BookType.Fantastic, PublishDate = new DateTime(2015, 05, 24), @@ -109,7 +67,7 @@ namespace Acme.BookStore await _bookRepository.InsertAsync( new Book { - Id = Guid.NewGuid(), + Id = _guidGenerator.Create(), Name = "Test book 2", Type = BookType.Science, PublishDate = new DateTime(2014, 02, 11), @@ -121,7 +79,8 @@ namespace Acme.BookStore } ```` -* 通过构造函数注入 `IRepository`,在 `BuildInternalAsync` 方法中用它创建两个book实体. +* 注入`IRepository`并在`SeedAsync`中使用它来创建两个书实体作为测试数据. +* 使用`IGuidGenerator`服务创建GUID. 虽然`Guid.NewGuid()`非常适合测试,但`IGuidGenerator`在使用真实数据库时还有其他特别重要的功能(参见[Guid生成文档](../../Guid-Generation.md)了解更多信息). ### 测试 BookAppService @@ -211,6 +170,8 @@ public async Task Should_Not_Create_A_Book_Without_Name() * 由于 `Name` 是空值, ABP 抛出一个 `AbpValidationException` 异常. -### 测试 Web 页面 +打开**测试资源管理器**(测试 -> Windows -> 测试资源管理器)并**执行**所有测试: + +![bookstore-appservice-tests](images/bookstore-appservice-tests.png) -TODO +恭喜, 绿色图标表示测试已成功通过! diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png new file mode 100644 index 0000000000..fcadfd7a82 Binary files /dev/null and b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog-v2.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png deleted file mode 100644 index 44398e6b4c..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-create-dialog.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png new file mode 100644 index 0000000000..2606bbab32 Binary files /dev/null and b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page-v2.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png deleted file mode 100644 index 2452ca546e..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-add-index-page.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png new file mode 100644 index 0000000000..942d2832a6 Binary files /dev/null and b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-appservice-tests.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png new file mode 100644 index 0000000000..1ba9a48600 Binary files /dev/null and b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file-v2.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png deleted file mode 100644 index e36c5dfff7..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-index-js-file.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png new file mode 100644 index 0000000000..79314dd2dc Binary files /dev/null and b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files-v2.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png deleted file mode 100644 index ca1da1c7ec..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-localization-files.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png new file mode 100644 index 0000000000..8701164d75 Binary files /dev/null and b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects-v2.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png deleted file mode 100644 index f9511d7b11..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-test-projects.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png deleted file mode 100644 index 48b5881e96..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png and /dev/null differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png new file mode 100644 index 0000000000..307e3516a5 Binary files /dev/null and b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v3.png differ diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png deleted file mode 100644 index b47636cd68..0000000000 Binary files a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution.png and /dev/null differ diff --git a/docs/zh-Hans/Value-Types.md b/docs/zh-Hans/Value-Types.md new file mode 100644 index 0000000000..a0e340063c --- /dev/null +++ b/docs/zh-Hans/Value-Types.md @@ -0,0 +1,3 @@ +## Value Types + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 8e88cf6921..c20bd028a6 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -41,6 +41,10 @@ } ] }, + { + "text": "CLI", + "path": "CLI.md" + }, { "text": "基础知识", "items": [ diff --git a/docs/zh-Hans/images/bookstore-user-management-v2.png b/docs/zh-Hans/images/bookstore-user-management-v2.png new file mode 100644 index 0000000000..dd95740754 Binary files /dev/null and b/docs/zh-Hans/images/bookstore-user-management-v2.png differ diff --git a/docs/zh-Hans/images/bookstore-user-management.png b/docs/zh-Hans/images/bookstore-user-management.png deleted file mode 100644 index bc3c176557..0000000000 Binary files a/docs/zh-Hans/images/bookstore-user-management.png and /dev/null differ diff --git a/docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png b/docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png deleted file mode 100644 index 48b5881e96..0000000000 Binary files a/docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png and /dev/null differ diff --git a/docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png b/docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png new file mode 100644 index 0000000000..307e3516a5 Binary files /dev/null and b/docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png differ diff --git a/docs/zh-Hans/images/bookstore-visual-studio-solution.png b/docs/zh-Hans/images/bookstore-visual-studio-solution.png deleted file mode 100644 index b47636cd68..0000000000 Binary files a/docs/zh-Hans/images/bookstore-visual-studio-solution.png and /dev/null differ diff --git a/docs/zh-Hans/images/db-migrator-app.png b/docs/zh-Hans/images/db-migrator-app.png new file mode 100644 index 0000000000..ace6abb226 Binary files /dev/null and b/docs/zh-Hans/images/db-migrator-app.png differ diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 10be5a757d..be77f0ab24 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -1,7 +1,6 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28803.156 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}" EndProject @@ -219,11 +218,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp", "src\Volo.Abp\Vo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Authentication.JwtBearer", "src\Volo.Abp.AspNetCore.Authentication.JwtBearer\Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj", "{46C6336C-A1D8-4858-98CE-6F4C698C5A77}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Cli", "src\Volo.Abp.Cli\Volo.Abp.Cli.csproj", "{69168816-4394-4DDA-BB6B-C21983D37F0B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli", "src\Volo.Abp.Cli\Volo.Abp.Cli.csproj", "{69168816-4394-4DDA-BB6B-C21983D37F0B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation", "src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj", "{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FluentValidation", "src\Volo.Abp.FluentValidation\Volo.Abp.FluentValidation.csproj", "{43D5FE61-ECBF-4B16-AD95-0043E18EB93A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.FluentValidation.Tests", "test\Volo.Abp.FluentValidation.Tests\Volo.Abp.FluentValidation.Tests.csproj", "{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.FluentValidation.Tests", "test\Volo.Abp.FluentValidation.Tests\Volo.Abp.FluentValidation.Tests.csproj", "{E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Sqlite", "src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj", "{58CF8957-5045-4F81-884D-72DF48F721CC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli.Core", "src\Volo.Abp.Cli.Core\Volo.Abp.Cli.Core.csproj", "{3DA9923E-048E-4FE7-9748-3A0194F5D196}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Specifications", "src\Volo.Abp.Specifications\Volo.Abp.Specifications.csproj", "{2C621EED-563C-4F81-A75E-50879E173544}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Specifications.Tests", "test\Volo.Abp.Specifications.Tests\Volo.Abp.Specifications.Tests.csproj", "{D078553A-C70C-4F56-B3E2-9C5BA6384C72}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Cli.Core.Tests", "test\Volo.Abp.Cli.Core.Tests\Volo.Abp.Cli.Core.Tests.csproj", "{F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Widgets", "src\Volo.Abp.AspNetCore.Mvc.UI.Widgets\Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj", "{EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Dashboards", "src\Volo.Abp.AspNetCore.Mvc.UI.Dashboards\Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj", "{054D766D-5992-460E-A4D8-936D80BE2C1A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -655,6 +668,10 @@ Global {46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Debug|Any CPU.Build.0 = Debug|Any CPU {46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Release|Any CPU.ActiveCfg = Release|Any CPU {46C6336C-A1D8-4858-98CE-6F4C698C5A77}.Release|Any CPU.Build.0 = Release|Any CPU + {69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.Build.0 = Release|Any CPU {43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Debug|Any CPU.Build.0 = Debug|Any CPU {43D5FE61-ECBF-4B16-AD95-0043E18EB93A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -663,10 +680,34 @@ Global {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A}.Release|Any CPU.Build.0 = Release|Any CPU - {69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69168816-4394-4DDA-BB6B-C21983D37F0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69168816-4394-4DDA-BB6B-C21983D37F0B}.Release|Any CPU.Build.0 = Release|Any CPU + {58CF8957-5045-4F81-884D-72DF48F721CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58CF8957-5045-4F81-884D-72DF48F721CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58CF8957-5045-4F81-884D-72DF48F721CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58CF8957-5045-4F81-884D-72DF48F721CC}.Release|Any CPU.Build.0 = Release|Any CPU + {3DA9923E-048E-4FE7-9748-3A0194F5D196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DA9923E-048E-4FE7-9748-3A0194F5D196}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DA9923E-048E-4FE7-9748-3A0194F5D196}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DA9923E-048E-4FE7-9748-3A0194F5D196}.Release|Any CPU.Build.0 = Release|Any CPU + {2C621EED-563C-4F81-A75E-50879E173544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C621EED-563C-4F81-A75E-50879E173544}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C621EED-563C-4F81-A75E-50879E173544}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C621EED-563C-4F81-A75E-50879E173544}.Release|Any CPU.Build.0 = Release|Any CPU + {D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D078553A-C70C-4F56-B3E2-9C5BA6384C72}.Release|Any CPU.Build.0 = Release|Any CPU + {F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F006B0B4-F25D-4511-9FB3-F17AA44BDCEA}.Release|Any CPU.Build.0 = Release|Any CPU + {EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A}.Release|Any CPU.Build.0 = Release|Any CPU + {054D766D-5992-460E-A4D8-936D80BE2C1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {054D766D-5992-460E-A4D8-936D80BE2C1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {054D766D-5992-460E-A4D8-936D80BE2C1A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -778,9 +819,16 @@ Global {575BEFA1-19C2-49B1-8D31-B5D4472328DE} = {447C8A77-E5F0-4538-8687-7383196D04EA} {6C161F55-54B6-42A5-B177-3B0ED50323C1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {46C6336C-A1D8-4858-98CE-6F4C698C5A77} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {69168816-4394-4DDA-BB6B-C21983D37F0B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {43D5FE61-ECBF-4B16-AD95-0043E18EB93A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {E9E1714F-7ED2-4BD1-BA4A-BA06E398288A} = {447C8A77-E5F0-4538-8687-7383196D04EA} - {69168816-4394-4DDA-BB6B-C21983D37F0B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {58CF8957-5045-4F81-884D-72DF48F721CC} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {3DA9923E-048E-4FE7-9748-3A0194F5D196} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {2C621EED-563C-4F81-A75E-50879E173544} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {D078553A-C70C-4F56-B3E2-9C5BA6384C72} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {F006B0B4-F25D-4511-9FB3-F17AA44BDCEA} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {EE1AAB08-3FBD-487F-B0B4-BEBA4B69528A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {054D766D-5992-460E-A4D8-936D80BE2C1A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs index a9f3bb9c62..c7b20c6a33 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -205,6 +205,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual List ExploreModelsRecursively(List list, ModelExplorer model) { + if (model.GetAttribute() != null) + { + return list; + } + if (IsCsharpClassOrPrimitive(model.ModelType) || IsListOfCsharpClassOrPrimitive(model.ModelType)) { list.Add(ModelExplorerToModelExpressionConverter(model)); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index cdf1f30788..dc2f04b623 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -35,10 +35,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form TagHelper.AspFor.Name, SurroundInnerHtmlAndGet(context, output, innerHtml, isCheckbox), order, - out var surpress + out var suppress ); - if (surpress) + if (suppress) { output.SuppressOutput(); } @@ -389,10 +389,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return idAttr != null ? "for=\"" + idAttr.Value + "\"" : ""; } - protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool surpress) + protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress) { var list = context.GetValue>(FormGroupContents) ?? new List(); - surpress = list == null; + suppress = list == null; if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\""))) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs index 6ec2911714..f89d3c1fee 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpRadioInputTagHelperService.cs @@ -29,9 +29,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form var html = GetHtml(context, output, selectItems); - AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, html, order, out var surpress); + AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, html, order, out var suppress); - if (surpress) + if (suppress) { output.SuppressOutput(); } @@ -157,10 +157,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return TagHelper.AspFor.ModelExplorer.Model?.ToString(); } - protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool surpress) + protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress) { var list = context.GetValue>(FormGroupContents) ?? new List(); - surpress = list == null; + suppress = list == null; if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\""))) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs index cd3294d66f..d0b1a494ae 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs @@ -33,9 +33,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form var order = TagHelper.AspFor.ModelExplorer.GetDisplayOrder(); - AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, SurroundInnerHtmlAndGet(context, output, innerHtml), order, out var surpress); + AddGroupToFormGroupContents(context, TagHelper.AspFor.Name, SurroundInnerHtmlAndGet(context, output, innerHtml), order, out var suppress); - if (surpress) + if (suppress) { output.SuppressOutput(); } @@ -187,8 +187,22 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { var localizer = _tagHelperLocalizer.GetLocalizer(explorer); - var selectItems = explorer.Metadata.IsEnum ? explorer.ModelType.GetTypeInfo().GetMembers(BindingFlags.Public | BindingFlags.Static) - .Select((t, i) => new SelectListItem { Value = i.ToString(), Text = GetLocalizedPropertyName(localizer, explorer.ModelType, t.Name) }).ToList() : null; + var selectItems = new List(); + var isNullableType = Nullable.GetUnderlyingType(explorer.ModelType) != null; + var enumType = explorer.ModelType; + + if (isNullableType) + { + enumType = Nullable.GetUnderlyingType(explorer.ModelType); + selectItems.Add(new SelectListItem()); + } + + selectItems.AddRange(enumType.GetEnumNames() + .Select(enumName => new SelectListItem + { + Value = Convert.ToUInt64(Enum.Parse(enumType, enumName)).ToString(), + Text = GetLocalizedPropertyName(localizer, enumType, enumName) + })); return selectItems; } @@ -304,10 +318,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form return idAttr != null ? "for=\"" + idAttr.Value + "\"" : ""; } - protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool surpress) + protected virtual void AddGroupToFormGroupContents(TagHelperContext context, string propertyName, string html, int order, out bool suppress) { var list = context.GetValue>(FormGroupContents) ?? new List(); - surpress = list == null; + suppress = list == null; if (list != null && !list.Any(igc => igc.HtmlContent.Contains("id=\"" + propertyName.Replace('.', '_') + "\""))) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DynamicFormIgnore.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DynamicFormIgnore.cs new file mode 100644 index 0000000000..8726fc89ab --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/DynamicFormIgnore.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form +{ + [AttributeUsage(AttributeTargets.Property)] + public class DynamicFormIgnore : Attribute + { + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs index 714a7aa68b..a0f0e65f1e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json new file mode 100644 index 0000000000..0ce91a367d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57333/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Volo.Abp.AspNetCore.Mvc.UI.Dashboards": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:57343/" + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj new file mode 100644 index 0000000000..633b783ad0 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo.Abp.AspNetCore.Mvc.UI.Dashboards.csproj @@ -0,0 +1,35 @@ + + + + + + netstandard2.0 + Volo.Abp.AspNetCore.Mvc.UI.Dashboards + Volo.Abp.AspNetCore.Mvc.UI.Dashboards + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + true + Library + + + + + + + + + + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs new file mode 100644 index 0000000000..6fcb11d28e --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpAspNetCoreMvcUiDashboardsModule.cs @@ -0,0 +1,20 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + [DependsOn( + typeof(AbpAspNetCoreMvcUiWidgetsModule) + )] + public class AbpAspNetCoreMvcUiDashboardsModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs new file mode 100644 index 0000000000..f351abd952 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardScriptContributor.cs @@ -0,0 +1,8 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public class AbpBasicDashboardScriptContributor : BundleContributor + { + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardStyleContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardStyleContributor.cs new file mode 100644 index 0000000000..5c074783fc --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/AbpBasicDashboardStyleContributor.cs @@ -0,0 +1,8 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public class AbpBasicDashboardStyleContributor : BundleContributor + { + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewComponent.cshtml.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewComponent.cshtml.cs new file mode 100644 index 0000000000..9309bcdf7d --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewComponent.cshtml.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard +{ + public class DashboardViewComponent : AbpViewComponent + { + private readonly DashboardOptions _dashboardOptions; + private readonly WidgetOptions _widgetOptions; + + public DashboardViewComponent(IOptions dashboardOptions, IOptions widgetOptions) + { + _dashboardOptions = dashboardOptions.Value; + _widgetOptions = widgetOptions.Value; + } + + public IViewComponentResult Invoke(string dashboardName) + { + var dashboard = _dashboardOptions.Dashboards.Single(d => d.Name.Equals(dashboardName)); + + var model = new DashboardViewModel(dashboard, _widgetOptions.Widgets); + + return View("~/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml", model); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewModel.cs new file mode 100644 index 0000000000..47405ae63c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/DashboardViewModel.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard +{ + public class DashboardViewModel + { + public DashboardDefinition Dashboard { get; set; } + + public List Widgets { get; set; } + + public DashboardViewModel(DashboardDefinition dashboard, List widgets) + { + Dashboard = dashboard; + Widgets = widgets; + } + + public WidgetDefinition GetWidget(string name) + { + return Widgets.Single(d => d.Name.Equals(name)); + } + + public async Task CheckPermissionsAsync(IAuthorizationService authorizationService, WidgetDefinition widget) + { + foreach (var permission in widget.RequiredPermissions) + { + if (!await authorizationService.IsGrantedAsync(permission)) + { + return false; + } + } + + return true; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml new file mode 100644 index 0000000000..89fecb905c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/Components/Dashboard/Default.cshtml @@ -0,0 +1,26 @@ +@using Microsoft.AspNetCore.Authorization +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Widgets +@inject IAuthorizationService AuthorizationService +@inject IWidgetRenderer WidgetRenderer +@inject IStringLocalizerFactory localizer +@model Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard.DashboardViewModel +@{ +} + + + @foreach (var widgetConfiguration in Model.Dashboard.AvailableWidgets) + { + var widgetDefinition = Model.GetWidget(widgetConfiguration.WidgetName); + if (await Model.CheckPermissionsAsync(AuthorizationService, widgetDefinition)) + { + widgetDefinition.DefaultDimensions = widgetConfiguration.Dimensions ?? widgetDefinition.DefaultDimensions ?? new WidgetDimensions(6, 4); + + + + @await WidgetRenderer.RenderAsync(Component, widgetDefinition.Name) + + } + } + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardDefinition.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardDefinition.cs new file mode 100644 index 0000000000..20f8da6b75 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardDefinition.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; +using Volo.Abp.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public class DashboardDefinition + { + /// + /// Unique name of the dashboard. + /// + [NotNull] + public string Name { get; } + + /// + /// A list of Widgets available for this dashboard. + /// + public List AvailableWidgets { get; } + + /// + /// Display name of the widget. + /// + [NotNull] + public ILocalizableString DisplayName + { + get => _displayName; + set => _displayName = Check.NotNull(value, nameof(value)); + } + private ILocalizableString _displayName; + + public DashboardDefinition( + [NotNull] string name, + [CanBeNull] ILocalizableString displayName) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + DisplayName = displayName ?? new FixedLocalizableString(name); + + AvailableWidgets = new List(); + } + + public DashboardDefinition WithWidget(string widgetName, WidgetLocation location = null, WidgetDimensions dimensions = null) + { + AvailableWidgets.Add( new DashboardWidgetConfiguration(widgetName, dimensions, location)); + + return this; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardOptions.cs new file mode 100644 index 0000000000..fc3ca09b74 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardOptions.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public class DashboardOptions + { + public List Dashboards { get; } + + public DashboardOptions() + { + Dashboards = new List(); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardRenderer.cs new file mode 100644 index 0000000000..d35c58f0e2 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardRenderer.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Dashboards.Components.Dashboard; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public class DashboardRenderer : IDashboardRenderer + { + public async Task RenderAsync(IViewComponentHelper componentHelper, object args = null) + { + return await componentHelper.InvokeAsync(typeof(DashboardViewComponent), args ?? new object()); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardWidgetConfiguration.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardWidgetConfiguration.cs new file mode 100644 index 0000000000..74a20bc877 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/DashboardWidgetConfiguration.cs @@ -0,0 +1,28 @@ +using JetBrains.Annotations; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public class DashboardWidgetConfiguration + { + [NotNull] + public string WidgetName { get; } + + [CanBeNull] + public WidgetDimensions Dimensions { get; set; } + + [CanBeNull] + public WidgetLocation Location { get; set; } + + public DashboardWidgetConfiguration( + [NotNull] string widgetName, + [CanBeNull] WidgetDimensions dimensions = null, + [CanBeNull] WidgetLocation location = null + ) + { + WidgetName = Check.NotNullOrWhiteSpace(widgetName, nameof(widgetName)); + Dimensions = dimensions; + Location = location; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IDashboardRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IDashboardRenderer.cs new file mode 100644 index 0000000000..64e149696f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/IDashboardRenderer.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public interface IDashboardRenderer : ITransientDependency + { + Task RenderAsync(IViewComponentHelper componentHelper, object args = null); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/WidgetLocation.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/WidgetLocation.cs new file mode 100644 index 0000000000..7ce21a342e --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/WidgetLocation.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.AspNetCore.Mvc.UI.Dashboards +{ + public class WidgetLocation + { + public int X { get; set; } + + public int Y { get; set; } + + public WidgetLocation(int x, int y) + { + X = x; + Y = y; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/_ViewImports.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/_ViewImports.cshtml new file mode 100644 index 0000000000..225780c2c2 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards/Volo/Abp/AspNetCore/Mvc/UI/Dashboards/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling \ No newline at end of file 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 new file mode 100644 index 0000000000..7151dc73f3 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json @@ -0,0 +1,11 @@ +{ + "culture": "cs", + "texts": { + "GivenTenantIsNotAvailable": "Zvolený tenant není dostupný: {0}", + "Tenant": "Tenant", + "Switch": "změnit", + "Name": "Název", + "SwitchTenantHint": "Nechte pole prázdné ke změně na hostitele.", + "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/AbpAspNetCoreMvcUiPackagesModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/AbpAspNetCoreMvcUiPackagesModule.cs index bab248f669..ec82d4b3d6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/AbpAspNetCoreMvcUiPackagesModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/AbpAspNetCoreMvcUiPackagesModule.cs @@ -1,5 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.Mvc.UI.Packages diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml index 4864f2dafe..1cd530d021 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml @@ -28,9 +28,12 @@ @(await Component.InvokeAsync()) - +
@(await Component.InvokeAsync()) +
+ @RenderSection("content_toolbar", false) +
@RenderBody()
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js index c9d11b5fac..b3fa839d10 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js @@ -63,14 +63,14 @@ }); }; - var toCamelCase = function(str) { + var toCamelCase = function (str) { var regexs = [ /(^[A-Z])/, // first char of string /((\.)[A-Z])/ // first char after a dot (.) ]; regexs.forEach( - function(regex) { + function (regex) { var infLoopAvoider = 0; while (regex.test(str)) { @@ -87,7 +87,7 @@ return str; }; - $.fn.serializeFormToObject = function (camelCase = true) { + $.fn.serializeFormToObject = function (camelCase) { //serialize to array var data = $(this).serializeArray(); @@ -108,14 +108,26 @@ //map to object var obj = {}; - if (camelCase) { + if (camelCase !== undefined ? camelCase : true) { data.forEach(function (d) { d.name = toCamelCase(d.name); }); } data.map(function (x) { - obj[x.name] = x.value; + //TODO: improve mapping. it only supports one level deep object. + var names = x.name.split("."); + if (names.length === 1 && !obj[names[0]]) { + obj[names[0]] = x.value; + } + else if (names.length === 2) { + if (!obj[names[0]]) { + obj[names[0]] = {}; + } + if (!obj[names[0]][names[1]]) { + obj[names[0]][names[1]] = x.value; + } + } }); return obj; @@ -152,7 +164,7 @@ } }); - $(this).on('abp-ajax-success',function () { + $(this).on('abp-ajax-success', function () { formSaved = true; }); }; 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 new file mode 100644 index 0000000000..26634c6270 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo.Abp.AspNetCore.Mvc.UI.Widgets.csproj @@ -0,0 +1,16 @@ + + + + + + netstandard2.0 + Volo.Abp.AspNetCore.Mvc.UI.Widgets + Volo.Abp.AspNetCore.Mvc.UI.Widgets + + + + + + + + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs new file mode 100644 index 0000000000..cdf1db75cd --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/AbpAspNetCoreMvcUiWidgetsModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets +{ + [DependsOn( + typeof(AbpAspNetCoreMvcUiBootstrapModule) + )] + public class AbpAspNetCoreMvcUiWidgetsModule : AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/IWidgetRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/IWidgetRenderer.cs new file mode 100644 index 0000000000..ab3c4ee09f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/IWidgetRenderer.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets +{ + public interface IWidgetRenderer : ITransientDependency + { + Task RenderAsync(IViewComponentHelper componentHelper, string widgetName, object args = null); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetDefinition.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetDefinition.cs new file mode 100644 index 0000000000..b1b404215e --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetDefinition.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.Localization; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets +{ + public class WidgetDefinition + { + /// + /// Unique name of the widget. + /// + [NotNull] + public string Name { get; } + + /// + /// Display name of the widget. + /// + [NotNull] + public ILocalizableString DisplayName + { + get => _displayName; + set => _displayName = Check.NotNull(value, nameof(value)); + } + private ILocalizableString _displayName; + + [NotNull] + public Type ViewComponentType { get; } + + [CanBeNull] + public WidgetDimensions DefaultDimensions { get; set; } + + public List RequiredPermissions { get; set; } + + public WidgetDefinition( + [NotNull] string name, + [CanBeNull] ILocalizableString displayName, + [NotNull] Type viewComponentType) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + ViewComponentType = Check.NotNull(viewComponentType, nameof(viewComponentType)); + DisplayName = displayName ?? new FixedLocalizableString(name); + RequiredPermissions = new List(); + } + + public WidgetDefinition AddRequiredPermission(string permissionName) + { + RequiredPermissions.Add(permissionName); + return this; + } + + public WidgetDefinition SetDefaultDimension(int width, int height) + { + DefaultDimensions = new WidgetDimensions(width, height); + return this; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetDimensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetDimensions.cs new file mode 100644 index 0000000000..eb24d208b4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetDimensions.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets +{ + public class WidgetDimensions + { + public int Width { get; } + + public int Height { get; } + + public WidgetDimensions(int width, int height) + { + Width = width; + Height = height; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetOptions.cs new file mode 100644 index 0000000000..7a4fa3487f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetOptions.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets +{ + public class WidgetOptions + { + public List Widgets { get; } + + public WidgetOptions() + { + Widgets = new List(); + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetRenderer.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetRenderer.cs new file mode 100644 index 0000000000..821e53e853 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets/Volo/Abp/AspNetCore/Mvc/UI/Widgets/WidgetRenderer.cs @@ -0,0 +1,25 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Widgets +{ + public class WidgetRenderer : IWidgetRenderer + { + private readonly WidgetOptions _widgetOptions; + + public WidgetRenderer(IOptions widgetOptions) + { + _widgetOptions = widgetOptions.Value; + } + + public async Task RenderAsync(IViewComponentHelper componentHelper, string widgetName, object args = null) + { + var widget = _widgetOptions.Widgets.Single(w=>w.Name.Equals(widgetName)); + + return await componentHelper.InvokeAsync(widget.ViewComponentType, args ?? new object()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs index 8fd16f5864..62258329ad 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -21,19 +22,43 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages { public abstract class AbpPageModel : PageModel { - public IClock Clock { get; set; } + public IServiceProvider ServiceProvider { get; set; } + protected readonly object ServiceProviderLock = new object(); + protected TService LazyGetRequiredService(ref TService reference) + { + if (reference == null) + { + lock (ServiceProviderLock) + { + if (reference == null) + { + reference = ServiceProvider.GetRequiredService(); + } + } + } + + return reference; + } + + public IClock Clock => LazyGetRequiredService(ref _clock); + private IClock _clock; public AlertList Alerts => AlertManager.Alerts; - public IUnitOfWorkManager UnitOfWorkManager { get; set; } + public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager); + private IUnitOfWorkManager _unitOfWorkManager; - public IObjectMapper ObjectMapper { get; set; } + public IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper); + private IObjectMapper _objectMapper; - public IGuidGenerator GuidGenerator { get; set; } + public IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator); + private IGuidGenerator _guidGenerator; - public ILoggerFactory LoggerFactory { get; set; } + public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); + private ILoggerFactory _loggerFactory; - public IStringLocalizerFactory StringLocalizerFactory { get; set; } + public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); + private IStringLocalizerFactory _stringLocalizerFactory; public IStringLocalizer L { @@ -56,17 +81,23 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages protected Type LocalizationResourceType { get; set; } - public ICurrentUser CurrentUser { get; set; } + public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); + private ICurrentUser _currentUser; - public ICurrentTenant CurrentTenant { get; set; } + public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); + private ICurrentTenant _currentTenant; - public ISettingProvider SettingProvider { get; set; } + public ISettingProvider SettingProvider => LazyGetRequiredService(ref _settingProvider); + private ISettingProvider _settingProvider; - public IModelStateValidator ModelValidator { get; set; } + public IModelStateValidator ModelValidator => LazyGetRequiredService(ref _modelValidator); + private IModelStateValidator _modelValidator; - public IAuthorizationService AuthorizationService { get; set; } + public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); + private IAuthorizationService _authorizationService; - public IAlertManager AlertManager { get; set; } + public IAlertManager AlertManager => LazyGetRequiredService(ref _alertManager); + private IAlertManager _alertManager; protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningExtensions.cs new file mode 100644 index 0000000000..c239a42a45 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningExtensions.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.Versioning; +using Volo.Abp.ApiVersioning; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Conventions; +using Volo.Abp.AspNetCore.Mvc.Versioning; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AbpApiVersioningExtensions + { + public static IServiceCollection AddAbpApiVersioning(this IServiceCollection services, Action setupAction) + { + services.AddTransient(); + services.AddTransient(); + + services.AddApiVersioning(setupAction); + + return services; + } + + public static void ConfigureAbp(this ApiVersioningOptions options, AbpAspNetCoreMvcOptions mvcOptions) + { + foreach (var setting in mvcOptions.ConventionalControllers.ConventionalControllerSettings) + { + if (setting.ApiVersionConfigurer == null) + { + ConfigureApiVersionsByConvention(options, setting); + } + else + { + setting.ApiVersionConfigurer.Invoke(options); + } + } + } + + private static void ConfigureApiVersionsByConvention(ApiVersioningOptions options, ConventionalControllerSetting setting) + { + foreach (var controllerType in setting.ControllerTypes) + { + var controllerBuilder = options.Conventions.Controller(controllerType); + + if (setting.ApiVersions.Any()) + { + foreach (var apiVersion in setting.ApiVersions) + { + controllerBuilder.HasApiVersion(apiVersion); + } + } + else + { + if (!controllerType.IsDefined(typeof(ApiVersionAttribute), true)) + { + controllerBuilder.IsApiVersionNeutral(); + } + } + } + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs deleted file mode 100644 index 13148a4bc6..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/Extensions/DependencyInjection/AbpApiVersioningOptionsExtensions.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Linq; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Versioning; -using Volo.Abp.ApiVersioning; -using Volo.Abp.AspNetCore.Mvc; -using Volo.Abp.AspNetCore.Mvc.Conventions; -using Volo.Abp.AspNetCore.Mvc.Versioning; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class AbpApiVersioningOptionsExtensions - { - public static void ConfigureAbp(this ApiVersioningOptions options, IServiceCollection services) - { - //TODO: Use new builder will be released with Api Versioning 2.1 instead of reflection! - - services.AddTransient(); - - services.Configure(op => - { - //TODO: Configuring api version should be done directly inside ConfigureAbp, - //TODO: not in a callback that will be called by MVC later! For that, we immediately need to controllerAssemblySettings - - foreach (var setting in op.ConventionalControllers.ConventionalControllerSettings) - { - if (setting.ApiVersionConfigurer == null) - { - ConfigureApiVersionsByConvention(options, setting); - } - else - { - setting.ApiVersionConfigurer.Invoke(options); - } - } - }); - } - - private static void ConfigureApiVersionsByConvention(ApiVersioningOptions options, ConventionalControllerSetting setting) - { - foreach (var controllerType in setting.ControllerTypes) - { - var controllerBuilder = options.Conventions.Controller(controllerType); - - if (setting.ApiVersions.Any()) - { - foreach (var apiVersion in setting.ApiVersions) - { - controllerBuilder.HasApiVersion(apiVersion); - } - } - else - { - if (!controllerType.IsDefined(typeof(ApiVersionAttribute), true)) - { - controllerBuilder.IsApiVersionNeutral(); - } - } - } - } - } -} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj index e99572852e..4ee308e591 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj @@ -23,7 +23,7 @@ - +
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 9729476f2f..fa317d7211 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 @@ -1,34 +1,36 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Modularity; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.ViewComponents; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc.Json; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.VirtualFileSystem; +using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Modeling; using Volo.Abp.Localization; +using Volo.Abp.Modularity; using Volo.Abp.UI; namespace Volo.Abp.AspNetCore.Mvc { [DependsOn( - typeof(AbpAspNetCoreModule), - typeof(AbpLocalizationModule), - typeof(AbpApiVersioningAbstractionsModule), + typeof(AbpAspNetCoreModule), + typeof(AbpLocalizationModule), + typeof(AbpApiVersioningAbstractionsModule), typeof(AbpAspNetCoreMvcContractsModule), typeof(AbpUiModule) )] @@ -109,6 +111,11 @@ namespace Volo.Abp.AspNetCore.Mvc { mvcOptions.AddAbp(context.Services); }); + + Configure(jsonOptions => + { + jsonOptions.SerializerSettings.ContractResolver = new AbpMvcJsonContractResolver(context.Services); + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs index a5d206cf55..943d0d6e3f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Aspects; using Volo.Abp.AspNetCore.Mvc.Validation; using Volo.Abp.Features; using Volo.Abp.Guids; +using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Timing; @@ -18,27 +21,72 @@ namespace Volo.Abp.AspNetCore.Mvc { public abstract class AbpController : Controller, IAvoidDuplicateCrossCuttingConcerns { - public IUnitOfWorkManager UnitOfWorkManager { get; set; } + public IServiceProvider ServiceProvider { get; set; } + protected readonly object ServiceProviderLock = new object(); + protected TService LazyGetRequiredService(ref TService reference) + { + if (reference == null) + { + lock (ServiceProviderLock) + { + if (reference == null) + { + reference = ServiceProvider.GetRequiredService(); + } + } + } + + return reference; + } - public IObjectMapper ObjectMapper { get; set; } + public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager); + private IUnitOfWorkManager _unitOfWorkManager; - public IGuidGenerator GuidGenerator { get; set; } + public IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper); + private IObjectMapper _objectMapper; - public ILoggerFactory LoggerFactory { get; set; } + public IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator); + private IGuidGenerator _guidGenerator; - public ICurrentUser CurrentUser { get; set; } + public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); + private ILoggerFactory _loggerFactory; - public ICurrentTenant CurrentTenant { get; set; } + public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); + private ICurrentUser _currentUser; - public IAuthorizationService AuthorizationService { get; set; } + public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); + private ICurrentTenant _currentTenant; + + public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); + private IAuthorizationService _authorizationService; protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; - public IClock Clock { get; set; } + public IClock Clock => LazyGetRequiredService(ref _clock); + private IClock _clock; + + public IModelStateValidator ModelValidator => LazyGetRequiredService(ref _modelValidator); + private IModelStateValidator _modelValidator; - public IModelStateValidator ModelValidator { get; set; } + public IFeatureChecker FeatureChecker => LazyGetRequiredService(ref _featureChecker); + private IFeatureChecker _featureChecker; - public IFeatureChecker FeatureChecker { get; set; } + public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); + private IStringLocalizerFactory _stringLocalizerFactory; + + public IStringLocalizer L => _localizer ?? (_localizer = StringLocalizerFactory.Create(LocalizationResource)); + private IStringLocalizer _localizer; + + protected Type LocalizationResource + { + get => _localizationResource; + set + { + _localizationResource = value; + _localizer = null; + } + } + private Type _localizationResource = typeof(DefaultResource); public List AppliedCrossCuttingConcerns { get; } = new List(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs index 2274f0a9b6..d9de135e7f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpDataAnnotationAutoLocalizationMetadataDetailsProvider.cs @@ -39,6 +39,11 @@ namespace Volo.Abp.AspNetCore.Mvc return; } + if (context.Key.Name.IsNullOrWhiteSpace()) + { + return; + } + if (_localizationOptions.Value.Value.DataAnnotationLocalizerProvider == null) { return; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 1a9646d13f..3eb7d18e39 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -85,7 +85,7 @@ namespace Volo.Abp.AspNetCore.Mvc method, apiDescription.RelativePath, apiDescription.HttpMethod, - setting?.ApiVersions.Select(v => v.ToString()).ToList() ?? new List() //TODO: Also get from ApiVersion attributes if available..? + GetSupportedVersions(controllerType, method, setting) )); AddParameterDescriptionsToModel(actionModel, method, apiDescription); @@ -126,6 +126,29 @@ namespace Volo.Abp.AspNetCore.Mvc return methodNameBuilder.ToString(); } + private static List GetSupportedVersions(Type controllerType, MethodInfo method, ConventionalControllerSetting setting) + { + var supportedVersions = new List(); + + var mapToAttributes = method.GetCustomAttributes().ToArray(); + if (mapToAttributes.Any()) + { + supportedVersions.AddRange( + mapToAttributes.SelectMany(a => a.Versions) + ); + } + else + { + supportedVersions.AddRange( + controllerType.GetCustomAttributes().SelectMany(a => a.Versions) + ); + + setting?.ApiVersions.ForEach(supportedVersions.Add); + } + + return supportedVersions.Select(v => v.ToString()).Distinct().ToList(); + } + private void AddParameterDescriptionsToModel(ActionApiDescriptionModel actionModel, MethodInfo method, ApiDescription apiDescription) { if (!apiDescription.ParameterDescriptions.Any()) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalApiControllerSpecification.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalApiControllerSpecification.cs new file mode 100644 index 0000000000..59b2184a53 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpConventionalApiControllerSpecification.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.AspNetCore.Mvc.Conventions +{ + public class AbpConventionalApiControllerSpecification : IApiControllerSpecification + { + private readonly AbpAspNetCoreMvcOptions _options; + + public AbpConventionalApiControllerSpecification(IOptions options) + { + _options = options.Value; + } + + public bool IsSatisfiedBy(ControllerModel controller) + { + var configuration = _options + .ConventionalControllers + .ConventionalControllerSettings + .GetSettingOrNull(controller.ControllerType.AsType()); + + return configuration != null; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs new file mode 100644 index 0000000000..695212f563 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Json/AbpMvcJsonContractResolver.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Reflection; +using Volo.Abp.Json.Newtonsoft; +using Volo.Abp.Reflection; +using Volo.Abp.Timing; + +namespace Volo.Abp.AspNetCore.Mvc.Json +{ + public class AbpMvcJsonContractResolver : DefaultContractResolver + { + private readonly Lazy _dateTimeConverter; + + public AbpMvcJsonContractResolver(IServiceCollection services) + { + _dateTimeConverter = services.GetServiceLazy(); + + NamingStrategy = new CamelCaseNamingStrategy(); + } + + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + JsonProperty property = base.CreateProperty(member, memberSerialization); + + ModifyProperty(member, property); + + return property; + } + + protected virtual void ModifyProperty(MemberInfo member, JsonProperty property) + { + if (property.PropertyType != typeof(DateTime) && property.PropertyType != typeof(DateTime?)) + { + return; + } + + if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(member) == null) + { + property.Converter = _dateTimeConverter.Value; + } + + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj b/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj index 37c728f175..fb85cbb3ec 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj +++ b/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj @@ -29,7 +29,7 @@ - + diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs index 502fbda5bb..f36a7f4860 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingMiddleware.cs @@ -36,7 +36,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling // We can't do anything if the response has already started, just abort. if (httpContext.Response.HasStarted) { - _logger.LogWarning("An exception occured, but response has already started!"); + _logger.LogWarning("An exception occurred, but response has already started!"); throw; } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs index 73d189f1d3..c4265c7ced 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs @@ -22,7 +22,11 @@ namespace Volo.Abp.AspNetCore.VirtualFileSystem ".css", ".png", ".jpg", - ".jpeg" + ".jpeg", + ".woff", + ".woff2", + ".tff", + ".otf" }; } } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs index 616190af64..da406fa490 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs @@ -9,6 +9,12 @@ namespace Volo.Abp.Auditing { //TODO: Consider to add an option to disable auditing for application service methods? + /// + /// If this value is true, auditing will not throw an exceptions and it will log it when an error occurred while saving AuditLog. + /// Default: true. + /// + public bool HideErrors { get; set; } + /// /// Default: true. /// @@ -36,11 +42,12 @@ namespace Volo.Abp.Auditing /// Default: false. /// public bool IsEnabledForGetRequests { get; set; } - + public AbpAuditingOptions() { IsEnabled = true; IsEnabledForAnonymousUsers = true; + HideErrors = true; Contributors = new List(); diff --git a/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj b/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj index ae384414a3..029669c348 100644 --- a/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj +++ b/framework/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj @@ -18,7 +18,7 @@
- + \ No newline at end of file diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProviderFactory.cs b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProviderFactory.cs index 8e0d4f574d..21a7ca331d 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProviderFactory.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/AutofacServiceProviderFactory.cs @@ -38,7 +38,7 @@ namespace Autofac.Extensions.DependencyInjection /// /// Initializes a new instance of the class. /// - /// Action on a that adds component registrations to the conatiner. + /// Action on a that adds component registrations to the container. public AutofacServiceProviderFactory(Action configurationAction = null) { _configurationAction = configurationAction ?? (builder => { }); diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/ServiceCollectionExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/ServiceCollectionExtensions.cs index 5552cda7b2..1195cb9fb1 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/ServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Extensions/DependencyInjection/ServiceCollectionExtensions.cs @@ -37,7 +37,7 @@ namespace Autofac.Extensions.DependencyInjection /// Adds the to the service collection. /// /// The service collection to add the factory to. - /// Action on a that adds component registrations to the conatiner. + /// Action on a that adds component registrations to the container. /// The service collection. public static IServiceCollection AddAutofac(this IServiceCollection services, Action configurationAction = null) { diff --git a/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj b/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj index c8737bbfeb..3e68488105 100644 --- a/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj +++ b/framework/src/Volo.Abp.Autofac/Volo.Abp.Autofac.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobInfo.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobInfo.cs index 7e1154ec7e..6241bc81f9 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobInfo.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/BackgroundJobInfo.cs @@ -42,7 +42,7 @@ namespace Volo.Abp.BackgroundJobs public virtual DateTime? LastTryTime { get; set; } /// - /// This is true if this job is continously failed and will not be executed again. + /// This is true if this job is continuously failed and will not be executed again. /// public virtual bool IsAbandoned { get; set; } diff --git a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/InMemoryBackgroundJobStore.cs b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/InMemoryBackgroundJobStore.cs index 2e3e8879ec..1dc6d7c700 100644 --- a/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/InMemoryBackgroundJobStore.cs +++ b/framework/src/Volo.Abp.BackgroundJobs/Volo/Abp/BackgroundJobs/InMemoryBackgroundJobStore.cs @@ -23,19 +23,19 @@ namespace Volo.Abp.BackgroundJobs _jobs = new ConcurrentDictionary(); } - public Task FindAsync(Guid jobId) + public virtual Task FindAsync(Guid jobId) { return Task.FromResult(_jobs.GetOrDefault(jobId)); } - public Task InsertAsync(BackgroundJobInfo jobInfo) + public virtual Task InsertAsync(BackgroundJobInfo jobInfo) { _jobs[jobInfo.Id] = jobInfo; return Task.FromResult(0); } - public Task> GetWaitingJobsAsync(int maxResultCount) + public virtual Task> GetWaitingJobsAsync(int maxResultCount) { var waitingJobs = _jobs.Values .Where(t => !t.IsAbandoned && t.NextTryTime <= Clock.Now) @@ -48,14 +48,14 @@ namespace Volo.Abp.BackgroundJobs return Task.FromResult(waitingJobs); } - public Task DeleteAsync(Guid jobId) + public virtual Task DeleteAsync(Guid jobId) { _jobs.TryRemove(jobId, out _); return Task.FromResult(0); } - public Task UpdateAsync(BackgroundJobInfo jobInfo) + public virtual Task UpdateAsync(BackgroundJobInfo jobInfo) { if (jobInfo.IsAbandoned) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj b/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj new file mode 100644 index 0000000000..708c296880 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj @@ -0,0 +1,36 @@ + + + + + + netstandard2.0 + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + + + + + + + + + + C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.SqlXml.dll + + + + \ 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 new file mode 100644 index 0000000000..eb22185b73 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs @@ -0,0 +1,33 @@ +using System.Text; +using Volo.Abp.Cli.Commands; +using Volo.Abp.Domain; +using Volo.Abp.IdentityModel; +using Volo.Abp.Json; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Cli +{ + [DependsOn( + typeof(AbpDddDomainModule), + typeof(AbpJsonModule), + typeof(AbpIdentityModelModule) + )] + public class AbpCliCoreModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + Configure(options => + { + options.Commands["help"] = typeof(HelpCommand); + options.Commands["new"] = typeof(NewCommand); + options.Commands["update"] = typeof(UpdateCommand); + options.Commands["add-package"] = typeof(AddPackageCommand); + options.Commands["add-module"] = typeof(AddModuleCommand); + options.Commands["login"] = typeof(LoginCommand); + options.Commands["logout"] = typeof(LogoutCommand); + }); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgs.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgs.cs new file mode 100644 index 0000000000..5e1191c385 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgs.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Text; +using JetBrains.Annotations; + +namespace Volo.Abp.Cli.Args +{ + public class CommandLineArgs + { + [CanBeNull] + public string Command { get; } + + [CanBeNull] + public string Target { get; } + + [NotNull] + public CommandLineOptions Options { get; } + + public CommandLineArgs([CanBeNull] string command = null, [CanBeNull] string target = null) + { + Command = command; + Target = target; + Options = new CommandLineOptions(); + } + + public static CommandLineArgs Empty() + { + return new CommandLineArgs(); + } + + public bool IsCommand(string command) + { + return string.Equals(Command, command, StringComparison.OrdinalIgnoreCase); + } + + public override string ToString() + { + var sb = new StringBuilder(); + + if (Command != null) + { + sb.AppendLine($"Command: {Command}"); + } + + if (Target != null) + { + sb.AppendLine($"Target: {Target}"); + } + + if (Options.Any()) + { + sb.AppendLine("Options:"); + foreach (var option in Options) + { + sb.AppendLine($" - {option.Key} = {option.Value}"); + } + } + + if (sb.Length <= 0) + { + sb.Append(""); + } + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs new file mode 100644 index 0000000000..1dd29e5df7 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Args +{ + public class CommandLineArgumentParser : ICommandLineArgumentParser, ITransientDependency + { + public CommandLineArgs Parse(string[] args) + { + if (args.IsNullOrEmpty()) + { + return CommandLineArgs.Empty(); + } + + var argumentList = args.ToList(); + + //Command + + var command = argumentList[0]; + argumentList.RemoveAt(0); + + if (!argumentList.Any()) + { + return new CommandLineArgs(command); + } + + //Target + + var target = argumentList[0]; + if (target.StartsWith("-")) + { + target = null; + } + else + { + argumentList.RemoveAt(0); + } + + if (!argumentList.Any()) + { + return new CommandLineArgs(command, target); + } + + //Options + + var commandLineArgs = new CommandLineArgs(command, target); + + while (argumentList.Any()) + { + var optionName = ParseOptionName(argumentList[0]); + argumentList.RemoveAt(0); + + if (!argumentList.Any()) + { + commandLineArgs.Options[optionName] = null; + break; + } + + if (IsOptionName(argumentList[0])) + { + commandLineArgs.Options[optionName] = null; + continue; + } + + commandLineArgs.Options[optionName] = argumentList[0]; + argumentList.RemoveAt(0); + } + + return commandLineArgs; + } + + private static bool IsOptionName(string argument) + { + return argument.StartsWith("-") || argument.StartsWith("--"); + } + + private static string ParseOptionName(string argument) + { + if (argument.StartsWith("--")) + { + if (argument.Length <= 2) + { + throw new ArgumentException("Should specify an option name after '--' prefix!"); + } + + return argument.RemovePreFix("--"); + } + + if (argument.StartsWith("-")) + { + if (argument.Length <= 1) + { + throw new ArgumentException("Should specify an option name after '--' prefix!"); + } + + return argument.RemovePreFix("-"); + } + + throw new ArgumentException("Option names should start with '-' or '--'."); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineOptions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineOptions.cs new file mode 100644 index 0000000000..3e72d6e434 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineOptions.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Cli.Args +{ + public class CommandLineOptions : Dictionary + { + [CanBeNull] + public string GetOrNull([NotNull] string name, params string[] alternativeNames) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + var value = this.GetOrDefault(name); + if (!value.IsNullOrWhiteSpace()) + { + return value; + } + + if (!alternativeNames.IsNullOrEmpty()) + { + foreach (var alternativeName in alternativeNames) + { + value = this.GetOrDefault(alternativeName); + if (!value.IsNullOrWhiteSpace()) + { + return value; + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs new file mode 100644 index 0000000000..c467541210 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Cli.Args +{ + public interface ICommandLineArgumentParser + { + CommandLineArgs Parse(string[] args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs new file mode 100644 index 0000000000..fa94eb00bb --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs @@ -0,0 +1,43 @@ +using System.IO; +using System.Text; +using System.Threading.Tasks; +using IdentityModel; +using Volo.Abp.DependencyInjection; +using Volo.Abp.IdentityModel; +using Volo.Abp.IO; + +namespace Volo.Abp.Cli.Auth +{ + public class AuthService : ITransientDependency + { + protected IIdentityModelAuthenticationService AuthenticationService { get; } + + public AuthService(IIdentityModelAuthenticationService authenticationService) + { + AuthenticationService = authenticationService; + } + + public async Task LoginAsync(string userName, string password) + { + var accessToken = await AuthenticationService.GetAccessTokenAsync( + new IdentityClientConfiguration( + CliUrls.AccountAbpIo, + "role email abpio_www abpio_commercial", + "abp-cli", + "1q2w3e*", + OidcConstants.GrantTypes.Password, + userName, + password + ) + ); + + File.WriteAllText(CliPaths.AccessToken, accessToken, Encoding.UTF8); + } + + public Task LogoutAsync() + { + FileHelper.DeleteIfExists(CliPaths.AccessToken); + return Task.CompletedTask; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliOptions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliOptions.cs new file mode 100644 index 0000000000..3885314e70 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliOptions.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Cli +{ + public class CliOptions + { + public Dictionary Commands { get; } + + public CliOptions() + { + Commands = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs new file mode 100644 index 0000000000..eb6339b9da --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs @@ -0,0 +1,15 @@ +using System; +using System.IO; + +namespace Volo.Abp.Cli +{ + public static class CliPaths + { + public static string TemplateCache => Path.Combine(AbpRootPath, "templates"); //TODO: Move somewhere else? + public static string Log => Path.Combine(AbpRootPath, "cli", "logs"); + public static string Root => Path.Combine(AbpRootPath, "cli"); + public static string AccessToken => Path.Combine(AbpRootPath, "cli", "access-token.bin"); + + private static readonly string AbpRootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".abp"); + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs new file mode 100644 index 0000000000..3c2a88094f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs @@ -0,0 +1,170 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.Commands; +using Volo.Abp.Cli.NuGet; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli +{ + public class CliService : ITransientDependency + { + public ILogger Logger { get; set; } + + protected ICommandLineArgumentParser CommandLineArgumentParser { get; } + protected ICommandSelector CommandSelector { get; } + protected IHybridServiceScopeFactory ServiceScopeFactory { get; } + protected NuGetService NuGetService { get; } + + public CliService( + ICommandLineArgumentParser commandLineArgumentParser, + ICommandSelector commandSelector, + IHybridServiceScopeFactory serviceScopeFactory, + NuGetService nugetService) + { + CommandLineArgumentParser = commandLineArgumentParser; + CommandSelector = commandSelector; + ServiceScopeFactory = serviceScopeFactory; + NuGetService = nugetService; + + Logger = NullLogger.Instance; + } + + public async Task RunAsync(string[] args) + { + Logger.LogInformation($"ABP CLI, version {GetCliVersion()}."); + Logger.LogInformation("https://abp.io"); + + await CheckForNewVersion(); + + CheckDependencies(); + + var commandLineArgs = CommandLineArgumentParser.Parse(args); + var commandType = CommandSelector.Select(commandLineArgs); + + using (var scope = ServiceScopeFactory.CreateScope()) + { + var command = (IConsoleCommand)scope.ServiceProvider.GetRequiredService(commandType); + + try + { + await command.ExecuteAsync(commandLineArgs); + } + catch (CliUsageException usageException) + { + Logger.LogWarning(usageException.Message); + } + catch (Exception ex) + { + Logger.LogException(ex); + } + } + } + + private void CheckDependencies() + { + var installedNpmPackages = GetInstalledNpmPackages(); + + if (!installedNpmPackages.Contains(" yarn@")) + { + InstallYarn(); + } + if (!installedNpmPackages.Contains(" gulp@")) + { + InstallGulp(); + } + } + + private string GetInstalledNpmPackages() + { + var output = ""; + + using (var process = new Process()) + { + process.StartInfo = new ProcessStartInfo(CmdHelper.GetFileName()) + { + Arguments = CmdHelper.GetArguments("npm list -g --depth 0"), + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true + }; + process.Start(); + + using (var stdOut = process.StandardOutput) + { + using (var stdErr = process.StandardError) + { + output = stdOut.ReadToEnd(); + output += stdErr.ReadToEnd(); + } + } + } + + return output; + } + + private void InstallYarn() + { + Logger.LogInformation("Installing yarn..."); + CmdHelper.RunCmd("npm install yarn -g"); + } + + private void InstallGulp() + { + Logger.LogInformation("Installing gulp..."); + CmdHelper.RunCmd("npm install gulp -g"); + } + + private async Task CheckForNewVersion() + { + try + { + var currentVersion = GetCliVersion(); + var latestVersion = await NuGetService.GetLatestVersionOrNullAsync("Volo.Abp.Cli"); + + if (!latestVersion.IsNullOrEmpty() && currentVersion != latestVersion) + { + Logger.LogInformation(""); + Logger.LogWarning("ABP CLI has a newer version (" + latestVersion + "). Please update to get the latest features and fixes."); + Logger.LogWarning(""); + Logger.LogWarning("Update Command: "); + Logger.LogWarning(" dotnet tool update -g Volo.Abp.Cli"); + Logger.LogWarning(""); + } + } + catch (Exception e) + { + Logger.LogWarning("Could not get the latest version infom from NuGet.org:"); + Logger.LogWarning(e.Message); + } + } + + private static string GetCliVersion() + { + var version = typeof(CliService) + .Assembly + .GetFileVersion(); + + /* Assembly versions are like "2.4.0.0", but NuGet removes the last "0" here, + * like "2.4.0". + * So, we need to remove it from the assembly version to match to the NuGet version. + */ + + if (version.Split('.').Length == 4) + { + version = version.RemovePostFix(".0"); + } + + return version; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs new file mode 100644 index 0000000000..3c034d5702 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUrls.cs @@ -0,0 +1,13 @@ +namespace Volo.Abp.Cli +{ + public static class CliUrls + { +#if DEBUG + public const string WwwAbpIo = "https://localhost:44328/"; + public const string AccountAbpIo = "https://localhost:44333/"; +#else + public const string WwwAbpIo = "https://abp.io/"; + public const string AccountAbpIo = "https://account.abp.io/"; +#endif + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUsageException.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUsageException.cs new file mode 100644 index 0000000000..5ce2b7a1b3 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliUsageException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Volo.Abp.Cli +{ + public class CliUsageException : Exception + { + public CliUsageException(string message) + : base(message) + { + + } + + public CliUsageException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddCommandHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddCommandHelper.cs new file mode 100644 index 0000000000..d1b5c9d716 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddCommandHelper.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; + +namespace Volo.Abp.Cli.Commands +{ + public static class AddCommandHelper + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs new file mode 100644 index 0000000000..a43a910340 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddModuleCommand.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; +using System.Text; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System.Threading.Tasks; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.ProjectModification; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class AddModuleCommand : IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + + protected SolutionModuleAdder SolutionModuleAdder { get; } + + public AddModuleCommand(SolutionModuleAdder solutionModuleAdder) + { + SolutionModuleAdder = solutionModuleAdder; + Logger = NullLogger.Instance; + } + + public async Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + if (commandLineArgs.Target == null) + { + throw new CliUsageException("Module name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); + } + + var skipDbMigrations = Convert.ToBoolean( + commandLineArgs.Options.GetOrNull(Options.DbMigrations.Skip) ?? "false"); + + await SolutionModuleAdder.AddAsync( + GetSolutionFile(commandLineArgs), + commandLineArgs.Target, + skipDbMigrations + ); + } + + public Task GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("'add-module' command is used to add a multi-package ABP module to a solution."); + sb.AppendLine("It should be used in a folder containing a .sln file."); + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(" abp add-module [-s|--solution]"); + sb.AppendLine(""); + sb.AppendLine("Options:"); + sb.AppendLine(" -s|--solution Specify the solution file explicitly."); + sb.AppendLine(" --skip-db-migrations Specify if a new migration will be added or not."); + sb.AppendLine(""); + sb.AppendLine("Examples:"); + sb.AppendLine(" abp add-module Volo.Blogging Adds the module to the current soluton."); + sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore Adds the module to the given soluton."); + sb.AppendLine(" abp add-module Volo.Blogging -s Acme.BookStore --skip-db-migrations false Adds the module to the given soluton but doesn't create a database migration."); + sb.AppendLine(""); + + return Task.FromResult(sb.ToString()); + } + + public Task GetShortDescriptionAsync() + { + return Task.FromResult("Adds a multi-package module to a solution by finding all packages of the module, " + + "finding related projects in the solution and adding each package to the" + + " corresponding project in the solution."); + } + + protected virtual string GetSolutionFile(CommandLineArgs commandLineArgs) + { + var providedSolutionFile = PathHelper.NormalizePath( + commandLineArgs.Options.GetOrNull( + AddPackageCommand.Options.Project.Short, + AddPackageCommand.Options.Project.Long + ) + ); + + if (!providedSolutionFile.IsNullOrWhiteSpace()) + { + return providedSolutionFile; + } + + var foundSolutionFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.sln"); + if (foundSolutionFiles.Length == 1) + { + return foundSolutionFiles[0]; + } + + if (foundSolutionFiles.Length == 0) + { + throw new CliUsageException("'abp add-module' command should be used inside a folder contaning a .sln file!"); + } + + //foundSolutionFiles.Length > 1 + + var sb = new StringBuilder("There are multiple solution (.sln) files in the current directory. Please specify one of the files below:"); + + foreach (var foundSolutionFile in foundSolutionFiles) + { + sb.AppendLine("* " + foundSolutionFile); + } + + sb.AppendLine("Example:"); + sb.AppendLine($"abp add-module {commandLineArgs.Target} -p {foundSolutionFiles[0]}"); + + throw new CliUsageException(sb.ToString()); + } + + public static class Options + { + public static class Solution + { + public const string Short = "s"; + public const string Long = "solution"; + } + + public static class DbMigrations + { + public const string Skip = "skip-db-migrations"; + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs new file mode 100644 index 0000000000..24185d7c07 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/AddPackageCommand.cs @@ -0,0 +1,118 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.ProjectModification; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class AddPackageCommand : IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + + protected ProjectNugetPackageAdder ProjectNugetPackageAdder { get; } + + public AddPackageCommand(ProjectNugetPackageAdder projectNugetPackageAdder) + { + ProjectNugetPackageAdder = projectNugetPackageAdder; + Logger = NullLogger.Instance; + } + + public virtual async Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + if (commandLineArgs.Target == null) + { + throw new CliUsageException("Package name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); + } + + await ProjectNugetPackageAdder.AddAsync( + GetProjectFile(commandLineArgs), + commandLineArgs.Target + ); + } + + public Task GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("'add-package' command is used to add an ABP package to a project."); + sb.AppendLine("It should be used in a folder containing a .csproj file."); + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(" abp add-package [-p|--project]"); + sb.AppendLine(""); + sb.AppendLine("Options:"); + sb.AppendLine(" -p|--project Specify the project file explicitly."); + sb.AppendLine(""); + sb.AppendLine("Examples:"); + sb.AppendLine(" abp add-package Volo.Abp.FluentValidation Adds the package to the current project."); + sb.AppendLine(" abp add-package Volo.Abp.FluentValidation -p Acme.BookStore.Application Adds the package to the given project."); + sb.AppendLine(""); + + return Task.FromResult(sb.ToString()); + } + + public Task GetShortDescriptionAsync() + { + return Task.FromResult("Adds a new ABP package to a project by adding related nuget package" + + " as a dependency to the project and adding [DependsOn(...)] attribute to" + + " the module class in the project."); + } + + protected virtual string GetProjectFile(CommandLineArgs commandLineArgs) + { + var providedProjectFile = PathHelper.NormalizePath( + commandLineArgs.Options.GetOrNull( + Options.Project.Short, + Options.Project.Long + ) + ); + + if (!providedProjectFile.IsNullOrWhiteSpace()) + { + return providedProjectFile; + } + + var foundProjectFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.csproj"); + if (foundProjectFiles.Length == 1) + { + return foundProjectFiles[0]; + } + + if (foundProjectFiles.Length == 0) + { + throw new CliUsageException("'abp add-package' command should be used inside a folder contaning a .csproj file!"); + } + + //foundProjectFiles.Length > 1 + + var sb = new StringBuilder("There are multiple project (.csproj) files in the current directory. Please specify one of the files below:"); + + foreach (var foundProjectFile in foundProjectFiles) + { + sb.AppendLine("* " + foundProjectFile); + } + + sb.AppendLine("Example:"); + sb.AppendLine($"abp add-package {commandLineArgs.Target} -p {foundProjectFiles[0]}"); + + throw new CliUsageException(sb.ToString()); + } + + + public static class Options + { + public static class Project + { + public const string Short = "p"; + public const string Long = "project"; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CommandSelector.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CommandSelector.cs new file mode 100644 index 0000000000..595917e845 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CommandSelector.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using Volo.Abp.Cli.Args; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class CommandSelector : ICommandSelector, ITransientDependency + { + protected CliOptions Options { get; } + + public CommandSelector(IOptions options) + { + Options = options.Value; + } + + public Type Select(CommandLineArgs commandLineArgs) + { + if (commandLineArgs.Command.IsNullOrWhiteSpace()) + { + return typeof(HelpCommand); + } + + return Options.Commands.GetOrDefault(commandLineArgs.Command) + ?? typeof(HelpCommand); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs new file mode 100644 index 0000000000..fe574c2112 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/HelpCommand.cs @@ -0,0 +1,82 @@ +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Volo.Abp.Cli.Args; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class HelpCommand : IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + protected CliOptions CliOptions { get; } + protected IHybridServiceScopeFactory ServiceScopeFactory { get; } + + public HelpCommand(IOptions cliOptions, + IHybridServiceScopeFactory serviceScopeFactory) + { + ServiceScopeFactory = serviceScopeFactory; + Logger = NullLogger.Instance; + CliOptions = cliOptions.Value; + } + + public async Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + if (string.IsNullOrWhiteSpace(commandLineArgs.Target)) + { + Logger.LogInformation(await GetUsageInfo()); + return; + } + + var commandType = CliOptions.Commands[commandLineArgs.Target]; + + using (var scope = ServiceScopeFactory.CreateScope()) + { + var command = (IConsoleCommand) scope.ServiceProvider.GetRequiredService(commandType); + Logger.LogInformation(await command.GetUsageInfo()); + } + } + + public async Task GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(""); + sb.AppendLine(" abp [options]"); + sb.AppendLine(""); + sb.AppendLine("Command List:"); + + foreach (var command in CliOptions.Commands.ToArray()) + { + string shortDescription; + + using (var scope = ServiceScopeFactory.CreateScope()) + { + shortDescription = await ((IConsoleCommand)scope.ServiceProvider.GetRequiredService(command.Value)) + .GetShortDescriptionAsync(); + } + + sb.Append(" >"); + sb.Append(command.Key); + sb.Append(string.IsNullOrWhiteSpace(shortDescription) ? "":":"); + sb.Append(" "); + sb.AppendLine(shortDescription); + } + + sb.AppendLine(""); + + return sb.ToString(); + } + + public Task GetShortDescriptionAsync() + { + return Task.FromResult(""); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ICommandSelector.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ICommandSelector.cs new file mode 100644 index 0000000000..507f8ab452 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/ICommandSelector.cs @@ -0,0 +1,10 @@ +using System; +using Volo.Abp.Cli.Args; + +namespace Volo.Abp.Cli.Commands +{ + public interface ICommandSelector + { + Type Select(CommandLineArgs commandLineArgs); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/IConsoleCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/IConsoleCommand.cs new file mode 100644 index 0000000000..b9fb34210b --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/IConsoleCommand.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Volo.Abp.Cli.Args; + +namespace Volo.Abp.Cli.Commands +{ + public interface IConsoleCommand + { + Task ExecuteAsync(CommandLineArgs commandLineArgs); + + Task GetUsageInfo(); + + Task GetShortDescriptionAsync(); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs new file mode 100644 index 0000000000..52517b72fa --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginCommand.cs @@ -0,0 +1,63 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.Auth; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class LoginCommand : IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + + protected AuthService AuthService { get; } + + public LoginCommand(AuthService authService) + { + AuthService = authService; + Logger = NullLogger.Instance; + } + + public async Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + if (commandLineArgs.Target.IsNullOrEmpty()) + { + throw new CliUsageException("Username name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); + } + + Console.Write("Password: "); + var password = ConsoleHelper.ReadSecret(); + if (password.IsNullOrWhiteSpace()) + { + throw new CliUsageException("Password name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); + } + + await AuthService.LoginAsync(commandLineArgs.Target, password); + + Logger.LogInformation($"Successfully logged in as '{commandLineArgs.Target}'"); + } + + public Task GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(" abp login "); + sb.AppendLine(""); + sb.AppendLine("Example:"); + sb.AppendLine(" abp login john"); + + return Task.FromResult(sb.ToString()); + } + + public Task GetShortDescriptionAsync() + { + return Task.FromResult(""); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LogoutCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LogoutCommand.cs new file mode 100644 index 0000000000..22d30367e6 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LogoutCommand.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.Auth; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class LogoutCommand : IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + + protected AuthService AuthService { get; } + + public LogoutCommand(AuthService authService) + { + AuthService = authService; + Logger = NullLogger.Instance; + } + + public Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + return AuthService.LogoutAsync(); + } + + public Task GetUsageInfo() + { + return Task.FromResult(""); + } + + public Task GetShortDescriptionAsync() + { + return Task.FromResult(""); + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..44b5416e1e --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -0,0 +1,143 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Ionic.Zip; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.ProjectBuilding; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class NewCommand : IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + + protected ProjectBuilder ProjectBuilder { get; } + + public NewCommand(ProjectBuilder projectBuilder) + { + ProjectBuilder = projectBuilder; + + Logger = NullLogger.Instance; + } + + public async Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + if (commandLineArgs.Target == null) + { + throw new CliUsageException("Project name is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo()); + } + + Logger.LogInformation("Creating a new project..."); + Logger.LogInformation("Project name: " + commandLineArgs.Target); + + var result = await ProjectBuilder.BuildAsync( + new ProjectBuildArgs( + SolutionName.Parse(commandLineArgs.Target), + commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long), + GetDatabaseProviderOrNull(commandLineArgs), + commandLineArgs.Options + ) + ); + + var outputFolder = commandLineArgs.Options.GetOrNull(Options.OutputFolder.Short, Options.OutputFolder.Long); + if (outputFolder != null) + { + if (!Directory.Exists(outputFolder)) + { + Directory.CreateDirectory(outputFolder); + } + + outputFolder = Path.GetFullPath(outputFolder); + } + else + { + outputFolder = Directory.GetCurrentDirectory(); + } + + using (var templateFileStream = new MemoryStream(result.ZipContent)) + { + using (var templateZipFile = ZipFile.Read(templateFileStream)) + { + templateZipFile.ExtractAll(outputFolder, ExtractExistingFileAction.Throw); + } + } + + Logger.LogInformation($"Successfully created the project '{commandLineArgs.Target}'"); + Logger.LogInformation($"The output folder is: '{outputFolder}'"); + } + + public Task GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(" abp new [-t|--template] [-d|--database-provider] [-o|--output-folder]"); + sb.AppendLine(""); + sb.AppendLine("Options:"); + sb.AppendLine("-t|--template "); + sb.AppendLine("-o|--output-folder "); + sb.AppendLine("-d|--database-provider (if supported by the template)"); + sb.AppendLine("--tiered (if supported by the template)"); + sb.AppendLine("--no-ui (if supported by the template)"); + sb.AppendLine(""); + sb.AppendLine("Some examples:"); + sb.AppendLine(" abp new Acme.BookStore"); + sb.AppendLine(" abp new Acme.BookStore --tiered"); + sb.AppendLine(" abp new Acme.BookStore -t mvc-module"); + sb.AppendLine(" abp new Acme.BookStore -t mvc-module no-ui"); + sb.AppendLine(" abp new Acme.BookStore -d mongodb"); + sb.AppendLine(" abp new Acme.BookStore -t mvc -d mongodb"); + sb.AppendLine(" abp new Acme.BookStore -t mvc -d mongodb -o d:\\project"); + sb.AppendLine(""); + sb.AppendLine("See the documentation for more info."); + + return Task.FromResult(sb.ToString()); + } + + public Task GetShortDescriptionAsync() + { + return Task.FromResult("Generates a new solution based on the ABP startup templates."); + } + + protected virtual DatabaseProvider GetDatabaseProviderOrNull(CommandLineArgs commandLineArgs) + { + var optionValue = commandLineArgs.Options.GetOrNull(Options.DatabaseProvider.Short, Options.DatabaseProvider.Long); + switch (optionValue) + { + case "ef": + return DatabaseProvider.EntityFrameworkCore; + case "mongodb": + return DatabaseProvider.MongoDb; + default: + return DatabaseProvider.NotSpecified; + } + } + + public static class Options + { + public static class Template + { + public const string Short = "t"; + public const string Long = "template"; + } + + public static class DatabaseProvider + { + public const string Short = "d"; + public const string Long = "database-provider"; + } + + public static class OutputFolder + { + public const string Short = "o"; + public const string Long = "output-folder"; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs new file mode 100644 index 0000000000..df95839767 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.ProjectModification; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands +{ + public class UpdateCommand : IConsoleCommand, ITransientDependency + { + public ILogger Logger { get; set; } + + private readonly VoloPackagesVersionUpdater _packagesVersionUpdater; + + public UpdateCommand(VoloPackagesVersionUpdater packagesVersionUpdater) + { + _packagesVersionUpdater = packagesVersionUpdater; + + Logger = NullLogger.Instance; + } + + public async Task ExecuteAsync(CommandLineArgs commandLineArgs) + { + var includePreviews = commandLineArgs.Options.GetOrNull(Options.IncludePreviews.Short, Options.IncludePreviews.Long) != null; + + var solution = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.sln").FirstOrDefault(); + + if (solution != null) + { + var solutionName = Path.GetFileName(solution).RemovePostFix(".sln"); + + _packagesVersionUpdater.UpdateSolution(solution, includePreviews); + + Logger.LogInformation($"Volo packages are updated in {solutionName} solution."); + return; + } + + var project = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.csproj").FirstOrDefault(); + + if (project != null) + { + var projectName = Path.GetFileName(project).RemovePostFix(".csproj"); + + _packagesVersionUpdater.UpdateProject(project, includePreviews); + + Logger.LogInformation($"Volo packages are updated in {projectName} project."); + return; + } + + throw new CliUsageException("No solution or project found in this directory." + Environment.NewLine + Environment.NewLine + GetUsageInfo()); + } + + public Task GetUsageInfo() + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + sb.AppendLine("Usage:"); + sb.AppendLine(" abp update [-p|--include-previews]"); + sb.AppendLine(""); + sb.AppendLine("Options:"); + sb.AppendLine("--include-previews (if supported by the template)"); + sb.AppendLine(""); + sb.AppendLine("Some examples:"); + sb.AppendLine(" abp update"); + sb.AppendLine(" abp update --include-previews"); + sb.AppendLine(""); + sb.AppendLine("See the documentation for more info."); + + return Task.FromResult(sb.ToString()); + } + + public Task GetShortDescriptionAsync() + { + return Task.FromResult("Automatically updates all ABP related packages in a" + + " solution or project to the latest versions."); + } + + public static class Options + { + public static class IncludePreviews + { + public const string Short = "p"; + public const string Long = "include-previews"; + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs new file mode 100644 index 0000000000..115ce41d74 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/NuGet/NuGetService.cs @@ -0,0 +1,63 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; +using Volo.Abp.Threading; + +namespace Volo.Abp.Cli.NuGet +{ + public class NuGetService : ISingletonDependency + { + protected IJsonSerializer JsonSerializer { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public NuGetService( + IJsonSerializer jsonSerializer, + ICancellationTokenProvider cancellationTokenProvider) + { + JsonSerializer = jsonSerializer; + CancellationTokenProvider = cancellationTokenProvider; + } + + public async Task GetLatestVersionOrNullAsync(string packageId, bool includePreviews = false) + { + using (var client = new HttpClient()) + { + client.Timeout = TimeSpan.FromSeconds(30); + + var responseMessage = await client.GetAsync( + $"https://api.nuget.org/v3-flatcontainer/{packageId.ToLowerInvariant()}/index.json", + CancellationTokenProvider.Token + ); + + if (!responseMessage.IsSuccessStatusCode) + { + throw new Exception("Remote server returns error! HTTP status code: " + responseMessage.StatusCode); + } + + var result = await responseMessage.Content.ReadAsStringAsync(); + + var versions = JsonSerializer.Deserialize(result).Versions; + + if (!includePreviews) + { + versions = versions + .Where(x => !x.Contains("beta") && !x.Contains("preview") && !x.Contains("alpha") && !x.Contains("rc")) + .ToList(); + } + + return versions.Count > 0 ? versions.Last() : null; + } + } + + public class NuGetVersionResultDto + { + [JsonProperty("versions")] + public List Versions { get; set; } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoTemplateStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoTemplateStore.cs new file mode 100644 index 0000000000..faf20ad974 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoTemplateStore.cs @@ -0,0 +1,159 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http; +using Volo.Abp.IO; +using Volo.Abp.Json; +using Volo.Abp.Threading; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class AbpIoTemplateStore : ITemplateStore, ITransientDependency + { + public ILogger Logger { get; set; } + + protected CliOptions Options { get; } + protected IJsonSerializer JsonSerializer { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public AbpIoTemplateStore( + IOptions options, + IJsonSerializer jsonSerializer, + ICancellationTokenProvider cancellationTokenProvider) + { + JsonSerializer = jsonSerializer; + CancellationTokenProvider = cancellationTokenProvider; + Options = options.Value; + + Logger = NullLogger.Instance; + } + + public async Task GetAsync( + string name, + DatabaseProvider databaseProvider, + string projectName, + string version = null) + { + if (version == null) + { + version = await GetLatestTemplateVersionAsync(name); + } + + DirectoryHelper.CreateIfNotExists(CliPaths.TemplateCache); + + var localCacheFile = Path.Combine(CliPaths.TemplateCache, name + "-" + version + ".zip"); + if (File.Exists(localCacheFile)) + { + Logger.LogInformation("Using cached template: " + name + ", version: " + version); + return new TemplateFile(File.ReadAllBytes(localCacheFile), version); + } + + Logger.LogInformation("Downloading template: " + name + ", version: " + version); + + var fileContent = await DownloadTemplateFileContentAsync( + new TemplateDownloadInputDto + { + Name = name, + Version = version, + DatabaseProvider = databaseProvider.ToString(), + ProjectName = projectName + } + ); + + File.WriteAllBytes(localCacheFile, fileContent); + + return new TemplateFile(fileContent, version); + } + + private async Task GetLatestTemplateVersionAsync(string name) + { + var postData = JsonSerializer.Serialize(new GetLatestTemplateVersionDto { Name = name }); + + using (var client = new HttpClient()) + { + client.Timeout = TimeSpan.FromSeconds(30); + + AddAuthentication(client); + + var responseMessage = await client.PostAsync( + $"{CliUrls.WwwAbpIo}api/download/template/get-version/", + new StringContent(postData, Encoding.UTF8, MimeTypes.Application.Json), + CancellationTokenProvider.Token + ); + + if (!responseMessage.IsSuccessStatusCode) + { + throw new Exception("Remote server returns error! HTTP status code: " + responseMessage.StatusCode); + } + + var result = await responseMessage.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(result).Version; + } + } + + private async Task DownloadTemplateFileContentAsync(TemplateDownloadInputDto input) + { + var postData = JsonSerializer.Serialize(input); + + using (var client = new HttpClient()) + { + client.Timeout = TimeSpan.FromMinutes(3); + + AddAuthentication(client); + + var responseMessage = await client.PostAsync( + $"{CliUrls.WwwAbpIo}api/download/template/", + new StringContent(postData, Encoding.UTF8, MimeTypes.Application.Json), + CancellationTokenProvider.Token + ); + + if (!responseMessage.IsSuccessStatusCode) + { + throw new Exception("Remote server returns error! HTTP status code: " + responseMessage.StatusCode); + } + + return await responseMessage.Content.ReadAsByteArrayAsync(); + } + } + + private static void AddAuthentication(HttpClient client) + { + if (File.Exists(CliPaths.AccessToken)) + { + var accessToken = File.ReadAllText(CliPaths.AccessToken, Encoding.UTF8); + if (!accessToken.IsNullOrEmpty()) + { + client.SetBearerToken(accessToken); + } + } + } + + public class TemplateDownloadInputDto + { + public string Name { get; set; } + + public string Version { get; set; } + + public string DatabaseProvider { get; set; } + + public string ProjectName { get; set; } + } + + public class GetLatestTemplateVersionDto + { + public string Name { get; set; } + } + + public class GetLatestTemplateVersionResultDto + { + public string Version { get; set; } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseProvider.cs new file mode 100644 index 0000000000..852c63057a --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/DatabaseProvider.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public enum DatabaseProvider + { + NotSpecified = 0, + EntityFrameworkCore = 1, + MongoDb = 2 + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/GithubRepositoryInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/GithubRepositoryInfo.cs new file mode 100644 index 0000000000..22c8bbe569 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/GithubRepositoryInfo.cs @@ -0,0 +1,25 @@ +using System; + +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public class GithubRepositoryInfo + { + public string RepositoryNameWithOrganization { get; } + + public string RepositoryName { get; } + + public string AccessToken { get; } + + public GithubRepositoryInfo(string repositoryNameWithOrganization, string accessToken) + { + if (!repositoryNameWithOrganization.Contains("/")) + { + throw new ApplicationException($"{nameof(repositoryNameWithOrganization)} '{repositoryNameWithOrganization}' is not valid! It should be formatted as 'organization-name/repository-name'." ); + } + + RepositoryNameWithOrganization = repositoryNameWithOrganization; + RepositoryName = repositoryNameWithOrganization.Split('/')[1]; + AccessToken = accessToken; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContext.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContext.cs new file mode 100644 index 0000000000..1ea040022f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContext.cs @@ -0,0 +1,33 @@ +using JetBrains.Annotations; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public class ProjectBuildContext + { + [NotNull] + public TemplateFile TemplateFile { get; } + + [NotNull] + public ProjectBuildArgs BuildArgs { get; } + + [NotNull] + public TemplateInfo Template { get; } + + public FileEntryList Files { get; set; } + + public ProjectResult Result { get; set; } + + public ProjectBuildContext( + [NotNull] TemplateInfo template, + [NotNull] TemplateFile templateFile, + [NotNull] ProjectBuildArgs buildArgs) + { + Template = Check.NotNull(template, nameof(template)); + TemplateFile = Check.NotNull(templateFile, nameof(templateFile)); + BuildArgs = Check.NotNull(buildArgs, nameof(buildArgs)); + + Result = new ProjectResult(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContextExtensions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContextExtensions.cs new file mode 100644 index 0000000000..e793c77011 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildContextExtensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Linq; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public static class ProjectBuildContextExtensions + { + public static FileEntry GetFile(this ProjectBuildContext context, string filePath) + { + var file = context.Files.FirstOrDefault(f => f.Name == filePath); + if (file == null) + { + throw new ApplicationException("Could not find file: " + filePath); + } + + return file; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipeline.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipeline.cs new file mode 100644 index 0000000000..a6e5e2e56f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipeline.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public class ProjectBuildPipeline + { + public List Steps { get; } + + public ProjectBuildPipeline() + { + Steps = new List(); + } + + public void Execute(ProjectBuildContext context) + { + foreach (var step in Steps) + { + step.Execute(context); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipelineBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipelineBuilder.cs new file mode 100644 index 0000000000..c4d15d5548 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipelineBuilder.cs @@ -0,0 +1,21 @@ +using Volo.Abp.Cli.ProjectBuilding.Building.Steps; + +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public static class ProjectBuildPipelineBuilder + { + public static ProjectBuildPipeline Build(ProjectBuildContext context) + { + var pipeline = new ProjectBuildPipeline(); + + pipeline.Steps.Add(new FileEntryListReadStep()); + pipeline.Steps.AddRange(context.Template.GetCustomSteps(context)); + pipeline.Steps.Add(new NugetReferenceReplaceStep()); + pipeline.Steps.Add(new TemplateCodeDeleteStep()); + pipeline.Steps.Add(new SolutionRenameStep()); + pipeline.Steps.Add(new CreateProjectResultZipStep()); + + return pipeline; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipelineStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipelineStep.cs new file mode 100644 index 0000000000..d148cea84a --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectBuildPipelineStep.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public abstract class ProjectBuildPipelineStep + { + public abstract void Execute(ProjectBuildContext context); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectResult.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectResult.cs new file mode 100644 index 0000000000..206a713413 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/ProjectResult.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public class ProjectResult + { + public byte[] ZipContent { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeLocalhostPortStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeLocalhostPortStep.cs new file mode 100644 index 0000000000..e902e245cf --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ChangeLocalhostPortStep.cs @@ -0,0 +1,35 @@ +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class ChangeLocalhostPortStep : ProjectBuildPipelineStep + { + public string LaunchSettingsFilePath { get; } + public int[] PortNumbers { get; } + + public ChangeLocalhostPortStep( + string launchSettingsFilePath, + params int[] portNumbers) + { + Check.NotNullOrEmpty(portNumbers, nameof(portNumbers)); + + LaunchSettingsFilePath = launchSettingsFilePath; + PortNumbers = portNumbers; + } + + public override void Execute(ProjectBuildContext context) + { + var launchSettings = context.GetFile(LaunchSettingsFilePath); + var randomPort = RandomHelper.GetRandom(50001, 59990); + var newContent = launchSettings.Content; + + for (var i = 0; i < PortNumbers.Length; i++) + { + newContent = newContent.Replace( + PortNumbers[i].ToString(), + (randomPort + i).ToString() + ); + } + + launchSettings.SetContent(newContent); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/CreateProjectResultZipStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/CreateProjectResultZipStep.cs new file mode 100644 index 0000000000..53e9415617 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/CreateProjectResultZipStep.cs @@ -0,0 +1,23 @@ +using Ionic.Zip; +using Volo.Abp.Cli.ProjectBuilding.Files; +using Volo.Abp.Cli.ProjectBuilding.Zipping; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class CreateProjectResultZipStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + context.Result.ZipContent = CreateZipFileFromEntries(context.Files); + } + + private static byte[] CreateZipFileFromEntries(FileEntryList entries) + { + using (var resultZipFile = new ZipFile()) + { + entries.CopyToZipFile(resultZipFile); + return resultZipFile.GetBytes(); + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/FileEntryListReadStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/FileEntryListReadStep.cs new file mode 100644 index 0000000000..3484621df1 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/FileEntryListReadStep.cs @@ -0,0 +1,26 @@ +using System.IO; +using Ionic.Zip; +using Volo.Abp.Cli.ProjectBuilding.Files; +using Volo.Abp.Cli.ProjectBuilding.Zipping; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class FileEntryListReadStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + context.Files = GetEntriesFromZipFile(context.TemplateFile.FileBytes); + } + + private static FileEntryList GetEntriesFromZipFile(byte[] fileBytes) + { + using (var memoryStream = new MemoryStream(fileBytes)) + { + using (var templateZipFile = ZipFile.Read(memoryStream)) + { + return templateZipFile.ToFileEntryList(); + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/NugetReferenceReplaceStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/NugetReferenceReplaceStep.cs new file mode 100644 index 0000000000..49ccb308c8 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/NugetReferenceReplaceStep.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using HtmlAgilityPack; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class NugetReferenceReplaceStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + new NugetReferenceReplacer( + context.Files, + "MyCompanyName", + "MyProjectName", + context.TemplateFile.Version + ).Run(); + } + + private class NugetReferenceReplacer + { + private readonly List _entries; + private readonly string _companyNamePlaceHolder; + private readonly string _projectNamePlaceHolder; + private readonly string _latestNugetPackageVersion; + + public NugetReferenceReplacer(List entries, string companyNamePlaceHolder, string projectNamePlaceHolder, string latestNugetPackageVersion) + { + _entries = entries; + _companyNamePlaceHolder = companyNamePlaceHolder; + _projectNamePlaceHolder = projectNamePlaceHolder; + _latestNugetPackageVersion = latestNugetPackageVersion; + } + + public void Run() + { + foreach (var fileEntry in _entries) + { + if (fileEntry.Name.EndsWith(".csproj")) + { + fileEntry.SetContent(ProcessFileContent(fileEntry.Content)); + } + } + } + + private string ProcessFileContent(string content) + { + Check.NotNull(content, nameof(content)); + + var doc = new HtmlDocument(); + + doc.Load(GenerateStreamFromString(content)); + + var nodes = doc.DocumentNode.SelectNodes("//projectreference[@include]"); + + if (nodes == null) + { + return content; + } + + return ProcessReferenceNodes(nodes, content); + } + + private string ProcessReferenceNodes(HtmlNodeCollection nodes, string content) + { + Check.NotNull(nodes, nameof(nodes)); + Check.NotNull(content, nameof(content)); + + foreach (var node in nodes) + { + var valueAttr = node.Attributes.FirstOrDefault(a => a.Name.ToLower() == "include"); + + // ReSharper disable once PossibleNullReferenceException : Can not be null because nodes are selected with include attribute filter in previous method + if (valueAttr.Value.Contains($"{_companyNamePlaceHolder}.{_projectNamePlaceHolder}")) + { + continue; + } + + var newValue = ConvertToNugetReference(valueAttr.Value); + + var oldLine = $" $"\"{_projectName}\""; + + public RemoveProjectFromSolutionStep( + string projectName, + string solutionFilePath = null, + string projectFolderPath = null) + { + _projectName = projectName; + _solutionFilePath = solutionFilePath ?? "/MyCompanyName.MyProjectName.sln"; + _projectFolderPath = projectFolderPath ?? ("/src/" + projectName); + } + + public override void Execute(ProjectBuildContext context) + { + var projectFolderWithSlash = _projectFolderPath + "/"; + context.Files.RemoveAll(file => file.Name.StartsWith(projectFolderWithSlash)); + context.Files.RemoveAll(file => file.Name == _projectFolderPath); + var solutionFile = context.GetFile(_solutionFilePath); + solutionFile.NormalizeLineEndings(); + solutionFile.SetLines(RemoveProject(solutionFile.GetLines().ToList())); + } + + private List RemoveProject(List solutionFileLines) + { + var projectKey = FindProjectKey(solutionFileLines); + var newSolutionFileLines = new List(); + var firstOccurence = true; + + for (var i = 0; i < solutionFileLines.Count; ++i) + { + if (solutionFileLines[i].Contains(projectKey)) + { + if (firstOccurence) + { + firstOccurence = false; + ++i; //Skip "EndProject" line too. + } + + continue; + } + + newSolutionFileLines.Add(solutionFileLines[i]); + } + + return newSolutionFileLines; + } + + private string FindProjectKey(List solutionFileLines) + { + foreach (var solutionFileLine in solutionFileLines) + { + if (solutionFileLine.Contains(ProjectNameWithQuotes)) + { + var curlyBracketStartIndex = solutionFileLine.LastIndexOf("{", StringComparison.OrdinalIgnoreCase); + var curlyBracketEndIndex = solutionFileLine.LastIndexOf("}", StringComparison.OrdinalIgnoreCase); + return solutionFileLine.Substring(curlyBracketStartIndex + 1, curlyBracketEndIndex - curlyBracketStartIndex - 1); + } + } + + throw new ApplicationException($"The solution file '{_solutionFilePath}' does not contain a project '{_projectName}'"); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RenameHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RenameHelper.cs new file mode 100644 index 0000000000..6f6fc6d983 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/RenameHelper.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Linq; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public static class RenameHelper + { + public static void RenameAll(List entries, string placeHolder, string name) + { + RenameDirectoryRecursively(entries, placeHolder, name); + RenameAllFiles(entries, placeHolder, name); + ReplaceContent(entries, placeHolder, name); + } + + private static void RenameDirectoryRecursively(List entries, string placeHolder, string name) + { + foreach (var entry in entries.Where(e => e.IsDirectory)) + { + if (entry.Name.Contains(placeHolder)) + { + entry.SetName(entry.Name.Replace(placeHolder, name)); + } + } + } + + private static void RenameAllFiles(List entries, string placeHolder, string name) + { + foreach (var entry in entries.Where(e => !e.IsDirectory)) + { + if (entry.Name.Contains(placeHolder)) + { + entry.SetName(entry.Name.Replace(placeHolder, name)); + } + } + } + + private static void ReplaceContent(List entries, string placeHolder, string name) + { + foreach (var entry in entries.Where(e => !e.IsDirectory)) + { + if (entry.Content.Length < placeHolder.Length) + { + continue; + } + + if (entry.IsBinaryFile) + { + continue; + } + + var newContent = entry.Content.Replace(placeHolder, name); + if (newContent != entry.Content) + { + entry.SetContent(newContent); + } + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs new file mode 100644 index 0000000000..076ee084d1 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/SolutionRenameStep.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class SolutionRenameStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + new SolutionRenamer( + context.Files, + "MyCompanyName", + "MyProjectName", + context.BuildArgs.SolutionName.CompanyName, + context.BuildArgs.SolutionName.ProjectName + ).Run(); + } + + private class SolutionRenamer + { + private readonly List _entries; + private readonly string _companyNamePlaceHolder; + private readonly string _projectNamePlaceHolder; + + private readonly string _companyName; + private readonly string _projectName; + + public SolutionRenamer(List entries, string companyNamePlaceHolder, string projectNamePlaceHolder, string companyName, string projectName) + { + if (string.IsNullOrWhiteSpace(companyName)) + { + companyName = null; + } + + if (companyNamePlaceHolder == null && companyName != null) + { + throw new UserFriendlyException($"Can not set {nameof(companyName)} if {nameof(companyNamePlaceHolder)} is null."); + } + + _entries = entries; + + _companyNamePlaceHolder = companyNamePlaceHolder; + _projectNamePlaceHolder = projectNamePlaceHolder ?? throw new ArgumentNullException(nameof(projectNamePlaceHolder)); + + _companyName = companyName; + _projectName = projectName ?? throw new ArgumentNullException(nameof(projectName)); + } + + public void Run() + { + if (_companyNamePlaceHolder != null && _companyName != null) + { + RenameHelper.RenameAll(_entries, _companyNamePlaceHolder, _companyName); + } + else if (_companyNamePlaceHolder != null) + { + RenameHelper.RenameAll(_entries, _companyNamePlaceHolder + "." + _projectNamePlaceHolder, _projectNamePlaceHolder); + } + + RenameHelper.RenameAll(_entries, _projectNamePlaceHolder, _projectName); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/TemplateCodeDeleteStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/TemplateCodeDeleteStep.cs new file mode 100644 index 0000000000..e673446d9f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/TemplateCodeDeleteStep.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps +{ + public class TemplateCodeDeleteStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + foreach (var file in context.Files) + { + if (file.Name.EndsWith(".cs")) //TODO: Why only cs! + { + file.RemoveTemplateCode(); + file.RemoveTemplateCodeMarkers(); + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateInfo.cs new file mode 100644 index 0000000000..4041dc8d7a --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/TemplateInfo.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Cli.ProjectBuilding.Building +{ + public abstract class TemplateInfo + { + public string Name { get; } + + public DatabaseProvider DefaultDatabaseProvider { get; } + + protected TemplateInfo( + [NotNull] string name, + DatabaseProvider defaultDatabaseProvider = DatabaseProvider.NotSpecified) + { + Name = Check.NotNullOrWhiteSpace(name, nameof(name)); + DefaultDatabaseProvider = Check.NotNull(defaultDatabaseProvider, nameof(defaultDatabaseProvider)); + } + + public virtual IEnumerable GetCustomSteps(ProjectBuildContext context) + { + return Array.Empty(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntry.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntry.cs new file mode 100644 index 0000000000..ffa248c9f2 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntry.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Volo.Abp.Cli.ProjectBuilding.Files +{ + public class FileEntry + { + private static string[] BinaryFileExtensions = { + ".exe", ".dll", ".bin", + ".suo", ".obj", ".pdb", + ".png", "jpg", "jpeg", ".ico" + ,".woff", ".woff2", ".eot", ".svg", ".ttf" + }; + + public string Name { get; private set; } + + public bool IsDirectory { get; } + + public Encoding Encoding { get; } + + public byte[] Bytes { get; private set; } + + public string Content { get; private set; } + + public bool IsBinaryFile { get; private set; } + + public FileEntry(string name, byte[] bytes, bool isDirectory) + { + Name = name; + Bytes = bytes; + IsDirectory = isDirectory; + + Encoding = CalculateEncoding(); + Content = Encoding.GetString(bytes); + IsBinaryFile = CalculateIsBinaryFile(); + } + + public void SetContent(string fileContent) + { + Content = fileContent; + Bytes = Encoding.GetBytes(fileContent); + } + + public string[] GetLines() + { + return Content.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None); + } + + public void SetLines(IEnumerable lines) + { + SetContent(lines.JoinAsString(Environment.NewLine)); + } + + public void SetName(string fileName) + { + Name = fileName; + IsBinaryFile = CalculateIsBinaryFile(); + } + + public void NormalizeLineEndings() + { + if (Content.IsNullOrEmpty()) + { + return; + } + + SetContent(Content.NormalizeLineEndings()); + } + + public override string ToString() + { + var str = new StringBuilder(Name); + + if (IsDirectory) + { + str.Append(" [DIR]"); + } + + str.Append($" [{Bytes.Length}]"); + + return str.ToString(); + } + + private Encoding CalculateEncoding() + { + if (Bytes.IsNullOrEmpty()) + { + return Encoding.ASCII; + } + + if (Bytes[0] == 0x2b && Bytes[1] == 0x2f && Bytes[2] == 0x76) + { + return Encoding.UTF7; + } + + if (Bytes[0] == 0xef && Bytes[1] == 0xbb && Bytes[2] == 0xbf) + { + return Encoding.UTF8; + } + + if (Bytes[0] == 0xff && Bytes[1] == 0xfe) + { + return Encoding.Unicode; //UTF-16LE + } + + if (Bytes[0] == 0xfe && Bytes[1] == 0xff) + { + return Encoding.BigEndianUnicode; //UTF-16BE + } + + if (Bytes[0] == 0 && Bytes[1] == 0 && Bytes[2] == 0xfe && Bytes[3] == 0xff) + { + return Encoding.UTF32; + } + + return Encoding.UTF8; + } + + private bool CalculateIsBinaryFile() + { + return BinaryFileExtensions.Any(ext => Name.EndsWith(ext)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs new file mode 100644 index 0000000000..900948ce9c --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryExtensions.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Cli.ProjectBuilding.Files +{ + public static class FileEntryExtensions + { + public static FileEntry ReplaceText(this FileEntry file, string oldText, string newText) + { + file.NormalizeLineEndings(); + file.SetContent(file.Content.Replace(oldText, newText)); + return file; + } + + public static void RemoveTemplateCode(this FileEntry file) + { + RemoveMarkedTemplateCode(file, ""); + } + + public static void RemoveTemplateCodeIf(this FileEntry file, string condition) + { + RemoveByCondition(file, "IF", condition); + } + + public static void RemoveTemplateCodeIfNot(this FileEntry file, string condition) + { + RemoveByCondition(file, "IF-NOT", condition); + } + + public static void RemoveTemplateCodeMarkers(this FileEntry file) + { + file.NormalizeLineEndings(); + + var lines = file.GetLines(); + var newLines = new List(); + + for (int i = 0; i < lines.Length; i++) + { + if (lines[i].Contains("")) + { + //TODO: What if we use inline like: some-code + //TODO: This logic skips the code in that case. Should handle it + ++i; + } + + if (i < lines.Length) + { + newLines.Add(lines[i]); + } + } + + file.SetLines(newLines); + } + + private static void RemoveByCondition(this FileEntry file, string conditionName, string condition) + { + RemoveMarkedTemplateCode(file, $""); + } + + private static void RemoveMarkedTemplateCode(this FileEntry file, string beginMark) + { + file.NormalizeLineEndings(); + + var lines = file.GetLines(); + var newLines = new List(); + + for (int i = 0; i < lines.Length; i++) + { + if (lines[i].Contains(beginMark)) + { + while (!lines[i].Contains("") && i < lines.Length) + { + ++i; + } + + ++i; + } + + if (i < lines.Length) + { + newLines.Add(lines[i]); + } + } + + file.SetLines(newLines); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryList.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryList.cs new file mode 100644 index 0000000000..69c7b89d6f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryList.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Cli.ProjectBuilding.Files +{ + public class FileEntryList : List + { + public FileEntryList(IEnumerable entries) + : base(entries) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryListExtensions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryListExtensions.cs new file mode 100644 index 0000000000..d8eff66813 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Files/FileEntryListExtensions.cs @@ -0,0 +1,24 @@ +using System; +using Ionic.Zip; + +namespace Volo.Abp.Cli.ProjectBuilding.Files +{ + public static class FileEntryListExtensions + { + public static void CopyToZipFile(this FileEntryList fileEntryList, ZipFile zipFile) + { + foreach (var entry in fileEntryList) + { + try + { + zipFile.AddEntry(entry.Name, entry.Bytes); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Github/GithubRelease.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Github/GithubRelease.cs new file mode 100644 index 0000000000..9d37befbc2 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Github/GithubRelease.cs @@ -0,0 +1,21 @@ +using System; +using Newtonsoft.Json; + +namespace Volo.Abp.Cli.ProjectBuilding.Github +{ + [JsonObject] + public class GithubRelease + { + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("prerelease")] + public bool IsPrerelease { get; set; } + + [JsonProperty("published_at")] + public DateTime PublishTime { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/IProjectBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/IProjectBuilder.cs new file mode 100644 index 0000000000..5f8caa3252 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/IProjectBuilder.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public interface IProjectBuilder + { + Task BuildAsync(ProjectBuildArgs args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ITemplateInfoProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ITemplateInfoProvider.cs new file mode 100644 index 0000000000..5330bc9962 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ITemplateInfoProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Cli.ProjectBuilding.Building; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public interface ITemplateInfoProvider + { + TemplateInfo GetDefault(); + + TemplateInfo Get(string name); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ITemplateStore.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ITemplateStore.cs new file mode 100644 index 0000000000..064c4eb81b --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ITemplateStore.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Cli.ProjectBuilding.Building; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public interface ITemplateStore + { + Task GetAsync( + string name, + DatabaseProvider databaseProvider, + string projectName, + [CanBeNull] string version = null + ); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..ca106c1f6e --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.Cli.ProjectBuilding.Building; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class ProjectBuildArgs + { + [NotNull] + public SolutionName SolutionName { get; } + + [CanBeNull] + public string TemplateName { get; set; } + + public DatabaseProvider DatabaseProvider { get; set; } + + [NotNull] + public Dictionary ExtraProperties { get; set; } + + public ProjectBuildArgs( + [NotNull] SolutionName solutionName, + [CanBeNull] string templateName = null, + DatabaseProvider databaseProvider = DatabaseProvider.NotSpecified, + Dictionary extraProperties = null) + { + DatabaseProvider = databaseProvider; + TemplateName = templateName; + SolutionName = Check.NotNull(solutionName, nameof(solutionName)); + + ExtraProperties = extraProperties ?? new Dictionary(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildResult.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildResult.cs new file mode 100644 index 0000000000..eb5e524f40 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildResult.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class ProjectBuildResult + { + public byte[] ZipContent { get; } + + public string ProjectName { get; } + + public ProjectBuildResult(byte[] zipContent, string projectName) + { + ZipContent = zipContent; + ProjectName = projectName; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuilder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuilder.cs new file mode 100644 index 0000000000..64aa02ee6e --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuilder.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class ProjectBuilder : IProjectBuilder, ITransientDependency + { + protected ITemplateStore TemplateStore { get; } + protected ITemplateInfoProvider TemplateInfoProvider { get; } + + public ProjectBuilder(ITemplateStore templateStore, ITemplateInfoProvider templateInfoProvider) + { + TemplateStore = templateStore; + TemplateInfoProvider = templateInfoProvider; + } + + public async Task BuildAsync(ProjectBuildArgs args) + { + var templateInfo = GetTemplateInfo(args); + + args.TemplateName = templateInfo.Name; + + if (args.DatabaseProvider == DatabaseProvider.NotSpecified) + { + if (templateInfo.DefaultDatabaseProvider != DatabaseProvider.NotSpecified) + { + args.DatabaseProvider = templateInfo.DefaultDatabaseProvider; + } + } + + var templateFile = await TemplateStore.GetAsync( + args.TemplateName, + args.DatabaseProvider, + args.SolutionName.FullName + ); + + var context = new ProjectBuildContext( + templateInfo, + templateFile, + args + ); + + ProjectBuildPipelineBuilder.Build(context).Execute(context); + + return new ProjectBuildResult(context.Result.ZipContent, args.SolutionName.ProjectName); + } + + private TemplateInfo GetTemplateInfo(ProjectBuildArgs args) + { + if (args.TemplateName.IsNullOrWhiteSpace()) + { + return TemplateInfoProvider.GetDefault(); + } + else + { + return TemplateInfoProvider.Get(args.TemplateName); + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SolutionName.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SolutionName.cs new file mode 100644 index 0000000000..ba93774fbe --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/SolutionName.cs @@ -0,0 +1,50 @@ +using System; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class SolutionName + { + public string FullName { get; } + + public string CompanyName { get; } + + public string ProjectName { get; } + + private SolutionName(string fullName, string companyName, string projectName) + { + FullName = fullName; + CompanyName = companyName; + ProjectName = projectName; + } + + public static SolutionName Parse(string fullName) + { + if (fullName.Length < 1) + { + throw new UserFriendlyException("Solution name is not valid. It should be 1 character length at minimum."); + } + + string companyName = null; + var projectName = fullName; + + if (fullName.Contains(".")) + { + var lastDotIndex = fullName.LastIndexOf(".", StringComparison.OrdinalIgnoreCase); + companyName = fullName.Substring(0, lastDotIndex); + projectName = fullName.Substring(lastDotIndex + 1); + + if (companyName.Length < 1) + { + throw new UserFriendlyException("Solution name is not valid. Company name should be 1 character length at minimum."); + } + + if (projectName.Length < 1) + { + throw new UserFriendlyException("Solution name is not valid. Project name should be 1 character length at minimum."); + } + } + + return new SolutionName(fullName, companyName, projectName); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateFile.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateFile.cs new file mode 100644 index 0000000000..234a7b509a --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateFile.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class TemplateFile + { + public string Version { get; } + + public byte[] FileBytes { get; } + + public TemplateFile(byte[] fileBytes, string version) + { + FileBytes = fileBytes; + Version = version; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs new file mode 100644 index 0000000000..89a5b654e8 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs @@ -0,0 +1,29 @@ +using System; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.ProjectBuilding.Templates.Mvc; +using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectBuilding +{ + public class TemplateInfoProvider : ITemplateInfoProvider, ITransientDependency + { + public TemplateInfo GetDefault() + { + return Get(MvcTemplate.TemplateName); + } + + public TemplateInfo Get(string name) + { + switch (name) + { + case MvcTemplate.TemplateName: + return new MvcTemplate(); + case MvcModuleTemplate.TemplateName: + return new MvcModuleTemplate(); + default: + throw new Exception("There is no template found with given name: " + name); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplate.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplate.cs new file mode 100644 index 0000000000..069c5fbf03 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplate.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.ProjectBuilding.Building.Steps; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.Mvc +{ + public class MvcTemplate : TemplateInfo + { + /// + /// "mvc". + /// + public const string TemplateName = "mvc"; + + public MvcTemplate() + : base(TemplateName, DatabaseProvider.EntityFrameworkCore) + { + + } + + public override IEnumerable GetCustomSteps(ProjectBuildContext context) + { + var steps = new List(); + + SwitchDatabaseProvider(context, steps); + DeleteUnrelatedProjects(context, steps); + + return steps; + } + + private static void SwitchDatabaseProvider(ProjectBuildContext context, List steps) + { + if (context.BuildArgs.DatabaseProvider == DatabaseProvider.MongoDb) + { + steps.Add(new MvcTemplateSwitchEntityFrameworkCoreToMongoDbStep()); + } + + if (context.BuildArgs.DatabaseProvider != DatabaseProvider.EntityFrameworkCore) + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.Tests", projectFolderPath: "/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests")); + } + + if (context.BuildArgs.DatabaseProvider != DatabaseProvider.MongoDb) + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.MongoDB")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.MongoDB.Tests", projectFolderPath: "/test/MyCompanyName.MyProjectName.MongoDB.Tests")); + } + } + + private void DeleteUnrelatedProjects(ProjectBuildContext context, List steps) + { + if (context.BuildArgs.ExtraProperties.ContainsKey("tiered")) + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Tests", projectFolderPath: "/test/MyCompanyName.MyProjectName.Web.Tests")); + steps.Add(new MvcTemplateProjectRenameStep("MyCompanyName.MyProjectName.Web.Host", "MyCompanyName.MyProjectName.Web")); + } + else + { + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Host")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.HttpApi.Host")); + steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.IdentityServer")); + steps.Add(new MyTemplateChangeConsoleTestClientPortSettingsStep()); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplateProjectRenameStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplateProjectRenameStep.cs new file mode 100644 index 0000000000..33a25a80a1 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplateProjectRenameStep.cs @@ -0,0 +1,29 @@ +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.ProjectBuilding.Building.Steps; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.Mvc +{ + public class MvcTemplateProjectRenameStep : ProjectBuildPipelineStep + { + private readonly string _oldProjectName; + private readonly string _newProjectName; + + public MvcTemplateProjectRenameStep( + string oldProjectName, + string newProjectName) + { + _oldProjectName = oldProjectName; + _newProjectName = newProjectName; + } + + public override void Execute(ProjectBuildContext context) + { + context + .GetFile("/MyCompanyName.MyProjectName.sln") + .ReplaceText(_oldProjectName, _newProjectName); + + RenameHelper.RenameAll(context.Files, _oldProjectName, _newProjectName); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplateSwitchEntityFrameworkCoreToMongoDbStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplateSwitchEntityFrameworkCoreToMongoDbStep.cs new file mode 100644 index 0000000000..cffe19d05f --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MvcTemplateSwitchEntityFrameworkCoreToMongoDbStep.cs @@ -0,0 +1,196 @@ +using System; +using Volo.Abp.Cli.ProjectBuilding.Building; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.Mvc +{ + public class MvcTemplateSwitchEntityFrameworkCoreToMongoDbStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + //MyCompanyName.MyProjectName.Web + + ChangeProjectReference( + context, + "/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj", + "EntityFrameworkCore.DbMigrations", + "MongoDB" + ); + + ChangeModuleDependency( + context, + "/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs", + "MyCompanyName.MyProjectName.EntityFrameworkCore", + "MyCompanyName.MyProjectName.MongoDb", + "MyProjectNameEntityFrameworkCoreDbMigrationsModule", + "MyProjectNameMongoDbModule" + ); + + ChangeConnectionStringToMongoDb( + context, + "/src/MyCompanyName.MyProjectName.Web/appsettings.json" + ); + + //MyCompanyName.MyProjectName.IdentityServer + + ChangeProjectReference( + context, + "/src/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj", + "EntityFrameworkCore.DbMigrations", + "MongoDB" + ); + + ChangeModuleDependency( + context, + "/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs", + "MyCompanyName.MyProjectName.EntityFrameworkCore", + "MyCompanyName.MyProjectName.MongoDb", + "MyProjectNameEntityFrameworkCoreDbMigrationsModule", + "MyProjectNameMongoDbModule" + ); + + ChangeConnectionStringToMongoDb( + context, + "/src/MyCompanyName.MyProjectName.IdentityServer/appsettings.json" + ); + + //MyCompanyName.MyProjectName.HttpApi.Host + + ChangeProjectReference( + context, + "/src/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj", + "EntityFrameworkCore.DbMigrations", + "MongoDB" + ); + + ChangeModuleDependency( + context, + "/src/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs", + "MyCompanyName.MyProjectName.EntityFrameworkCore", + "MyCompanyName.MyProjectName.MongoDb", + "MyProjectNameEntityFrameworkCoreDbMigrationsModule", + "MyProjectNameMongoDbModule" + ); + + ChangeConnectionStringToMongoDb( + context, + "/src/MyCompanyName.MyProjectName.HttpApi.Host/appsettings.json" + ); + + //MyCompanyName.MyProjectName.DbMigrator + + ChangeProjectReference( + context, + "/src/MyCompanyName.MyProjectName.DbMigrator/MyCompanyName.MyProjectName.DbMigrator.csproj", + "EntityFrameworkCore.DbMigrations", + "MongoDB" + ); + + ChangeModuleDependency( + context, + "/src/MyCompanyName.MyProjectName.DbMigrator/MyProjectNameDbMigratorModule.cs", + "MyCompanyName.MyProjectName.EntityFrameworkCore", + "MyCompanyName.MyProjectName.MongoDb", + "MyProjectNameEntityFrameworkCoreDbMigrationsModule", + "MyProjectNameMongoDbModule" + ); + + ChangeConnectionStringToMongoDb( + context, + "/src/MyCompanyName.MyProjectName.DbMigrator/appsettings.json" + ); + + //MyCompanyName.MyProjectName.Domain.Tests + + ChangeProjectReference( + context, + "/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj", + "EntityFrameworkCore.Tests", + "MongoDB.Tests" + ); + + ChangeModuleDependency( + context, + "/test/MyCompanyName.MyProjectName.Domain.Tests/MyProjectNameDomainTestModule.cs", + "MyCompanyName.MyProjectName.EntityFrameworkCore", + "MyCompanyName.MyProjectName.MongoDb", + "MyProjectNameEntityFrameworkCoreDbMigrationsModule", + "MyProjectNameMongoDbTestModule" + ); + } + + private void ChangeProjectReference( + ProjectBuildContext context, + string targetProjectFilePath, + string oldReference, + string newReference) + { + var file = context.GetFile(targetProjectFilePath); + + file.NormalizeLineEndings(); + + var lines = file.GetLines(); + for (var i = 0; i < lines.Length; i++) + { + if (lines[i].Contains("ProjectReference") && lines[i].Contains(oldReference)) + { + lines[i] = lines[i].Replace(oldReference, newReference); + file.SetLines(lines); + return; + } + } + + throw new ApplicationException($"Could not find the '{oldReference}' reference in the project '{targetProjectFilePath}'!"); + } + + private void ChangeModuleDependency( + ProjectBuildContext context, + string targetModuleFilePath, + string oldNamespace, + string newNamespace, + string oldModuleName, + string newModuleName) + { + var file = context.GetFile(targetModuleFilePath); + + file.NormalizeLineEndings(); + + var lines = file.GetLines(); + + for (var i = 0; i < lines.Length; i++) + { + if (lines[i].Contains($"using {oldNamespace};")) + { + lines[i] = $"using {newNamespace};"; + } + else if (lines[i].Contains(oldModuleName)) + { + lines[i] = lines[i].Replace(oldModuleName, newModuleName); + } + } + + file.SetLines(lines); + } + + private void ChangeConnectionStringToMongoDb( + ProjectBuildContext context, + string appsettingFilePath) + { + var file = context.GetFile(appsettingFilePath); + + file.NormalizeLineEndings(); + + var lines = file.GetLines(); + for (var i = 0; i < lines.Length; i++) + { + if (lines[i].Contains("Default") && lines[i].Contains("Database")) + { + lines[i] = " \"Default\": \"mongodb://localhost:27017|MyProjectName\""; + file.SetLines(lines); + return; + } + } + + throw new ApplicationException("Could not find the 'Default' connection string in appsettings.json file!"); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MyTemplateChangeConsoleTestClientPortSettingsStep.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MyTemplateChangeConsoleTestClientPortSettingsStep.cs new file mode 100644 index 0000000000..88e0a562c8 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Mvc/MyTemplateChangeConsoleTestClientPortSettingsStep.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.Mvc +{ + public class MyTemplateChangeConsoleTestClientPortSettingsStep : ProjectBuildPipelineStep + { + public override void Execute(ProjectBuildContext context) + { + context + .GetFile("/test/MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp/appsettings.json") + .ReplaceText("44395", "44361") + .ReplaceText("44348", "44361"); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/MvcModule/MvcModuleTemplate.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/MvcModule/MvcModuleTemplate.cs new file mode 100644 index 0000000000..e3258ec8a6 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/MvcModule/MvcModuleTemplate.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.Cli.ProjectBuilding.Building.Steps; + +namespace Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule +{ + public class MvcModuleTemplate : TemplateInfo + { + /// + /// "mvc-module". + /// + public const string TemplateName = "mvc-module"; + + public MvcModuleTemplate() + : base(TemplateName) + { + + } + + public override IEnumerable GetCustomSteps(ProjectBuildContext context) + { + var steps = new List(); + + DeleteUnrelatedProjects(context, steps); + + return steps; + } + + private void DeleteUnrelatedProjects(ProjectBuildContext context, List steps) + { + if (context.BuildArgs.ExtraProperties.ContainsKey("no-ui")) + { + steps.Add(new RemoveProjectFromSolutionStep( + "MyCompanyName.MyProjectName.Web" + )); + + steps.Add(new RemoveProjectFromSolutionStep( + "MyCompanyName.MyProjectName.Web.Host", + projectFolderPath: "/host/MyCompanyName.MyProjectName.Web.Host" + )); + + steps.Add(new RemoveProjectFromSolutionStep( + "MyCompanyName.MyProjectName.Web.Unified", + projectFolderPath: "/host/MyCompanyName.MyProjectName.Web.Unified" + )); + } + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Zipping/ZipFileExtensions.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Zipping/ZipFileExtensions.cs new file mode 100644 index 0000000000..0160c8390e --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Zipping/ZipFileExtensions.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ionic.Zip; +using Volo.Abp.Cli.ProjectBuilding.Files; + +namespace Volo.Abp.Cli.ProjectBuilding.Zipping +{ + public static class ZipFileExtensions + { + public static byte[] GetBytes(this ZipFile zipFile) + { + using (var ms = new MemoryStream()) + { + zipFile.Save(ms); + return ms.ToArray(); + } + } + + public static FileEntryList ToFileEntryList(this ZipFile zipFile, string rootFolder = null) + { + var zipEntries = zipFile.Entries; + + if (rootFolder != null) + { + zipEntries = zipEntries.Where(entry => entry.FileName.StartsWith(rootFolder)).ToList(); + } + + var fileEntries = new List(); + + foreach (var zipEntry in zipEntries) + { + using (var entryStream = zipEntry.OpenReader()) + { + var fileName = zipEntry.FileName; + + if (rootFolder != null) + { + fileName = fileName.RemovePreFix(rootFolder); + } + + if (fileName.IsNullOrEmpty()) + { + continue; + } + + fileName = fileName.EnsureStartsWith('/'); + + fileEntries.Add(new FileEntry(fileName, entryStream.GetAllBytes(), zipEntry.IsDirectory)); + } + } + + return new FileEntryList(fileEntries); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs new file mode 100644 index 0000000000..0211a42243 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DbContextFileBuilderConfigureAdder.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class DbContextFileBuilderConfigureAdder : ITransientDependency + { + public ILogger Logger { get; set; } + + protected UsingStatementAdder UsingStatementAdder { get; } + + public DbContextFileBuilderConfigureAdder(UsingStatementAdder usingStatementAdder) + { + UsingStatementAdder = usingStatementAdder; + Logger = NullLogger.Instance; + } + + public void Add(string path, string moduleConfiguration) + { + var file = File.ReadAllText(path); + + file = UsingStatementAdder.Add(file, GetNamespace(moduleConfiguration)); + + var stringToAdd = GetLineToAdd(moduleConfiguration); + if (!file.Contains(stringToAdd)) + { + var indexToInsert = FindIndexToInsert(file); + + if (indexToInsert <= 0 || indexToInsert >= file.Length) + { + Logger.LogWarning($"\"OnModelCreating(ModelBuilder builder)\" method couldn't be found in {path}"); + return; + } + file = file.Insert(indexToInsert, " " + stringToAdd + Environment.NewLine + " "); + } + + + File.WriteAllText(path, file); + } + + protected int FindIndexToInsert(string file) + { + var indexOfMethodDeclaration = file.IndexOf("OnModelCreating(", StringComparison.Ordinal); + var indexOfOpeningBracket = indexOfMethodDeclaration + file.Substring(indexOfMethodDeclaration).IndexOf('{'); + + var stack = 1; + var index = indexOfOpeningBracket; + + while (stack > 0) + { + index++; + + if (index >= file.Length) + { + break; + } + + if (file[index] == '{') + { + stack++; + } + else if (file[index] == '}') + { + stack--; + } + } + + return index; + } + + protected string GetLineToAdd(string moduleConfiguration) + { + return "builder." + moduleConfiguration.Split(':')[1] + "();"; + } + + protected string GetNamespace(string moduleConfiguration) + { + return string.Join(".", moduleConfiguration.Split(':')[0]); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs new file mode 100644 index 0000000000..db39a660ce --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/DerivedClassFinder.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class DerivedClassFinder : ITransientDependency + { + public ILogger Logger { get; set; } + + public DerivedClassFinder() + { + Logger = NullLogger.Instance; + } + + public virtual List Find(string csprojFilePath, string baseClass) + { + var moduleFilePaths = new List(); + var csprojFileDirectory = Path.GetDirectoryName(csprojFilePath); + var binFile = Path.Combine(csprojFileDirectory, "bin"); + var objFile = Path.Combine(csprojFileDirectory, "obj"); + + + var csFiles = new DirectoryInfo(csprojFileDirectory) + .GetFiles("*.cs", SearchOption.AllDirectories) + .Where(f => !f.DirectoryName.StartsWith(binFile)) + .Where(f => !f.DirectoryName.StartsWith(objFile)) + .Select(f => f.FullName) + .ToList(); + + foreach (var csFile in csFiles) + { + try + { + if (IsDerived(csFile, baseClass)) + { + moduleFilePaths.Add(csFile); + } + } + catch (Exception) + { + Logger.LogDebug($"Couldn't parse {csFile}."); + } + } + + return moduleFilePaths; + } + + protected bool IsDerived(string csFile, string baseClass) + { + Logger.LogDebug(csFile); + var root = CSharpSyntaxTree.ParseText(File.ReadAllText(csFile)).GetRoot(); + var namespaceSyntax = root.DescendantNodes().OfType().First(); + var classDeclaration = (namespaceSyntax.DescendantNodes().OfType()).First(); + var baseTypeList = classDeclaration.BaseList?.Types.Select(t => t.ToString()).ToList(); + + if (baseTypeList == null) + { + return false; + } + + foreach (var baseType in baseTypeList) + { + if (baseType.Contains('<') && baseType.Substring(0, baseType.IndexOf('<')) == baseClass) + { + return true; + } + if (baseType == baseClass) + { + return true; + } + } + + return false; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs new file mode 100644 index 0000000000..d3c324431b --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs @@ -0,0 +1,46 @@ +using System; +using System.Diagnostics; +using System.IO; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class EfCoreMigrationAdder : ITransientDependency + { + public void AddMigration(string csprojFile, string module, bool updateDatabase = true) + { + var moduleName = ParseModuleName(module); + var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix(); + + CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" & dotnet ef migrations add " + migrationName); + + if (updateDatabase) + { + UpdateDatabase(csprojFile); + } + } + + protected void UpdateDatabase(string csprojFile) + { + CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" & dotnet ef database update"); + } + + protected virtual string ParseModuleName(string fullModuleName) + { + var words = fullModuleName?.Split('.'); + + if (words == null || words.Length <= 1) + { + return ""; + } + + return words[words.Length - 1]; + } + + protected virtual string GetUniquePostFix() + { + return "_" + new Random().Next(1,99999); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleClassDependcyAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleClassDependcyAdder.cs new file mode 100644 index 0000000000..9388ce42fe --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleClassDependcyAdder.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using System.Linq; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class ModuleClassDependcyAdder : ITransientDependency + { + protected UsingStatementAdder UsingStatementAdder { get; } + + public ModuleClassDependcyAdder(UsingStatementAdder usingStatementAdder) + { + UsingStatementAdder = usingStatementAdder; + } + + public virtual void Add(string path, string module) + { + ParseModuleNameAndNameSpace(module, out var nameSpace, out var moduleName); + + var file = File.ReadAllText(path); + + file = UsingStatementAdder.Add(file, nameSpace); + + if (!file.Contains(moduleName) ) + { + file = InsertDependsOnAttribute(file, moduleName); + } + + File.WriteAllText(path, file); + } + + protected virtual string InsertDependsOnAttribute(string file, string moduleName) + { + var indexOfPublicClassDeclaration = GetIndexOfWhereDependsOnWillBeAdded(file); + var dependsOnAttribute = GetDependsOnAttribute(moduleName); + + return file.Insert(indexOfPublicClassDeclaration, dependsOnAttribute); + } + + protected virtual int GetIndexOfWhereDependsOnWillBeAdded(string file) + { + var indexOfPublicClassDeclaration = file.IndexOf("public class", StringComparison.Ordinal); + + if (indexOfPublicClassDeclaration < 0) + { + throw new Exception("\"public class\" declaration not found!"); + } + + return indexOfPublicClassDeclaration; + } + + protected virtual string GetDependsOnAttribute(string moduleName) + { + return "[DependsOn(typeof(" + moduleName + "))]" + Environment.NewLine + " "; + } + + protected virtual string GetUsingStatement(string nameSpace) + { + return "using " + nameSpace + ";"; + } + + protected virtual void ParseModuleNameAndNameSpace(string module, out string nameSpace, out string moduleName) + { + var words = module?.Split('.'); + + if (words == null || words.Length <= 1) + { + nameSpace = null; + moduleName = module; + return; + } + + moduleName = words[words.Length - 1]; + nameSpace = string.Join(".", words.Take(words.Length - 1)); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs new file mode 100644 index 0000000000..d5e7a4e15a --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ModuleInfo.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class ModuleInfo + { + public string Name { get; set; } + + public string DisplayName { get; set; } + + public string EfCoreConfigureMethodName { get; set; } + + public List NugetPackages { get; set; } + + public List NpmPackages { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmApplicationType.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmApplicationType.cs new file mode 100644 index 0000000000..9f58aa52e9 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmApplicationType.cs @@ -0,0 +1,10 @@ +using System; + +namespace Volo.Abp.Cli.ProjectModification +{ + [Flags] + public enum NpmApplicationType + { + Mvc = 1 + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackageInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackageInfo.cs new file mode 100644 index 0000000000..47f0f1092d --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackageInfo.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Cli.ProjectModification +{ + public class NpmPackageInfo + { + public string Name { get; set; } + + public NpmApplicationType ApplicationType { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageInfo.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageInfo.cs new file mode 100644 index 0000000000..03c5c5493b --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageInfo.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.Cli.ProjectModification +{ + public class NugetPackageInfo + { + public string Name { get; set; } + + public string ModuleClass { get; set; } + + public NuGetPackageTarget Target { get; set; } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..66467bfa36 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.Cli.ProjectModification +{ + public enum NuGetPackageTarget : byte + { + DomainShared = 1, + Domain = 2, + ApplicationContracts = 3, + Application = 4, + HttpApi = 5, + HttpApiClient = 6, + Web = 7, + EntityFrameworkCore = 8, + MongoDB = 9 + } +} \ 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 new file mode 100644 index 0000000000..548290df5c --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Volo.Abp.Cli.ProjectModification +{ + public static class ProjectFinder + { + public static string FindNuGetTargetProjectFile(string[] projectFiles, NuGetPackageTarget target) + { + if (!projectFiles.Any()) + { + return null; + } + + var assemblyNames = GetAssemblyNames(projectFiles); + + switch (target) + { + case NuGetPackageTarget.Web: + return FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); + case NuGetPackageTarget.EntityFrameworkCore: + return FindProjectEndsWith(projectFiles, assemblyNames, ".EntityFrameworkCore"); + case NuGetPackageTarget.MongoDB: + return FindProjectEndsWith(projectFiles, assemblyNames, ".MongoDB"); + case NuGetPackageTarget.Application: + return FindProjectEndsWith(projectFiles, assemblyNames, ".Application") ?? + FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); + case NuGetPackageTarget.ApplicationContracts: + return FindProjectEndsWith(projectFiles, assemblyNames, ".Application.Contracts"); + case NuGetPackageTarget.Domain: + return FindProjectEndsWith(projectFiles, assemblyNames, ".Domain") ?? + FindProjectEndsWith(projectFiles, assemblyNames, ".Application") ?? + FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); + case NuGetPackageTarget.DomainShared: + return FindProjectEndsWith(projectFiles, assemblyNames, ".Domain.Shared"); + case NuGetPackageTarget.HttpApi: + return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi"); + case NuGetPackageTarget.HttpApiClient: + return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Client"); + default: + throw new ApplicationException($"{nameof(NuGetPackageTarget)}.{target} has not implemented!"); + } + } + + public static string[] FindNpmTargetProjectFile(string[] projectFiles) + { + if (!projectFiles.Any()) + { + return Array.Empty(); + } + + var assemblyNames = GetAssemblyNames(projectFiles); + + var projects = new List(); + + var project = FindProjectEndsWith(projectFiles, assemblyNames, ".Web.Host"); + if (project != null) + { + projects.Add(project); + } + + project = FindProjectEndsWith(projectFiles, assemblyNames, ".Host", ".HttpApi.Host"); + if (project != null) + { + projects.Add(project); + } + + if (projects.Any()) + { + return projects.ToArray(); + } + + project = FindProjectEndsWith(projectFiles, assemblyNames, ".Web"); + if (project != null) + { + projects.Add(project); + } + + return projects.ToArray(); + } + + public static string[] GetProjectFiles(string solutionFile) + { + var baseProjectFolder = GetBaseProjectFolder(solutionFile); + return Directory.GetFiles(baseProjectFolder, "*.csproj", SearchOption.AllDirectories); + } + + public static string[] GetAssemblyNames(string[] projectFiles) + { + return projectFiles.Select(GetAssemblyName).ToArray(); + } + + public static string GetAssemblyName(string projectFile) + { + return projectFile + .Substring(projectFile.LastIndexOf(Path.DirectorySeparatorChar) + 1) + .RemovePostFix(StringComparison.OrdinalIgnoreCase, ".csproj"); + } + + private static string FindProjectEndsWith( + string[] projectFiles, + string[] assemblyNames, + string postfix, + string excludePostfix = null) + { + for (var i = 0; i < assemblyNames.Length; i++) + { + var assemblyName = assemblyNames[i]; + if(assemblyName.EndsWith(postfix, StringComparison.OrdinalIgnoreCase) && + (excludePostfix == null || !assemblyName.EndsWith(excludePostfix))) + { + return projectFiles[i]; + } + } + + return null; + } + + private static string GetBaseProjectFolder(string solutionFile) + { + var baseFolder = Path.GetDirectoryName(solutionFile); + var srcFolder = Path.Combine(baseFolder, "src"); + if (Directory.Exists(srcFolder)) + { + baseFolder = srcFolder; + } + + return baseFolder; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs new file mode 100644 index 0000000000..63e0eb0393 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNpmPackageAdder.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; +using Volo.Abp.IO; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class ProjectNpmPackageAdder : ITransientDependency + { + public ILogger Logger { get; set; } + + public ProjectNpmPackageAdder() + { + Logger = NullLogger.Instance; + } + + public Task AddAsync(string directory, NpmPackageInfo npmPackage) + { + var packageJsonFilePath = Path.Combine(directory, "package.json"); + if (!File.Exists(packageJsonFilePath)) + { + return Task.CompletedTask; + } + + Logger.LogInformation($"Installing '{npmPackage.Name}' package to the project '{packageJsonFilePath}'..."); + + using (DirectoryHelper.ChangeCurrentDirectory(directory)) + { + Logger.LogInformation("yarn add " + npmPackage.Name); + CmdHelper.RunCmd("yarn add " + npmPackage.Name); + + Logger.LogInformation("gulp"); + CmdHelper.RunCmd("gulp"); + } + + return Task.CompletedTask; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs new file mode 100644 index 0000000000..3a7dab7b3a --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectNugetPackageAdder.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Cli.Utils; +using Volo.Abp.DependencyInjection; +using Volo.Abp.IO; +using Volo.Abp.Json; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class ProjectNugetPackageAdder : ITransientDependency + { + public ILogger Logger { get; set; } + + protected IJsonSerializer JsonSerializer { get; } + protected ProjectNpmPackageAdder NpmPackageAdder { get; } + protected DerivedClassFinder ModuleClassFinder { get; } + protected ModuleClassDependcyAdder ModuleClassDependcyAdder { get; } + + public ProjectNugetPackageAdder( + IJsonSerializer jsonSerializer, + ProjectNpmPackageAdder npmPackageAdder, + DerivedClassFinder moduleClassFinder, + ModuleClassDependcyAdder moduleClassDependcyAdder) + { + JsonSerializer = jsonSerializer; + NpmPackageAdder = npmPackageAdder; + ModuleClassFinder = moduleClassFinder; + ModuleClassDependcyAdder = moduleClassDependcyAdder; + Logger = NullLogger.Instance; + } + + public async Task AddAsync(string projectFile, string packageName) + { + await AddAsync( + projectFile, + await FindNugetPackageInfoAsync(packageName) + ); + } + + public async Task AddAsync(string projectFile, NugetPackageInfo package) + { + using (DirectoryHelper.ChangeCurrentDirectory(Path.GetDirectoryName(projectFile))) + { + Logger.LogInformation($"Installing '{package.Name}' package to the project '{Path.GetFileNameWithoutExtension(projectFile)}'..."); + + CmdHelper.Run("dotnet", "add package " + package.Name); + + var moduleFiles = ModuleClassFinder.Find(projectFile, "AbpModule"); + if (moduleFiles.Count == 0) + { + throw new CliUsageException($"Could not find a class derived from AbpModule in the project {projectFile}"); + } + + if (moduleFiles.Count > 1) + { + throw new CliUsageException($"There are multiple classes derived from AbpModule in the project {projectFile}: " + moduleFiles.JoinAsString(", ")); + } + + ModuleClassDependcyAdder.Add(moduleFiles.First(), package.ModuleClass); + + Logger.LogInformation("Successfully installed."); + } + } + + protected virtual async Task FindNugetPackageInfoAsync(string moduleName) + { + using (var client = new HttpClient()) + { + var url = $"{CliUrls.WwwAbpIo}api/app/nugetPackage/byName/?name=" + moduleName; + + var response = await client.GetAsync(url); + + if (!response.IsSuccessStatusCode) + { + if (response.StatusCode == HttpStatusCode.NotFound) + { + throw new CliUsageException($"'{moduleName}' nuget package could not be found!"); + } + + throw new Exception($"ERROR: Remote server returns '{response.StatusCode}'"); + } + + var responseContent = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(responseContent); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..e7a65c0ae8 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -0,0 +1,147 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class SolutionModuleAdder : ITransientDependency + { + public ILogger Logger { get; set; } + + protected IJsonSerializer JsonSerializer { get; } + protected ProjectNugetPackageAdder ProjectNugetPackageAdder { get; } + protected DbContextFileBuilderConfigureAdder DbContextFileBuilderConfigureAdder { get; } + protected EfCoreMigrationAdder EfCoreMigrationAdder { get; } + protected ProjectNpmPackageAdder ProjectNpmPackageAdder { get; } + protected DerivedClassFinder DerivedClassFinder { get; } + + public SolutionModuleAdder( + IJsonSerializer jsonSerializer, + ProjectNugetPackageAdder projectNugetPackageAdder, + DbContextFileBuilderConfigureAdder dbContextFileBuilderConfigureAdder, + EfCoreMigrationAdder efCoreMigrationAdder, + DerivedClassFinder derivedClassFinder, + ProjectNpmPackageAdder projectNpmPackageAdder) + { + EfCoreMigrationAdder = efCoreMigrationAdder; + DerivedClassFinder = derivedClassFinder; + JsonSerializer = jsonSerializer; + ProjectNugetPackageAdder = projectNugetPackageAdder; + DbContextFileBuilderConfigureAdder = dbContextFileBuilderConfigureAdder; + ProjectNpmPackageAdder = projectNpmPackageAdder; + Logger = NullLogger.Instance; + } + + public virtual async Task AddAsync( + [NotNull] string solutionFile, + [NotNull] string moduleName, + bool skipDbMigrations = false) + { + Check.NotNull(solutionFile, nameof(solutionFile)); + Check.NotNull(moduleName, nameof(moduleName)); + + var module = await FindModuleInfoAsync(moduleName); + + Logger.LogInformation($"Installing module '{module.Name}' to the solution '{Path.GetFileNameWithoutExtension(solutionFile)}'"); + + var projectFiles = ProjectFinder.GetProjectFiles(solutionFile); + + foreach (var nugetPackage in module.NugetPackages) + { + var targetProjectFile = ProjectFinder.FindNuGetTargetProjectFile(projectFiles, nugetPackage.Target); + if (targetProjectFile == null) + { + Logger.LogDebug($"Target project is not available for NuGet package '{nugetPackage.Name}'"); + continue; + } + + await ProjectNugetPackageAdder.AddAsync(targetProjectFile, nugetPackage); + } + + if (!module.NpmPackages.IsNullOrEmpty()) + { + var targetProjects = ProjectFinder.FindNpmTargetProjectFile(projectFiles); + if (targetProjects.Any()) + { + foreach (var targetProject in targetProjects) + { + foreach (var npmPackage in module.NpmPackages.Where(p => p.ApplicationType.HasFlag(NpmApplicationType.Mvc))) + { + await ProjectNpmPackageAdder.AddAsync(Path.GetDirectoryName(targetProject), npmPackage); + } + } + } + else + { + Logger.LogDebug("Target project is not available for NPM packages."); + } + } + + ModifyDbContext(projectFiles, module, skipDbMigrations); + } + + protected void ModifyDbContext(string[] projectFiles, ModuleInfo module, bool skipDbMigrations = false) + { + if (string.IsNullOrWhiteSpace(module.EfCoreConfigureMethodName)) + { + return; + } + + var dbMigrationsProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrations.csproj")); + + if (dbMigrationsProject == null) + { + Logger.LogDebug("Solution doesn't have a \".DbMigrations\" project."); + return; + } + + var dbContextFile = DerivedClassFinder.Find(dbMigrationsProject, "AbpDbContext").FirstOrDefault(); + + if (dbContextFile == null) + { + Logger.LogDebug($"{dbMigrationsProject} project doesn't have a class that is derived from \"AbpDbContext\"."); + return; + } + + DbContextFileBuilderConfigureAdder.Add(dbContextFile, module.EfCoreConfigureMethodName); + + + if (!skipDbMigrations) + { + EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name); + } + } + + protected virtual async Task FindModuleInfoAsync(string moduleName) + { + using (var client = new HttpClient()) + { + var url = $"{CliUrls.WwwAbpIo}api/app/module/byName/?name=" + moduleName; + + var response = await client.GetAsync(url); + + if (!response.IsSuccessStatusCode) + { + if (response.StatusCode == HttpStatusCode.NotFound) + { + throw new CliUsageException($"ERROR: '{moduleName}' module could not be found!"); + } + + throw new Exception($"ERROR: Remote server returns '{response.StatusCode}'"); + } + + var responseContent = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(responseContent); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/UsingStatementAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/UsingStatementAdder.cs new file mode 100644 index 0000000000..aed44cc512 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/UsingStatementAdder.cs @@ -0,0 +1,63 @@ +using System; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class UsingStatementAdder : ITransientDependency + { + public string Add(string fileContent, string nameSpace) + { + if (fileContent.Contains(nameSpace)) + { + return fileContent; + } + + var index = GetIndexOfTheEndOfTheLastUsingStatement(fileContent); + + if (index < 0 || index >= fileContent.Length) + { + index = 0; + } + + var usingStatement = GetUsingStatement(nameSpace); + + return fileContent.Insert(index, usingStatement); + } + + protected string GetUsingStatement(string nameSpace) + { + return Environment.NewLine + "using " + nameSpace + ";"; + } + + protected virtual int GetIndexOfTheEndOfTheLastUsingStatement(string fileContent) + { + var indexOfNamespaceDeclaration = fileContent.IndexOf("namespace", StringComparison.Ordinal); + + if (indexOfNamespaceDeclaration < 0) + { + return 0; + } + + fileContent = fileContent.Substring(0, indexOfNamespaceDeclaration); + + var indexOfTheStartOfLastUsingStatement = + fileContent.LastIndexOf("using ", StringComparison.Ordinal); + + if (indexOfTheStartOfLastUsingStatement < 0) + { + return 0; + } + + var indexOfFirstSemiColonAfterLastUsingStatement = + fileContent.Substring(indexOfTheStartOfLastUsingStatement).IndexOf(';'); + + if (indexOfFirstSemiColonAfterLastUsingStatement < 0) + { + return 0; + } + + return indexOfTheStartOfLastUsingStatement + + indexOfFirstSemiColonAfterLastUsingStatement + 1; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloPackagesVersionUpdater.cs new file mode 100644 index 0000000000..8e0be8f1a1 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloPackagesVersionUpdater.cs @@ -0,0 +1,87 @@ +using System; +using System.IO; +using System.Text; +using Volo.Abp.Cli.NuGet; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class VoloPackagesVersionUpdater : ITransientDependency + { + private readonly NuGetService _nuGetService; + + public VoloPackagesVersionUpdater(NuGetService nuGetService) + { + _nuGetService = nuGetService; + } + + public void UpdateSolution(string solutionPath, bool includePreviews) + { + var projectPaths = ProjectFinder.GetProjectFiles(solutionPath); + + foreach (var filePath in projectPaths) + { + UpdateInternal(filePath, includePreviews); + } + } + + public void UpdateProject(string projectPath, bool includePreviews) + { + UpdateInternal(projectPath, includePreviews); + } + + protected virtual void UpdateInternal(string projectPath, bool includePreviews) + { + var fileContent = File.ReadAllText(projectPath); + + var content = new StringBuilder(); + var index = 0; + + while (index >= 0) + { + fileContent = fileContent.Substring(index); + content.Append(ReplaceAPackage(fileContent, includePreviews, out index)); + } + + File.WriteAllText(projectPath, content.ToString()); + } + + private string ReplaceAPackage(string content, bool includePreviews, out int index) + { + var packageReferenceStartText = "nclude=\"Volo."; + var returningText = new StringBuilder(); + + var indexOfPackageReference = content.IndexOf(packageReferenceStartText, StringComparison.Ordinal); + + if (indexOfPackageReference < 0) + { + index = -1; + return content; + } + + returningText.Append(content.Substring(0, indexOfPackageReference + packageReferenceStartText.Length)); + content = content.Substring(indexOfPackageReference + packageReferenceStartText.Length); + + var indexAfterQuote = content.IndexOf("\"", StringComparison.Ordinal) + 1; + + var packageId = "Volo." + content.Substring(indexAfterQuote - 1); + + returningText.Append(content.Substring(0, indexAfterQuote)); + content = content.Substring(indexAfterQuote); + + var indexAfterSecondQuote = content.IndexOf("\"", StringComparison.Ordinal) + 1; + + returningText.Append(content.Substring(0, indexAfterSecondQuote)); + content = content.Substring(indexAfterSecondQuote); + + var indexOfThirdQuote = content.IndexOf("\"", StringComparison.Ordinal); + + var version = _nuGetService.GetLatestVersionOrNullAsync(packageId, includePreviews); + returningText.Append(version); + + index = indexOfPackageReference + packageReferenceStartText.Length + indexAfterQuote + indexAfterSecondQuote + indexOfThirdQuote; + + return returningText.ToString(); + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/CmdHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/CmdHelper.cs new file mode 100644 index 0000000000..c9225b62f0 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/CmdHelper.cs @@ -0,0 +1,43 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Volo.Abp.Cli.Utils +{ + public static class CmdHelper + { + public static void Run(string file, string arguments) + { + var procStartInfo = new ProcessStartInfo(file, arguments); + Process.Start(procStartInfo).WaitForExit(); + } + + public static void RunCmd(string command) + { + var procStartInfo = new ProcessStartInfo(GetFileName(), GetArguments(command)); + Process.Start(procStartInfo).WaitForExit(); + } + + public static string GetArguments(string command) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return "-c \"" + command + "\""; + } + + //Windows default. + return "/C \"" + command + "\""; + } + + public static string GetFileName() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + //TODO: Test this. it should work for both operaion systems. + return "/bin/bash"; + } + + //Windows default. + return "cmd.exe"; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ConsoleHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ConsoleHelper.cs new file mode 100644 index 0000000000..6e05e118d1 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/ConsoleHelper.cs @@ -0,0 +1,26 @@ +using System; +using System.Text; + +namespace Volo.Abp.Cli.Utils +{ + public static class ConsoleHelper + { + public static string ReadSecret() + { + var sb = new StringBuilder(); + + while (true) + { + var keyInfo = Console.ReadKey(true); + if (keyInfo.Key == ConsoleKey.Enter) + { + break; + } + + sb.Append(keyInfo.KeyChar); + } + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/PathHelper.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/PathHelper.cs new file mode 100644 index 0000000000..eeb1751c84 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Utils/PathHelper.cs @@ -0,0 +1,23 @@ +using System.IO; +using JetBrains.Annotations; + +namespace Volo.Abp.Cli.Utils +{ + public static class PathHelper + { + public static string NormalizePath([CanBeNull] string path) + { + if (string.IsNullOrEmpty(path)) + { + return path; + } + + if (Path.IsPathRooted(path)) + { + return path; + } + + return Path.Combine(Directory.GetCurrentDirectory(), path); + } + } +} diff --git a/framework/src/Volo.Abp.Cli/Program.cs b/framework/src/Volo.Abp.Cli/Program.cs deleted file mode 100644 index 5e9dcd4006..0000000000 --- a/framework/src/Volo.Abp.Cli/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Volo.Abp.Cli -{ - public class Program - { - private static void Main(string[] args) - { - Console.WriteLine("Welcome to ABP CLI..! This tool is in development. There is no functionality yet. So, bye bye... ;)"); - } - } -} diff --git a/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj b/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj index 29b80cb98a..f28c2554b2 100644 --- a/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj +++ b/framework/src/Volo.Abp.Cli/Volo.Abp.Cli.csproj @@ -7,6 +7,19 @@ netcoreapp2.2 true abp + + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/AbpCliModule.cs b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/AbpCliModule.cs new file mode 100644 index 0000000000..339041ef8a --- /dev/null +++ b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/AbpCliModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.Cli +{ + [DependsOn( + typeof(AbpCliCoreModule), + typeof(AbpAutofacModule) + )] + public class AbpCliModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs new file mode 100644 index 0000000000..8eefc78f9b --- /dev/null +++ b/framework/src/Volo.Abp.Cli/Volo/Abp/Cli/Program.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using Serilog.Events; +using System.IO; +using Volo.Abp.Threading; + +namespace Volo.Abp.Cli +{ + public class Program + { + private static void Main(string[] args) + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning) +#if DEBUG + .MinimumLevel.Override("Volo.Abp.Cli", LogEventLevel.Debug) +#else + .MinimumLevel.Override("Volo.Abp.Cli", LogEventLevel.Information) +#endif + .Enrich.FromLogContext() + .WriteTo.File(Path.Combine(CliPaths.Log, "abp-cli-logs.txt")) + .WriteTo.Console() + .CreateLogger(); + + using (var application = AbpApplicationFactory.Create( + options => + { + options.UseAutofac(); + options.Services.AddLogging(c => c.AddSerilog()); + })) + { + application.Initialize(); + + AsyncHelper.RunSync( + () => application.ServiceProvider + .GetRequiredService() + .RunAsync(args) + ); + + application.Shutdown(); + } + } + } +} diff --git a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs index a7cf7c763e..31e7f257d4 100644 --- a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs @@ -84,11 +84,11 @@ namespace System } /// - /// Gets index of nth occurence of a char in a string. + /// Gets index of nth occurrence of a char in a string. /// /// source string to be searched /// Char to search in - /// Count of the occurence + /// Count of the occurrence public static int NthIndexOf(this string str, char c, int n) { Check.NotNull(str, nameof(str)); diff --git a/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs index a90e4212e1..a930096404 100644 --- a/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/AbpTypeExtensions.cs @@ -1,4 +1,7 @@ -namespace System +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace System { public static class AbpTypeExtensions { @@ -31,5 +34,36 @@ { return targetType.IsAssignableFrom(type); } + + /// + /// Gets all base classes of this type. + /// + /// The type to get its base classes. + /// True, to include the standard type in the returned array. + public static Type[] GetBaseClasses(this Type type, bool includeObject = true) + { + var types = new List(); + AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject); + return types.ToArray(); + } + + private static void AddTypeAndBaseTypesRecursively( + [NotNull] List types, + [CanBeNull] Type type, + bool includeObject) + { + if (type == null) + { + return; + } + + if (!includeObject && type == typeof(object)) + { + return; + } + + AddTypeAndBaseTypesRecursively(types, type.BaseType, includeObject); + types.Add(type); + } } } diff --git a/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs b/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs index 88cf61b48e..e87ab67527 100644 --- a/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/IO/AbpStreamExtensions.cs @@ -1,4 +1,6 @@ -namespace System.IO +using System.Threading.Tasks; + +namespace System.IO { public static class AbpStreamExtensions { @@ -10,5 +12,14 @@ return memoryStream.ToArray(); } } + + public static async Task GetAllBytesAsync(this Stream stream) + { + using (var memoryStream = new MemoryStream()) + { + await stream.CopyToAsync(memoryStream); + return memoryStream.ToArray(); + } + } } } diff --git a/framework/src/Volo.Abp.Core/System/Reflection/AbpAssemblyExtensions.cs b/framework/src/Volo.Abp.Core/System/Reflection/AbpAssemblyExtensions.cs new file mode 100644 index 0000000000..0e61770618 --- /dev/null +++ b/framework/src/Volo.Abp.Core/System/Reflection/AbpAssemblyExtensions.cs @@ -0,0 +1,12 @@ +using System.Diagnostics; + +namespace System.Reflection +{ + public static class AbpAssemblyExtensions + { + public static string GetFileVersion(this Assembly assembly) + { + return FileVersionInfo.GetVersionInfo(assembly.Location).FileVersion; + } + } +} diff --git a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj index 71d48038e2..e5355a19c1 100644 --- a/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj +++ b/framework/src/Volo.Abp.Core/Volo.Abp.Core.csproj @@ -14,16 +14,16 @@ - + - + - + diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs index a4862789f2..8036760f6a 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/AbpApplicationBase.cs @@ -56,7 +56,7 @@ namespace Volo.Abp public virtual void Dispose() { - + //TODO: Shutdown if not done before? } protected virtual void SetServiceProvider(IServiceProvider serviceProvider) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs index 6d314d0685..f942f44b0f 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Check.cs @@ -122,7 +122,7 @@ namespace Volo.Abp } public static string Length( - string value, + [CanBeNull] string value, [InvokerParameterName] [NotNull] string parameterName, int maxLength, int minLength = 0) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AutoRegistrationHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AutoRegistrationHelper.cs deleted file mode 100644 index dd8795855e..0000000000 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/AutoRegistrationHelper.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.Extensions.DependencyInjection; - -namespace Volo.Abp.DependencyInjection -{ - public static class AutoRegistrationHelper - { - public static IEnumerable GetExposedServices(IServiceCollection services, Type type) - { - var typeInfo = type.GetTypeInfo(); - - var customExposedServices = typeInfo - .GetCustomAttributes() - .OfType() - .SelectMany(p => p.GetExposedServiceTypes(type)) - .ToList(); - - if (customExposedServices.Any()) - { - return customExposedServices; - } - - return GetDefaultExposedServices(services, type); - } - - private static IEnumerable GetDefaultExposedServices(IServiceCollection services, Type type) - { - var serviceTypes = new List(); - - serviceTypes.Add(type); - - foreach (var interfaceType in type.GetTypeInfo().GetInterfaces()) - { - var interfaceName = interfaceType.Name; - - if (interfaceName.StartsWith("I")) - { - interfaceName = interfaceName.Right(interfaceName.Length - 1); - } - - if (type.Name.EndsWith(interfaceName)) - { - serviceTypes.Add(interfaceType); - } - } - - var exposeActions = services.GetExposingActionList(); - if (exposeActions.Any()) - { - var args = new OnServiceExposingContext(type, serviceTypes); - foreach (var action in services.GetExposingActionList()) - { - action(args); - } - } - - return serviceTypes; - } - } -} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DefaultConventionalRegistrar.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DefaultConventionalRegistrar.cs index 19740d6ab6..9c5ee06d81 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DefaultConventionalRegistrar.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/DefaultConventionalRegistrar.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; @@ -24,7 +26,11 @@ namespace Volo.Abp.DependencyInjection return; } - foreach (var serviceType in AutoRegistrationHelper.GetExposedServices(services, type)) + var serviceTypes = ExposedServiceExplorer.GetExposedServices(type); + + TriggerServiceExposing(services, type, serviceTypes); + + foreach (var serviceType in serviceTypes) { var serviceDescriptor = ServiceDescriptor.Describe(serviceType, type, lifeTime.Value); @@ -43,6 +49,19 @@ namespace Volo.Abp.DependencyInjection } } + protected virtual void TriggerServiceExposing(IServiceCollection services, Type type, List serviceTypes) + { + var exposeActions = services.GetExposingActionList(); + if (exposeActions.Any()) + { + var args = new OnServiceExposingContext(type, serviceTypes); + foreach (var action in exposeActions) + { + action(args); + } + } + } + protected virtual bool IsConventionalRegistrationDisabled(Type type) { return type.IsDefined(typeof(DisableConventionalRegistrationAttribute), true); diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ExposeServicesAttribute.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ExposeServicesAttribute.cs index f46be345af..ad808e6026 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ExposeServicesAttribute.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ExposeServicesAttribute.cs @@ -1,19 +1,67 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace Volo.Abp.DependencyInjection { public class ExposeServicesAttribute : Attribute, IExposedServiceTypesProvider { - public Type[] ExposedServiceTypes { get; } + public Type[] ServiceTypes { get; } - public ExposeServicesAttribute(params Type[] exposedServiceTypes) + public bool? IncludeDefaults { get; set; } + + public bool? IncludeSelf { get; set; } + + public ExposeServicesAttribute(params Type[] serviceTypes) { - ExposedServiceTypes = exposedServiceTypes ?? new Type[0]; + ServiceTypes = serviceTypes ?? new Type[0]; } public Type[] GetExposedServiceTypes(Type targetType) { - return ExposedServiceTypes; + var serviceList = ServiceTypes.ToList(); + + if (IncludeDefaults == true) + { + foreach (var type in GetDefaultServices(targetType)) + { + serviceList.AddIfNotContains(type); + } + + if (IncludeSelf != false) + { + serviceList.AddIfNotContains(targetType); + } + } + else if (IncludeSelf == true) + { + serviceList.AddIfNotContains(targetType); + } + + return serviceList.ToArray(); + } + + private static List GetDefaultServices(Type type) + { + var serviceTypes = new List(); + + foreach (var interfaceType in type.GetTypeInfo().GetInterfaces()) + { + var interfaceName = interfaceType.Name; + + if (interfaceName.StartsWith("I")) + { + interfaceName = interfaceName.Right(interfaceName.Length - 1); + } + + if (type.Name.EndsWith(interfaceName)) + { + serviceTypes.Add(interfaceType); + } + } + + return serviceTypes; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ExposedServiceExplorer.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ExposedServiceExplorer.cs new file mode 100644 index 0000000000..f7324014d4 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ExposedServiceExplorer.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Volo.Abp.DependencyInjection +{ + public static class ExposedServiceExplorer + { + private static readonly ExposeServicesAttribute DefaultExposeServicesAttribute = + new ExposeServicesAttribute + { + IncludeDefaults = true + }; + + public static List GetExposedServices(Type type) + { + return type + .GetCustomAttributes() + .OfType() + .DefaultIfEmpty(DefaultExposeServicesAttribute) + .SelectMany(p => p.GetExposedServiceTypes(type)) + .ToList(); + } + } +} 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 c4185cbbed..b864695430 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using JetBrains.Annotations; namespace Volo.Abp.IO @@ -45,5 +46,22 @@ namespace Volo.Abp.IO return IsSubDirectoryOf(parentDirectory, parentOfChild); } + + public static IDisposable ChangeCurrentDirectory(string targetDirectory) + { + var currentDirectory = Directory.GetCurrentDirectory(); + + if (currentDirectory.Equals(targetDirectory, StringComparison.OrdinalIgnoreCase)) + { + return NullDisposable.Instance; + } + + Directory.SetCurrentDirectory(targetDirectory); + + return new DisposeAction(() => + { + Directory.SetCurrentDirectory(currentDirectory); + }); + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs index b58fdc8eed..32c8980c54 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/IAbpModule.cs @@ -1,6 +1,4 @@ -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.DependencyInjection; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.Modularity { 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 d79ae0b843..934948dc03 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs @@ -42,12 +42,12 @@ namespace Volo.Abp.Modularity } } - _logger.LogInformation("Initialized all modules."); + _logger.LogInformation("Initialized all ABP modules."); } private void LogListOfModules() { - _logger.LogInformation("Loaded modules:"); + _logger.LogInformation("Loaded ABP modules:"); foreach (var module in _moduleContainer.Modules) { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs index 1c4b162b21..b314ca6b84 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs @@ -165,5 +165,41 @@ namespace Volo.Abp.Reflection property = currentType.GetProperty(properties.Last()); property.SetValue(obj, value); } + + + /// + /// Get all the constant values in the specified type (including the base type). + /// + /// + /// + public static string[] GetPublicConstantsRecursively(Type type) + { + const int maxRecursiveParameterValidationDepth = 8; + + var publicConstants = new List(); + + void Recursively(List constants, Type targetType, int currentDepth) + { + if (currentDepth > maxRecursiveParameterValidationDepth) + { + return; + } + + constants.AddRange(targetType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) + .Where(x => x.IsLiteral && !x.IsInitOnly) + .Select(x => x.GetValue(null).ToString())); + + var nestedTypes = targetType.GetNestedTypes(BindingFlags.Public); + + foreach (var nestedType in nestedTypes) + { + Recursively(constants, nestedType, currentDepth + 1); + } + } + + Recursively(publicConstants, type, 1); + + return publicConstants.ToArray(); + } } } diff --git a/framework/src/Volo.Abp.Data/Volo.Abp.Data.csproj b/framework/src/Volo.Abp.Data/Volo.Abp.Data.csproj index 51670ab1ee..d2734cb119 100644 --- a/framework/src/Volo.Abp.Data/Volo.Abp.Data.csproj +++ b/framework/src/Volo.Abp.Data/Volo.Abp.Data.csproj @@ -15,6 +15,7 @@ + diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs index 428d9d6313..584298f2d4 100644 --- a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpDataModule.cs @@ -2,9 +2,13 @@ using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; +using Volo.Abp.Uow; namespace Volo.Abp.Data { + [DependsOn( + typeof(AbpUnitOfWorkModule) + )] public class AbpDataModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/DataSeeder.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/DataSeeder.cs index e55f6c633b..a4d6c0886b 100644 --- a/framework/src/Volo.Abp.Data/Volo/Abp/Data/DataSeeder.cs +++ b/framework/src/Volo.Abp.Data/Volo/Abp/Data/DataSeeder.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; +using Volo.Abp.Uow; namespace Volo.Abp.Data { @@ -19,7 +20,8 @@ namespace Volo.Abp.Data Options = options.Value; } - public async Task SeedAsync(DataSeedContext context) + [UnitOfWork] + public virtual async Task SeedAsync(DataSeedContext context) { using (var scope = ServiceScopeFactory.CreateScope()) { diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/LimitedResultRequestDto.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/LimitedResultRequestDto.cs index b3ed6a23cb..0931d090f3 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/LimitedResultRequestDto.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Dtos/LimitedResultRequestDto.cs @@ -9,7 +9,9 @@ namespace Volo.Abp.Application.Dtos [Serializable] public class LimitedResultRequestDto : ILimitedResultRequest { + public static int DefaultMaxResultCount { get; set; } = 10; + [Range(1, int.MaxValue)] - public virtual int MaxResultCount { get; set; } = 10; + public virtual int MaxResultCount { get; set; } = DefaultMaxResultCount; } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs index 3b2bbc7582..909ad40940 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -31,31 +32,59 @@ namespace Volo.Abp.Application.Services IAuditingEnabled, ITransientDependency { + public IServiceProvider ServiceProvider { get; set; } + protected readonly object ServiceProviderLock = new object(); + protected TService LazyGetRequiredService(ref TService reference) + { + if (reference == null) + { + lock (ServiceProviderLock) + { + if (reference == null) + { + reference = ServiceProvider.GetRequiredService(); + } + } + } + + return reference; + } + public static string[] CommonPostfixes { get; set; } = { "AppService", "ApplicationService", "Service" }; public List AppliedCrossCuttingConcerns { get; } = new List(); - public IUnitOfWorkManager UnitOfWorkManager { get; set; } + public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager); + private IUnitOfWorkManager _unitOfWorkManager; - public IObjectMapper ObjectMapper { get; set; } + public IObjectMapper ObjectMapper => LazyGetRequiredService(ref _objectMapper); + private IObjectMapper _objectMapper; public IGuidGenerator GuidGenerator { get; set; } - public ILoggerFactory LoggerFactory { get; set; } + public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); + private ILoggerFactory _loggerFactory; - public ICurrentTenant CurrentTenant { get; set; } + public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); + private ICurrentTenant _currentTenant; - public ICurrentUser CurrentUser { get; set; } + public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); + private ICurrentUser _currentUser; - public ISettingProvider SettingProvider { get; set; } + public ISettingProvider SettingProvider => LazyGetRequiredService(ref _settingProvider); + private ISettingProvider _settingProvider; - public IClock Clock { get; set; } + public IClock Clock => LazyGetRequiredService(ref _clock); + private IClock _clock; - public IAuthorizationService AuthorizationService { get; set; } + public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); + private IAuthorizationService _authorizationService; - public IFeatureChecker FeatureChecker { get; set; } + public IFeatureChecker FeatureChecker => LazyGetRequiredService(ref _featureChecker); + private IFeatureChecker _featureChecker; - public IStringLocalizerFactory StringLocalizerFactory { get; set; } + public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); + private IStringLocalizerFactory _stringLocalizerFactory; public IStringLocalizer L => _localizer ?? (_localizer = StringLocalizerFactory.Create(LocalizationResource)); private IStringLocalizer _localizer; diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs index 36c4780672..151212925a 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AsyncCrudAppService.cs @@ -1,10 +1,10 @@ using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.Linq; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Application.Services { @@ -20,8 +20,8 @@ namespace Volo.Abp.Application.Services } } - public abstract class AsyncCrudAppService - : AsyncCrudAppService + public abstract class AsyncCrudAppService + : AsyncCrudAppService where TEntity : class, IEntity where TEntityDto : IEntityDto { @@ -32,8 +32,8 @@ namespace Volo.Abp.Application.Services } } - public abstract class AsyncCrudAppService - : AsyncCrudAppService + public abstract class AsyncCrudAppService + : AsyncCrudAppService where TEntity : class, IEntity where TEntityDto : IEntityDto { @@ -44,9 +44,9 @@ namespace Volo.Abp.Application.Services } } - public abstract class AsyncCrudAppService - : CrudAppServiceBase, - IAsyncCrudAppService + public abstract class AsyncCrudAppService + : CrudAppServiceBase, + IAsyncCrudAppService where TEntity : class, IEntity where TEntityDto : IEntityDto { @@ -66,9 +66,9 @@ namespace Volo.Abp.Application.Services return MapToEntityDto(entity); } - public virtual async Task> GetListAsync(TGetAllInput input) + public virtual async Task> GetListAsync(TGetListInput input) { - await CheckGetAllPolicyAsync(); + await CheckGetListPolicyAsync(); var query = CreateFilteredQuery(input); @@ -90,9 +90,13 @@ namespace Volo.Abp.Application.Services await CheckCreatePolicyAsync(); var entity = MapToEntity(input); - - await Repository.InsertAsync(entity); - await CurrentUnitOfWork.SaveChangesAsync(); + + if(entity is IMultiTenant && !HasTenantIdProperty(entity)) + { + TryToSetTenantId(entity); + } + + await Repository.InsertAsync(entity, autoSave: true); return MapToEntityDto(entity); } @@ -102,11 +106,9 @@ namespace Volo.Abp.Application.Services await CheckUpdatePolicyAsync(); var entity = await GetEntityByIdAsync(id); - //TODO: Check if input has id different than given id and normalize if it's default value, throw ex otherwise - MapToEntity(input, entity); - await CurrentUnitOfWork.SaveChangesAsync(); + await Repository.UpdateAsync(entity, autoSave: true); return MapToEntityDto(entity); } @@ -128,9 +130,9 @@ namespace Volo.Abp.Application.Services await CheckPolicyAsync(GetPolicyName); } - protected virtual async Task CheckGetAllPolicyAsync() + protected virtual async Task CheckGetListPolicyAsync() { - await CheckPolicyAsync(GetAllPolicyName); + await CheckPolicyAsync(GetListPolicyName); } protected virtual async Task CheckCreatePolicyAsync() diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs index 8127e88f23..2595bc6edd 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppService.cs @@ -2,6 +2,7 @@ using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Application.Services { @@ -17,8 +18,8 @@ namespace Volo.Abp.Application.Services } } - public abstract class CrudAppService - : CrudAppService + public abstract class CrudAppService + : CrudAppService where TEntity : class, IEntity where TEntityDto : IEntityDto { @@ -29,8 +30,8 @@ namespace Volo.Abp.Application.Services } } - public abstract class CrudAppService - : CrudAppService + public abstract class CrudAppService + : CrudAppService where TEntity : class, IEntity where TEntityDto : IEntityDto where TCreateInput : IEntityDto @@ -42,9 +43,9 @@ namespace Volo.Abp.Application.Services } } - public abstract class CrudAppService - : CrudAppServiceBase, - ICrudAppService + public abstract class CrudAppService + : CrudAppServiceBase, + ICrudAppService where TEntity : class, IEntity where TEntityDto : IEntityDto { @@ -62,9 +63,9 @@ namespace Volo.Abp.Application.Services return MapToEntityDto(entity); } - public virtual PagedResultDto GetAll(TGetAllInput input) + public virtual PagedResultDto GetList(TGetListInput input) { - CheckGetAllPolicy(); + CheckGetListPolicy(); var query = CreateFilteredQuery(input); @@ -86,9 +87,13 @@ namespace Volo.Abp.Application.Services CheckCreatePolicy(); var entity = MapToEntity(input); - - Repository.Insert(entity); - CurrentUnitOfWork.SaveChanges(); + + if(entity is IMultiTenant && !HasTenantIdProperty(entity)) + { + TryToSetTenantId(entity); + } + + Repository.Insert(entity, autoSave: true); return MapToEntityDto(entity); } @@ -98,9 +103,8 @@ namespace Volo.Abp.Application.Services CheckUpdatePolicy(); var entity = GetEntityById(id); - MapToEntity(input, entity); - CurrentUnitOfWork.SaveChanges(); + Repository.Update(entity, autoSave: true); return MapToEntityDto(entity); } @@ -122,9 +126,9 @@ namespace Volo.Abp.Application.Services CheckPolicy(GetPolicyName); } - protected virtual void CheckGetAllPolicy() + protected virtual void CheckGetListPolicy() { - CheckPolicy(GetAllPolicyName); + CheckPolicy(GetListPolicyName); } protected virtual void CheckCreatePolicy() diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppServiceBase.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppServiceBase.cs index eb4c7a200f..b678362c53 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppServiceBase.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/CrudAppServiceBase.cs @@ -4,6 +4,7 @@ using System.Linq.Dynamic.Core; using Volo.Abp.Application.Dtos; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; +using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; namespace Volo.Abp.Application.Services @@ -12,7 +13,7 @@ namespace Volo.Abp.Application.Services /// This is a common base class for CrudAppService and AsyncCrudAppService classes. /// Inherit either from CrudAppService or AsyncCrudAppService, not from this class. /// - public abstract class CrudAppServiceBase : + public abstract class CrudAppServiceBase : ApplicationService where TEntity : class, IEntity where TEntityDto : IEntityDto @@ -21,7 +22,7 @@ namespace Volo.Abp.Application.Services protected virtual string GetPolicyName { get; set; } - protected virtual string GetAllPolicyName { get; set; } + protected virtual string GetListPolicyName { get; set; } protected virtual string CreatePolicyName { get; set; } @@ -39,7 +40,7 @@ namespace Volo.Abp.Application.Services /// /// The query. /// The input. - protected virtual IQueryable ApplySorting(IQueryable query, TGetAllInput input) + protected virtual IQueryable ApplySorting(IQueryable query, TGetListInput input) { //Try to sort query if available var sortInput = input as ISortedResultRequest; @@ -66,7 +67,7 @@ namespace Volo.Abp.Application.Services /// /// The query. /// The input. - protected virtual IQueryable ApplyPaging(IQueryable query, TGetAllInput input) + protected virtual IQueryable ApplyPaging(IQueryable query, TGetListInput input) { //Try to use paging if available var pagedInput = input as IPagedResultRequest; @@ -93,7 +94,7 @@ namespace Volo.Abp.Application.Services /// methods. /// /// The input. - protected virtual IQueryable CreateFilteredQuery(TGetAllInput input) + protected virtual IQueryable CreateFilteredQuery(TGetListInput input) { return Repository; } @@ -150,5 +151,27 @@ namespace Volo.Abp.Application.Services ObjectMapper.Map(updateInput, entity); } + + protected virtual void TryToSetTenantId(TEntity entity) + { + var tenantId = CurrentTenant.Id; + + if (!tenantId.HasValue) + { + return; + } + + var propertyInfo = entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)); + + if (propertyInfo != null && propertyInfo.GetSetMethod() != null) + { + propertyInfo.SetValue(entity, tenantId, null); + } + } + + protected virtual bool HasTenantIdProperty(TEntity entity) + { + return entity.GetType().GetProperty(nameof(IMultiTenant.TenantId)) != null; + } } } diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ICrudAppService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ICrudAppService.cs index 9f06c63aea..7e8b96b9f8 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ICrudAppService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ICrudAppService.cs @@ -29,7 +29,7 @@ namespace Volo.Abp.Application.Services { TEntityDto Get(TKey id); - PagedResultDto GetAll(TGetListInput input); + PagedResultDto GetList(TGetListInput input); TEntityDto Create(TCreateInput input); diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs index aa2a6e1c8f..d5f0a7dc2d 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs @@ -5,6 +5,8 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed [Serializable] public class EntityEto : EtoBase { + public string EntityType { get; set; } + public string KeysAsString { get; set; } public EntityEto() @@ -12,8 +14,9 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed } - public EntityEto(string keysAsString) + public EntityEto(string entityType, string keysAsString) { + EntityType = entityType; KeysAsString = keysAsString; } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs index 354f72dddc..5b386599ef 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs @@ -1,6 +1,5 @@ -using System; +using Microsoft.Extensions.Options; using System.Collections.Generic; -using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; using Volo.Abp.EventBus.Distributed; @@ -28,7 +27,7 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed var entity = entityObj as IEntity; if (entity == null) { - throw new ArgumentException($"{nameof(entityObj)} should be an entity and implement the '{typeof(IEntity).AssemblyQualifiedName}' interface!"); + return null; } var entityType = ProxyHelper.UnProxy(entity).GetType(); @@ -36,10 +35,10 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed if (etoType == null) { var keys = entity.GetKeys().JoinAsString(","); - return new EntityEto(keys); + return new EntityEto(entityType.FullName, keys); } - //TODO: Also add KeysAsString property to resulting json for compability with the EntityEto! + //TODO: Also add KeysAsString property to resulting json for compatibility with the EntityEto! return ObjectMapper.Map(entityType, etoType, entityObj); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs index e33e6751e8..ceefb311ef 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs @@ -1,7 +1,10 @@ -namespace Volo.Abp.Domain.Entities.Events.Distributed +using JetBrains.Annotations; + +namespace Volo.Abp.Domain.Entities.Events.Distributed { public interface IEntityToEtoMapper { + [CanBeNull] object Map(object entityObj); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs index dc4edea364..c9219feffc 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs @@ -48,35 +48,95 @@ namespace Volo.Abp.Domain.Entities.Events public virtual async Task TriggerEntityCreatingEventAsync(object entity) { - await TriggerEventWithEntity(LocalEventBus, typeof(EntityCreatingEventData<>), entity, true); + await TriggerEventWithEntity( + LocalEventBus, + typeof(EntityCreatingEventData<>), + entity, + true + ); } public virtual async Task TriggerEntityCreatedEventOnUowCompletedAsync(object entity) { - await TriggerEventWithEntity(LocalEventBus, typeof(EntityCreatedEventData<>), entity, false); - await TriggerEventWithEntity(DistributedEventBus, typeof(EntityCreatedEto<>), EntityToEtoMapper.Map(entity), false); + await TriggerEventWithEntity( + LocalEventBus, + typeof(EntityCreatedEventData<>), + entity, + false + ); + + var eto = EntityToEtoMapper.Map(entity); + if (eto != null) + { + await TriggerEventWithEntity( + DistributedEventBus, + typeof(EntityCreatedEto<>), + eto, + false + ); + } } public virtual async Task TriggerEntityUpdatingEventAsync(object entity) { - await TriggerEventWithEntity(LocalEventBus, typeof(EntityUpdatingEventData<>), entity, true); + await TriggerEventWithEntity( + LocalEventBus, + typeof(EntityUpdatingEventData<>), + entity, + true + ); } public virtual async Task TriggerEntityUpdatedEventOnUowCompletedAsync(object entity) { - await TriggerEventWithEntity(LocalEventBus, typeof(EntityUpdatedEventData<>), entity, false); - await TriggerEventWithEntity(DistributedEventBus, typeof(EntityUpdatedEto<>), EntityToEtoMapper.Map(entity), false); + await TriggerEventWithEntity( + LocalEventBus, + typeof(EntityUpdatedEventData<>), + entity, + false + ); + + var eto = EntityToEtoMapper.Map(entity); + if (eto != null) + { + await TriggerEventWithEntity( + DistributedEventBus, + typeof(EntityUpdatedEto<>), + eto, + false + ); + } } public virtual async Task TriggerEntityDeletingEventAsync(object entity) { - await TriggerEventWithEntity(LocalEventBus, typeof(EntityDeletingEventData<>), entity, true); + await TriggerEventWithEntity( + LocalEventBus, + typeof(EntityDeletingEventData<>), + entity, + true + ); } public virtual async Task TriggerEntityDeletedEventOnUowCompletedAsync(object entity) { - await TriggerEventWithEntity(LocalEventBus, typeof(EntityDeletedEventData<>), entity, false); - await TriggerEventWithEntity(DistributedEventBus, typeof(EntityDeletedEto<>), EntityToEtoMapper.Map(entity), false); + await TriggerEventWithEntity( + LocalEventBus, + typeof(EntityDeletedEventData<>), + entity, + false + ); + + var eto = EntityToEtoMapper.Map(entity); + if (eto != null) + { + await TriggerEventWithEntity( + DistributedEventBus, + typeof(EntityDeletedEto<>), + EntityToEtoMapper.Map(entity), + false + ); + } } protected virtual async Task TriggerEventsInternalAsync(EntityChangeReport changeReport) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs index ea94690d61..14e8a64131 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Guids; @@ -9,13 +10,34 @@ namespace Volo.Abp.Domain.Services { public abstract class DomainService : IDomainService { - public IClock Clock { get; set; } + public IServiceProvider ServiceProvider { get; set; } + protected readonly object ServiceProviderLock = new object(); + protected TService LazyGetRequiredService(ref TService reference) + { + if (reference == null) + { + lock (ServiceProviderLock) + { + if (reference == null) + { + reference = ServiceProvider.GetRequiredService(); + } + } + } + + return reference; + } + + public IClock Clock => LazyGetRequiredService(ref _clock); + private IClock _clock; public IGuidGenerator GuidGenerator { get; set; } - public ILoggerFactory LoggerFactory { get; set; } + public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); + private ILoggerFactory _loggerFactory; - public ICurrentTenant CurrentTenant { get; set; } + public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); + private ICurrentTenant _currentTenant; protected ILogger Logger => _lazyLogger.Value; private Lazy _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); diff --git a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj index ab1b4ed370..b2394ea229 100644 --- a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj +++ b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj @@ -14,8 +14,12 @@ - - + + + + + + diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs index b861a3898c..d3a9598da1 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs @@ -1,6 +1,8 @@ -using Volo.Abp.BackgroundJobs; +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.BackgroundJobs; using Volo.Abp.Emailing.Templates; -using Volo.Abp.Emailing.Templates.Virtual; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Settings; @@ -16,6 +18,11 @@ namespace Volo.Abp.Emailing )] public class AbpEmailingModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AutoAddDefinitionProviders(context.Services); + } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => @@ -27,18 +34,42 @@ namespace Volo.Abp.Emailing { options.AddJob(); }); + } - Configure(options => + private static void AutoAddDefinitionProviders(IServiceCollection services) + { + var definitionProviders = new List(); + + services.OnRegistred(context => { - options.Templates - .Add( - new EmailTemplateDefinition(StandardEmailTemplates.DefaultLayout, isLayout: true, layout: null) - .SetVirtualFilePath("/Volo/Abp/Emailing/Templates/DefaultLayout.html") - ).Add( - new EmailTemplateDefinition(StandardEmailTemplates.SimpleMessage) - .SetVirtualFilePath("/Volo/Abp/Emailing/Templates/SimpleMessageTemplate.html") - ); + + if (typeof(IEmailTemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType)) + { + definitionProviders.Add(context.ImplementationType); + } }); + + services.Configure(options => + { + options.DefinitionProviders.AddIfNotContains(definitionProviders); + }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + var emailTemplateDefinitionManager = + scope.ServiceProvider.GetRequiredService(); + + foreach (var templateDefinition in emailTemplateDefinitionManager.GetAll()) + { + foreach (var contributor in templateDefinition.Contributors) + { + contributor.Initialize(new EmailTemplateInitializationContext(templateDefinition, scope.ServiceProvider)); + } + } + } } } } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs new file mode 100644 index 0000000000..fdc7fa5375 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Emailing.Templates.VirtualFiles; + +namespace Volo.Abp.Emailing.Templates +{ + public class DefaultEmailTemplateProvider : EmailTemplateDefinitionProvider + { + public override void Define(IEmailTemplateDefinitionContext context) + { + context.Add(new EmailTemplateDefinition(StandardEmailTemplates.DefaultLayout, isLayout: true, layout: null) + .AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout")); + + context.Add(new EmailTemplateDefinition(StandardEmailTemplates.SimpleMessage) + .AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message")); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultLayout.html b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl similarity index 100% rename from framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultLayout.html rename to framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/SimpleMessageTemplate.html b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl similarity index 100% rename from framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/SimpleMessageTemplate.html rename to framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs new file mode 100644 index 0000000000..44a91212ea --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.Emailing.Templates +{ + public class EmailTemplateContributorList : List + { + public string GetOrNull(string cultureName) + { + foreach (var contributor in this.AsQueryable().Reverse()) + { + var templateString = contributor.GetOrNull(cultureName); + if (templateString != null) + { + return templateString; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs index 9ef2ae3b1b..19f0eb2fa7 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using JetBrains.Annotations; namespace Volo.Abp.Emailing.Templates @@ -16,29 +15,22 @@ namespace Volo.Abp.Emailing.Templates public Type LocalizationResource { get; set; } - public Dictionary Properties { get; } - - /// - /// Gets/sets a key-value on the . - /// - /// Name of the property - /// - /// Returns the value in the dictionary by given . - /// Returns null if given is not present in the dictionary. - /// - public object this[string name] - { - get => Properties.GetOrDefault(name); - set => Properties[name] = value; - } + public EmailTemplateContributorList Contributors { get; } + + public string DefaultCultureName { get; } + + public bool SingleTemplateFile { get; } - public EmailTemplateDefinition([NotNull]string name, Type localizationResource = null, bool isLayout = false, string layout = DefaultLayoutPlaceHolder) + public EmailTemplateDefinition([NotNull] string name, Type localizationResource = null, bool isLayout = false, + string layout = DefaultLayoutPlaceHolder, string defaultCultureName = null, bool singleTemplateFile = false) { Name = Check.NotNullOrWhiteSpace(name, nameof(name)); - Properties = new Dictionary(); LocalizationResource = localizationResource; + Contributors = new EmailTemplateContributorList(); IsLayout = isLayout; Layout = layout; + DefaultCultureName = defaultCultureName; + SingleTemplateFile = singleTemplateFile; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs new file mode 100644 index 0000000000..03a6c95d8b --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Volo.Abp.Emailing.Templates +{ + public class EmailTemplateDefinitionContext : IEmailTemplateDefinitionContext + { + protected Dictionary EmailTemplates { get; } + + public EmailTemplateDefinitionContext(Dictionary emailTemplates) + { + EmailTemplates = emailTemplates; + } + + public virtual EmailTemplateDefinition GetOrNull(string name) + { + return EmailTemplates.GetOrDefault(name); + } + + public virtual IReadOnlyList GetAll() + { + return EmailTemplates.Values.ToImmutableList(); + } + + public virtual void Add(params EmailTemplateDefinition[] definitions) + { + if (definitions.IsNullOrEmpty()) + { + return; + } + + foreach (var definition in definitions) + { + EmailTemplates[definition.Name] = definition; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs new file mode 100644 index 0000000000..a6a58aef27 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Emailing.Templates +{ + public class EmailTemplateDefinitionManager : IEmailTemplateDefinitionManager, ISingletonDependency + { + protected Lazy> EmailTemplateDefinitions { get; } + + protected EmailTemplateOptions Options { get; } + + protected IServiceProvider ServiceProvider { get; } + + public EmailTemplateDefinitionManager( + IOptions options, + IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + Options = options.Value; + + EmailTemplateDefinitions = + new Lazy>(CreateEmailTemplateDefinitions, true); + } + + public virtual EmailTemplateDefinition Get(string name) + { + Check.NotNull(name, nameof(name)); + + var template = GetOrNull(name); + + if (template == null) + { + throw new AbpException("Undefined template: " + name); + } + + return template; + } + + public virtual IReadOnlyList GetAll() + { + return EmailTemplateDefinitions.Value.Values.ToImmutableList(); + } + + public virtual EmailTemplateDefinition GetOrNull(string name) + { + return EmailTemplateDefinitions.Value.GetOrDefault(name); + } + + protected virtual IDictionary CreateEmailTemplateDefinitions() + { + var templates = new Dictionary(); + + using (var scope = ServiceProvider.CreateScope()) + { + var providers = Options + .DefinitionProviders + .Select(p => scope.ServiceProvider.GetRequiredService(p) as IEmailTemplateDefinitionProvider) + .ToList(); + + foreach (var provider in providers) + { + provider.Define(new EmailTemplateDefinitionContext(templates)); + } + } + + return templates; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs new file mode 100644 index 0000000000..e53505fafc --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs @@ -0,0 +1,9 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Emailing.Templates +{ + public abstract class EmailTemplateDefinitionProvider : IEmailTemplateDefinitionProvider, ITransientDependency + { + public abstract void Define(IEmailTemplateDefinitionContext context); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs new file mode 100644 index 0000000000..8cd4b95bbd --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs @@ -0,0 +1,18 @@ +using System; + +namespace Volo.Abp.Emailing.Templates +{ + public class EmailTemplateInitializationContext + { + public EmailTemplateDefinition EmailTemplateDefinition { get; } + + public IServiceProvider ServiceProvider { get; } + + public EmailTemplateInitializationContext(EmailTemplateDefinition emailTemplateDefinition, + IServiceProvider serviceProvider) + { + EmailTemplateDefinition = emailTemplateDefinition; + ServiceProvider = serviceProvider; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateOptions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateOptions.cs index 6d10bd562e..57533b1aff 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateOptions.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateOptions.cs @@ -1,26 +1,18 @@ -using System.Collections.Generic; -using Volo.Abp.Emailing.Templates.Virtual; +using Volo.Abp.Collections; namespace Volo.Abp.Emailing.Templates { public class EmailTemplateOptions { - public List Providers { get; } - - public EmailTemplateDefinitionDictionary Templates { get; } - public string DefaultLayout { get; set; } + public ITypeList DefinitionProviders { get; } + public EmailTemplateOptions() { - Providers = new List - { - new VirtualFileEmailTemplateProviderContributor() - }; - - Templates = new EmailTemplateDefinitionDictionary(); - DefaultLayout = StandardEmailTemplates.DefaultLayout; + + DefinitionProviders = new TypeList(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs index c5fb44e0ed..28abdfc213 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs @@ -1,6 +1,6 @@ using System; +using System.Globalization; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -10,80 +10,105 @@ namespace Volo.Abp.Emailing.Templates { public class EmailTemplateProvider : IEmailTemplateProvider, ITransientDependency { - protected IServiceProvider ServiceProvider { get; } + protected IEmailTemplateDefinitionManager EmailTemplateDefinitionManager; protected ITemplateLocalizer TemplateLocalizer { get; } protected EmailTemplateOptions Options { get; } + protected IStringLocalizerFactory StringLocalizerFactory; - public EmailTemplateProvider( - IOptions options, - IServiceProvider serviceProvider, - ITemplateLocalizer templateLocalizer) + public EmailTemplateProvider(IEmailTemplateDefinitionManager emailTemplateDefinitionManager, + ITemplateLocalizer templateLocalizer, IStringLocalizerFactory stringLocalizerFactory, + IOptions options) { - ServiceProvider = serviceProvider; + EmailTemplateDefinitionManager = emailTemplateDefinitionManager; TemplateLocalizer = templateLocalizer; + StringLocalizerFactory = stringLocalizerFactory; Options = options.Value; } - public async Task GetAsync(string name) + public async Task GetAsync(string name, string cultureName) { - using (var scope = ServiceProvider.CreateScope()) - { - return await GetInternalAsync(scope.ServiceProvider, name); - } + return await GetInternalAsync(name, cultureName); } - protected virtual async Task GetInternalAsync(IServiceProvider serviceProvider, string name) + protected virtual async Task GetInternalAsync(string name, string cultureName) { - var context = new EmailTemplateProviderContributorContext(name, serviceProvider); + var emailTemplateDefinition = EmailTemplateDefinitionManager.GetOrNull(name); + if (emailTemplateDefinition == null) + { + // TODO: Localized message + throw new AbpException($"email template {name} not definition"); + } - foreach (var provider in Options.Providers) + var emailTemplateString = emailTemplateDefinition.Contributors.GetOrNull(cultureName); + if (emailTemplateString == null && emailTemplateDefinition.DefaultCultureName != null) { - await provider.ProvideAsync(context); + emailTemplateString = + emailTemplateDefinition.Contributors.GetOrNull(emailTemplateDefinition.DefaultCultureName); + if (emailTemplateString != null) + { + cultureName = emailTemplateDefinition.DefaultCultureName; + } } - if (context.Template == null) + if (emailTemplateString != null) { - throw new AbpException($"Could not found the template: {name}"); + var emailTemplate = new EmailTemplate(emailTemplateString, emailTemplateDefinition); + + await SetLayoutAsync(emailTemplateDefinition, emailTemplate, cultureName); + + if (emailTemplateDefinition.SingleTemplateFile) + { + await LocalizeAsync(emailTemplateDefinition, emailTemplate, cultureName); + } + + return emailTemplate; } - await SetLayoutAsync(serviceProvider, context); - await LocalizeAsync(serviceProvider, context); - - return context.Template; + // TODO: Localized message + throw new AbpException($"{cultureName} template not exist!"); } - protected virtual async Task SetLayoutAsync(IServiceProvider serviceProvider, EmailTemplateProviderContributorContext context) + protected virtual async Task SetLayoutAsync(EmailTemplateDefinition emailTemplateDefinition, + EmailTemplate emailTemplate, string cultureName) { - var layout = context.Template.Definition.Layout; - - if (layout.IsNullOrEmpty()) + var layout = emailTemplateDefinition.Layout; + if (layout.IsNullOrWhiteSpace()) { return; } - + if (layout == EmailTemplateDefinition.DefaultLayoutPlaceHolder) { layout = Options.DefaultLayout; } - var layoutTemplate = await GetInternalAsync(serviceProvider, layout); - context.Template.SetLayout(layoutTemplate); + var layoutTemplate = await GetInternalAsync(layout, cultureName); + + emailTemplate.SetLayout(layoutTemplate); } - protected virtual Task LocalizeAsync(IServiceProvider serviceProvider, EmailTemplateProviderContributorContext context) + protected virtual Task LocalizeAsync(EmailTemplateDefinition emailTemplateDefinition, + EmailTemplate emailTemplate, string cultureName) { - if (context.Template.Definition.LocalizationResource == null) + if (emailTemplateDefinition.LocalizationResource == null) { return Task.CompletedTask; } - var localizer = serviceProvider - .GetRequiredService() - .Create(context.Template.Definition.LocalizationResource); - - context.Template.SetContent( - TemplateLocalizer.Localize(localizer, context.Template.Content) - ); + var localizer = StringLocalizerFactory.Create(emailTemplateDefinition.LocalizationResource); + if (cultureName != null) + { + emailTemplate.SetContent( + TemplateLocalizer.Localize(localizer.WithCulture(new CultureInfo(cultureName)), + emailTemplate.Content) + ); + } + else + { + emailTemplate.SetContent( + TemplateLocalizer.Localize(localizer, emailTemplate.Content) + ); + } return Task.CompletedTask; } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProviderContributorContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProviderContributorContext.cs deleted file mode 100644 index 9893ddc789..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProviderContributorContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateProviderContributorContext : IServiceProviderAccessor - { - public string Name { get; } - - public IServiceProvider ServiceProvider { get; } - - public EmailTemplate Template { get; set; } - - public EmailTemplateProviderContributorContext(string name, IServiceProvider serviceProvider) - { - Name = name; - ServiceProvider = serviceProvider; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs new file mode 100644 index 0000000000..d2c2775845 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Emailing.Templates +{ + public interface IEmailTemplateContributor + { + void Initialize(EmailTemplateInitializationContext context); + + string GetOrNull(string cultureName); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs new file mode 100644 index 0000000000..1641562ccf --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.Emailing.Templates +{ + public interface IEmailTemplateDefinitionContext + { + EmailTemplateDefinition GetOrNull(string name); + + void Add(params EmailTemplateDefinition[] definitions); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs new file mode 100644 index 0000000000..0936a2fe93 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.Emailing.Templates +{ + public interface IEmailTemplateDefinitionManager + { + [NotNull] + EmailTemplateDefinition Get([NotNull] string name); + + IReadOnlyList GetAll(); + + EmailTemplateDefinition GetOrNull(string name); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs new file mode 100644 index 0000000000..691d3874d6 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Emailing.Templates +{ + public interface IEmailTemplateDefinitionProvider + { + void Define(IEmailTemplateDefinitionContext context); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs index aad1fe4f05..4fc8381833 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs @@ -4,6 +4,6 @@ namespace Volo.Abp.Emailing.Templates { public interface IEmailTemplateProvider { - Task GetAsync(string name); + Task GetAsync(string name, string cultureName); } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProviderContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProviderContributor.cs deleted file mode 100644 index 6bc63f9661..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProviderContributor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateProviderContributor - { - Task ProvideAsync(EmailTemplateProviderContributorContext contributorContext); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs new file mode 100644 index 0000000000..35ac14c8fd --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Emailing.Templates +{ + public interface ITemplateRender + { + Task RenderAsync(string template, object model = null); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs new file mode 100644 index 0000000000..8c4e24017c --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Scriban; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Emailing.Templates +{ + public class TemplateRender : ITemplateRender, ITransientDependency + { + public async Task RenderAsync(string template, object model = null) + { + var scribanTemplate = Template.Parse(template); + return await scribanTemplate.RenderAsync(model); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/EmailTemplateDefinitionExtensions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/EmailTemplateDefinitionExtensions.cs deleted file mode 100644 index cb49491b0f..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/EmailTemplateDefinitionExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Volo.Abp.Emailing.Templates.Virtual -{ - public static class EmailTemplateDefinitionExtensions - { - public static EmailTemplateDefinition SetVirtualFilePath(this EmailTemplateDefinition emailTemplateDefinition, string path) - { - emailTemplateDefinition[VirtualFileEmailTemplateProviderContributor.VirtualFilePathKey] = path; - return emailTemplateDefinition; - } - - public static string GetVirtualFilePathOrNull(this EmailTemplateDefinition emailTemplateDefinition) - { - return emailTemplateDefinition[VirtualFileEmailTemplateProviderContributor.VirtualFilePathKey] as string; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateOptions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateOptions.cs deleted file mode 100644 index 0103ac27dc..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace Volo.Abp.Emailing.Templates.Virtual -{ - public class VirtualFileEmailTemplateOptions - { - public IDictionary Templates { get; } - - public VirtualFileEmailTemplateOptions() - { - Templates = new Dictionary(); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateProviderContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateProviderContributor.cs deleted file mode 100644 index d54f4c94cb..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateProviderContributor.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; -using Volo.Abp.VirtualFileSystem; - -namespace Volo.Abp.Emailing.Templates.Virtual -{ - public class VirtualFileEmailTemplateProviderContributor : IEmailTemplateProviderContributor - { - public const string VirtualFilePathKey = "VirtualFilePath"; - - public Task ProvideAsync(EmailTemplateProviderContributorContext contributorContext) - { - var templateDefinition = FindTemplateDefinition(contributorContext); - if (templateDefinition == null) - { - return Task.CompletedTask; - } - - var fileInfo = FindVirtualFileInfo(contributorContext, templateDefinition); - if (fileInfo == null) - { - return Task.CompletedTask; - } - - contributorContext.Template = new EmailTemplate(fileInfo.ReadAsString(), templateDefinition); - return Task.CompletedTask; - } - - protected virtual EmailTemplateDefinition FindTemplateDefinition(EmailTemplateProviderContributorContext contributorContext) - { - return contributorContext - .ServiceProvider - .GetRequiredService>() - .Value - .Templates - .GetOrDefault(contributorContext.Name); - } - - protected virtual IFileInfo FindVirtualFileInfo(EmailTemplateProviderContributorContext contributorContext, EmailTemplateDefinition templateDefinition) - { - var virtualFilePath = templateDefinition?.GetVirtualFilePathOrNull(); - if (virtualFilePath == null) - { - return null; - } - - var virtualFileProvider = contributorContext.ServiceProvider.GetRequiredService(); - - var fileInfo = virtualFileProvider.GetFileInfo(virtualFilePath); - if (fileInfo?.Exists != true) - { - return null; - } - - return fileInfo; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs new file mode 100644 index 0000000000..7bee611d28 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs @@ -0,0 +1,19 @@ +namespace Volo.Abp.Emailing.Templates.VirtualFiles +{ + public static class EmailTemplateDefinitionExtensions + { + public static EmailTemplateDefinition AddTemplateVirtualFile( + this EmailTemplateDefinition emailTemplateDefinition, string path) + { + emailTemplateDefinition.Contributors.Add(new SingleVirtualFileEmailTemplateContributor(path)); + return emailTemplateDefinition; + } + + public static EmailTemplateDefinition AddTemplateVirtualFiles( + this EmailTemplateDefinition emailTemplateDefinition, string path) + { + emailTemplateDefinition.Contributors.Add(new MultipleVirtualFilesEmailTemplateContributor(path)); + return emailTemplateDefinition; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs new file mode 100644 index 0000000000..2ae5f88cc5 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.Emailing.Templates.VirtualFiles +{ + public class MultipleVirtualFilesEmailTemplateContributor : IEmailTemplateContributor + { + private readonly string _virtualPath; + + private IVirtualFileProvider _virtualFileProvider; + + private Dictionary _templateDictionary; + + private readonly object _syncObj = new object(); + + public MultipleVirtualFilesEmailTemplateContributor(string virtualPath) + { + _virtualPath = virtualPath; + } + + public void Initialize(EmailTemplateInitializationContext context) + { + _virtualFileProvider = context.ServiceProvider.GetRequiredService(); + } + + public string GetOrNull(string cultureName) + { + return GetTemplateDictionary().GetOrDefault(cultureName); + } + + private Dictionary GetTemplateDictionary() + { + var dictionaries = _templateDictionary; + if (dictionaries != null) + { + return dictionaries; + } + + lock (_syncObj) + { + dictionaries = _templateDictionary; + if (dictionaries != null) + { + return dictionaries; + } + + _templateDictionary = new Dictionary(); + foreach (var file in _virtualFileProvider.GetDirectoryContents(_virtualPath)) + { + if (file.IsDirectory) + { + continue; + } + + // TODO: How to normalize file names? + _templateDictionary.Add(file.Name.RemovePostFix(".tpl"), file.ReadAsString()); + } + + dictionaries = _templateDictionary; + } + + return dictionaries; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs new file mode 100644 index 0000000000..d72d18e99a --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; +using Volo.Abp.VirtualFileSystem; + +namespace Volo.Abp.Emailing.Templates.VirtualFiles +{ + public class SingleVirtualFileEmailTemplateContributor : IEmailTemplateContributor + { + private readonly string _virtualPath; + + private IVirtualFileProvider _virtualFileProvider; + + public SingleVirtualFileEmailTemplateContributor(string virtualPath) + { + _virtualPath = virtualPath; + } + + public void Initialize(EmailTemplateInitializationContext context) + { + _virtualFileProvider = context.ServiceProvider.GetRequiredService(); + } + + public string GetOrNull(string cultureName) + { + var file = _virtualFileProvider.GetFileInfo(_virtualPath); + if (file == null || !file.Exists || file.IsDirectory) + { + return null; + } + + return file.ReadAsString(); + } + } +} \ 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 5b33186509..2f5cdfb4fb 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 @@ -18,7 +18,7 @@ - + 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 new file mode 100644 index 0000000000..cf6dad2b43 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo.Abp.EntityFrameworkCore.Sqlite.csproj @@ -0,0 +1,24 @@ + + + + + + netstandard2.0 + Volo.Abp.EntityFrameworkCore.Sqlite + Volo.Abp.EntityFrameworkCore.Sqlite + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextSqliteExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextSqliteExtensions.cs new file mode 100644 index 0000000000..6fd69fa42e --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextSqliteExtensions.cs @@ -0,0 +1,25 @@ +using System; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Volo.Abp.EntityFrameworkCore.DependencyInjection; + +namespace Volo.Abp.EntityFrameworkCore +{ + public static class AbpDbContextConfigurationContextSqliteExtensions + { + public static DbContextOptionsBuilder UseSqlite( + [NotNull] this AbpDbContextConfigurationContext context, + [CanBeNull] Action sqliteOptionsAction = null) + { + if (context.ExistingConnection != null) + { + return context.DbContextOptions.UseSqlite(context.ExistingConnection, sqliteOptionsAction); + } + else + { + return context.DbContextOptions.UseSqlite(context.ConnectionString, sqliteOptionsAction); + } + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsSqliteExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsSqliteExtensions.cs new file mode 100644 index 0000000000..68944194d1 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsSqliteExtensions.cs @@ -0,0 +1,30 @@ +using System; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Volo.Abp.EntityFrameworkCore +{ + public static class AbpDbContextOptionsSqliteExtensions + { + public static void UseSqlite( + [NotNull] this AbpDbContextOptions options, + [CanBeNull] Action sqliteOptionsAction = null) + { + options.Configure(context => + { + context.UseSqlite(sqliteOptionsAction); + }); + } + + public static void UseSqlite( + [NotNull] this AbpDbContextOptions options, + [CanBeNull] Action sqliteOptionsAction = null) + where TDbContext : AbpDbContext + { + options.Configure(context => + { + context.UseSqlite(sqliteOptionsAction); + }); + } + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs new file mode 100644 index 0000000000..8be6927e79 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Sqlite/Volo/Abp/EntityFrameworkCore/Sqlite/AbpEntityFrameworkCoreSqliteModule.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.EntityFrameworkCore.Sqlite +{ + [DependsOn( + typeof(AbpEntityFrameworkCoreModule) + )] + public class AbpEntityFrameworkCoreSqliteModule : AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj b/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj index 65dda178a5..bfbf0cdf49 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo.Abp.EntityFrameworkCore.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index fd7a3d495b..132e91c079 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -261,6 +261,22 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling } } + public static void ConfigureCreationAuditedAggregateRoot(this EntityTypeBuilder b) + where T : class + { + b.As().TryConfigureCreationAudited(); + b.As().TryConfigureExtraProperties(); + b.As().TryConfigureConcurrencyStamp(); + } + + public static void ConfigureAuditedAggregateRoot(this EntityTypeBuilder b) + where T : class + { + b.As().TryConfigureAudited(); + b.As().TryConfigureExtraProperties(); + b.As().TryConfigureConcurrencyStamp(); + } + public static void ConfigureFullAuditedAggregateRoot(this EntityTypeBuilder b) where T : class { diff --git a/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj index 12084177a9..fe8a6a4192 100644 --- a/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj +++ b/framework/src/Volo.Abp.FluentValidation/Volo.Abp.FluentValidation.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj b/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj index e418ca03e5..e4b9db1c7b 100644 --- a/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj +++ b/framework/src/Volo.Abp.HangFire/Volo.Abp.HangFire.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs b/framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs index e7b41784c7..91d1c4cb9b 100644 --- a/framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs +++ b/framework/src/Volo.Abp.Http.Client.IdentityModel/Volo/Abp/Http/Client/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs @@ -13,11 +13,12 @@ namespace Volo.Abp.Http.Client.IdentityModel { public IHttpContextAccessor HttpContextAccessor { get; set; } - protected IIdentityModelHttpClientAuthenticator IdentityModelHttpClientAuthenticator { get; } + protected IIdentityModelAuthenticationService IdentityModelAuthenticationService { get; } - public IdentityModelRemoteServiceHttpClientAuthenticator(IIdentityModelHttpClientAuthenticator identityModelHttpClientAuthenticator) + public IdentityModelRemoteServiceHttpClientAuthenticator( + IIdentityModelAuthenticationService identityModelAuthenticationService) { - IdentityModelHttpClientAuthenticator = identityModelHttpClientAuthenticator; + IdentityModelAuthenticationService = identityModelAuthenticationService; } public async Task Authenticate(RemoteServiceHttpClientAuthenticateContext context) @@ -32,7 +33,7 @@ namespace Volo.Abp.Http.Client.IdentityModel } } - await IdentityModelHttpClientAuthenticator.AuthenticateAsync( + await IdentityModelAuthenticationService.TryAuthenticateAsync( context.Client, context.RemoteService.GetIdentityClient() ); diff --git a/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj b/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj index d16fac5ba0..e608961fff 100644 --- a/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj +++ b/framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj @@ -14,8 +14,8 @@ - - + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/AbpIdentityModelModule.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/AbpIdentityModelModule.cs index cadeb794a7..b8578ca95d 100644 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/AbpIdentityModelModule.cs +++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/AbpIdentityModelModule.cs @@ -1,8 +1,12 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; +using Volo.Abp.Threading; namespace Volo.Abp.IdentityModel { + [DependsOn( + typeof(AbpThreadingModule) + )] public class AbpIdentityModelModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelAuthenticationService.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelAuthenticationService.cs new file mode 100644 index 0000000000..4acfac2ad7 --- /dev/null +++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelAuthenticationService.cs @@ -0,0 +1,17 @@ +using System.Net.Http; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Volo.Abp.IdentityModel +{ + //TODO: Re-consider this interface! + public interface IIdentityModelAuthenticationService + { + Task TryAuthenticateAsync( + [NotNull] HttpClient client, + string identityClientName = null); + + Task GetAccessTokenAsync( + IdentityClientConfiguration configuration); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelHttpClientAuthenticator.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelHttpClientAuthenticator.cs deleted file mode 100644 index 09a16735ff..0000000000 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IIdentityModelHttpClientAuthenticator.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.IdentityModel -{ - public interface IIdentityModelHttpClientAuthenticator - { - Task AuthenticateAsync(IdentityModelHttpClientAuthenticateContext context); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs index 9e891ccafa..e2e6e45e49 100644 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs +++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityClientConfiguration.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using IdentityModel; namespace Volo.Abp.IdentityModel @@ -71,23 +72,39 @@ namespace Volo.Abp.IdentityModel set => this[nameof(Scope)] = value; } + /// + /// RequireHttps. + /// Default: true. + /// + public bool RequireHttps + { + get => this.GetOrDefault(nameof(RequireHttps))?.To() ?? true; + set => this[nameof(RequireHttps)] = value.ToString().ToLowerInvariant(); + } + public IdentityClientConfiguration() { } public IdentityClientConfiguration( + string authority, + string scope, string clientId, string clientSecret, string grantType = OidcConstants.GrantTypes.ClientCredentials, string userName = null, - string userPassword = null) + string userPassword = null, + bool requireHttps = true) { + this[nameof(Authority)] = authority; + this[nameof(Scope)] = scope; this[nameof(ClientId)] = clientId; this[nameof(ClientSecret)] = clientSecret; this[nameof(GrantType)] = grantType; this[nameof(UserName)] = userName; this[nameof(UserPassword)] = userPassword; + this[nameof(RequireHttps)] = requireHttps.ToString().ToLowerInvariant(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs new file mode 100644 index 0000000000..032bd15457 --- /dev/null +++ b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs @@ -0,0 +1,148 @@ +using IdentityModel; +using IdentityModel.Client; +using JetBrains.Annotations; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Threading; + +namespace Volo.Abp.IdentityModel +{ + [Dependency(ReplaceServices = true)] + public class IdentityModelAuthenticationService : IIdentityModelAuthenticationService, ITransientDependency + { + public ILogger Logger { get; set; } + protected IdentityClientOptions ClientOptions { get; } + protected ICancellationTokenProvider CancellationTokenProvider { get; } + + public IdentityModelAuthenticationService( + IOptions options, + ICancellationTokenProvider cancellationTokenProvider) + { + CancellationTokenProvider = cancellationTokenProvider; + ClientOptions = options.Value; + Logger = NullLogger.Instance; + } + + public async Task TryAuthenticateAsync( + [NotNull] HttpClient client, + string identityClientName = null) + { + var accessToken = await GetAccessTokenOrNullAsync(identityClientName); + if (accessToken == null) + { + return false; + } + + SetAccessToken(client, accessToken); + return true; + + } + + protected virtual async Task GetAccessTokenOrNullAsync(string identityClientName) + { + var configuration = GetClientConfiguration(identityClientName); + if (configuration == null) + { + Logger.LogWarning($"Could not find {nameof(IdentityClientConfiguration)} for {identityClientName}. Either define a configuration for {identityClientName} or set a default configuration."); + return null; + } + + return await GetAccessTokenAsync(configuration); + } + + public virtual async Task GetAccessTokenAsync(IdentityClientConfiguration configuration) + { + var discoveryResponse = await GetDiscoveryResponse(configuration); + if (discoveryResponse.IsError) + { + throw new AbpException($"Could not retrieve the OpenId Connect discovery document! ErrorType: {discoveryResponse.ErrorType}. Error: {discoveryResponse.Error}"); + } + + var tokenResponse = await GetTokenResponse(discoveryResponse, configuration); + if (tokenResponse.IsError) + { + throw new AbpException($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}"); + } + + return tokenResponse.AccessToken; + } + + protected virtual void SetAccessToken(HttpClient client, string accessToken) + { + //TODO: "Bearer" should be configurable + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + } + + private IdentityClientConfiguration GetClientConfiguration(string identityClientName = null) + { + if (identityClientName.IsNullOrEmpty()) + { + return ClientOptions.IdentityClients.Default; + } + + return ClientOptions.IdentityClients.GetOrDefault(identityClientName) ?? + ClientOptions.IdentityClients.Default; + } + + protected virtual async Task GetDiscoveryResponse( + IdentityClientConfiguration configuration) + { + using (var httpClient = new HttpClient()) + { + return await httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest + { + Address = configuration.Authority, + Policy = + { + RequireHttps = configuration.RequireHttps + } + }); + } + } + + protected virtual async Task GetTokenResponse( + DiscoveryResponse discoveryResponse, + IdentityClientConfiguration configuration) + { + using (var httpClient = new HttpClient()) + { + switch (configuration.GrantType) + { + case OidcConstants.GrantTypes.ClientCredentials: + return await httpClient.RequestClientCredentialsTokenAsync( + new ClientCredentialsTokenRequest + { + Address = discoveryResponse.TokenEndpoint, + Scope = configuration.Scope, + ClientId = configuration.ClientId, + ClientSecret = configuration.ClientSecret + }, + CancellationTokenProvider.Token + ); + case OidcConstants.GrantTypes.Password: + return await httpClient.RequestPasswordTokenAsync( + new PasswordTokenRequest + { + Address = discoveryResponse.TokenEndpoint, + Scope = configuration.Scope, + ClientId = configuration.ClientId, + ClientSecret = configuration.ClientSecret, + UserName = configuration.UserName, + Password = configuration.UserPassword + }, + CancellationTokenProvider.Token + ); + default: + throw new AbpException("Grant type was not implemented: " + configuration.GrantType); + } + } + } + } +} diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticateContext.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticateContext.cs deleted file mode 100644 index 441129aee7..0000000000 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticateContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Net.Http; -using JetBrains.Annotations; - -namespace Volo.Abp.IdentityModel -{ - public class IdentityModelHttpClientAuthenticateContext - { - public HttpClient Client { get; } - - /// - /// The identity client name configured with the . - /// - public string IdentityClientName { get; } - - /// object to be authenticated - /// The identity client name configured with the . - public IdentityModelHttpClientAuthenticateContext( - [NotNull] HttpClient client, - string identityClientName = null) - { - Client = Check.NotNull(client, nameof(client)); - IdentityClientName = identityClientName; - } - } -} diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticatorExtensions.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticatorExtensions.cs deleted file mode 100644 index c40b0d8398..0000000000 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelHttpClientAuthenticatorExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Net.Http; -using System.Threading.Tasks; -using JetBrains.Annotations; - -namespace Volo.Abp.IdentityModel -{ - public static class IdentityModelHttpClientAuthenticatorExtensions - { - /// Authenticator object - /// object to be authenticated - /// The identity client name configured with the . - public static Task AuthenticateAsync( - [NotNull] this IIdentityModelHttpClientAuthenticator authenticator, - [NotNull] HttpClient client, - string identityClientName = null) - { - Check.NotNull(authenticator, nameof(authenticator)); - - return authenticator.AuthenticateAsync( - new IdentityModelHttpClientAuthenticateContext( - client, - identityClientName - ) - ); - } - } -} diff --git a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs b/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs deleted file mode 100644 index ad956aee1e..0000000000 --- a/framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelRemoteServiceHttpClientAuthenticator.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using IdentityModel; -using IdentityModel.Client; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.IdentityModel -{ - [Dependency(ReplaceServices = true)] - public class IdentityModelHttpClientAuthenticator : IIdentityModelHttpClientAuthenticator, ITransientDependency - { - public ILogger Logger { get; set; } - protected IdentityClientOptions ClientOptions { get; } - - public IdentityModelHttpClientAuthenticator( - IOptions options) - { - ClientOptions = options.Value; - Logger = NullLogger.Instance; - } - - public async Task AuthenticateAsync(IdentityModelHttpClientAuthenticateContext context) - { - var accessToken = await GetAccessTokenFromServerOrNullAsync(context); - - if (accessToken != null) - { - //TODO: "Bearer" should be configurable - context.Client.DefaultRequestHeaders.Authorization - = new AuthenticationHeaderValue("Bearer", accessToken); - } - } - - protected virtual async Task GetAccessTokenFromServerOrNullAsync(IdentityModelHttpClientAuthenticateContext context) - { - //TODO: Better logging - - var configuration = GetClientConfiguration(context); - - if (configuration == null) - { - Logger.LogWarning($"Could not find {nameof(IdentityClientConfiguration)} for {context.IdentityClientName}. Either define a configuration for {context.IdentityClientName} or set a default configuration."); - return null; - } - - var discoveryResponse = await GetDiscoveryResponse(configuration); - if (discoveryResponse.IsError) - { - Logger.LogError($"Could not retrieve the OpenId Connect discovery document! ErrorType: {discoveryResponse.ErrorType}. Error: {discoveryResponse.Error}"); - return null; - } - - var tokenResponse = await GetTokenResponse(discoveryResponse, configuration); - if (tokenResponse.IsError) - { - Logger.LogError($"Could not get token from the OpenId Connect server! ErrorType: {tokenResponse.ErrorType}. Error: {tokenResponse.Error}. ErrorDescription: {tokenResponse.ErrorDescription}. HttpStatusCode: {tokenResponse.HttpStatusCode}"); - return null; - } - - return tokenResponse.AccessToken; - } - - private IdentityClientConfiguration GetClientConfiguration(IdentityModelHttpClientAuthenticateContext context) - { - if (context.IdentityClientName.IsNullOrEmpty()) - { - return ClientOptions.IdentityClients.Default; - } - - return ClientOptions.IdentityClients.GetOrDefault(context.IdentityClientName) ?? - ClientOptions.IdentityClients.Default; - } - - protected virtual async Task GetDiscoveryResponse(IdentityClientConfiguration configuration) - { - return await DiscoveryClient.GetAsync(configuration.Authority); - } - - protected virtual async Task GetTokenResponse(DiscoveryResponse discoveryResponse, IdentityClientConfiguration configuration) - { - //TODO: Pass cancellation token - - var tokenClient = new TokenClient(discoveryResponse.TokenEndpoint, configuration.ClientId, configuration.ClientSecret); - - switch (configuration.GrantType) - { - case OidcConstants.GrantTypes.ClientCredentials: - return await tokenClient.RequestClientCredentialsAsync( - configuration.Scope - ); - case OidcConstants.GrantTypes.Password: - return await tokenClient.RequestResourceOwnerPasswordAsync( - configuration.UserName, - configuration.UserPassword, - configuration.Scope - ); - default: - throw new AbpException("Grant type was not implemented: " + configuration.GrantType); - } - } - } -} diff --git a/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj b/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj index 39b92cc428..8964f2f4fe 100644 --- a/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj +++ b/framework/src/Volo.Abp.Json/Volo.Abp.Json.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj b/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj index b429defa2d..65ceed101e 100644 --- a/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj +++ b/framework/src/Volo.Abp.Localization/Volo.Abp.Localization.csproj @@ -18,7 +18,7 @@ - + diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpValidation/cs.json b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpValidation/cs.json new file mode 100644 index 0000000000..d38574303a --- /dev/null +++ b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpValidation/cs.json @@ -0,0 +1,20 @@ +{ + "culture": "cs", + "texts": { + "'{0}' and '{1}' do not match.": "'{0}' a '{1}' se neshodují", + "The {0} field is not a valid credit card number.": "V poli {0} není platné číslo kreditní karty.", + "{0} is not valid.": "{0} není platný.", + "The {0} field is not a valid e-mail address.": "V poli {0} není platný email.", + "The {0} field only accepts files with the following extensions: {1}": "Pole {0} přijímá soubory pouze s následujícími koncovkami: {1}", + "The field {0} must be a string or array type with a maximum length of '{1}'.": "Vy poli {0} musí být řežezec nebo řada o maximální délce '{1}'.", + "The field {0} must be a string or array type with a minimum length of '{1}'.": "V poli {0} musí být řežezec nebo řada o minimální délce '{1}'.", + "The {0} field is not a valid phone number.": "V poli {0} není platné telefonní číslo.", + "The field {0} must be between {1} and {2}.": "Pole {0} musí být mezi {1} a {2}.", + "The field {0} must match the regular expression '{1}'.": "Pole {0} musí odpovídat regulérnímu výrazu '{1}'.", + "The {0} field is required.": "Pole {0} je povinné.", + "The field {0} must be a string with a maximum length of {1}.": "Pole {0} musí být řetězec o maximální délce {1}.", + "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "Pole {0} musí být řetězec o minimální délce {2} a maximální délce {1} znaků.", + "The {0} field is not a valid fully-qualified http, https, or ftp URL.": "Pole {0} není platná plně kvalifikovaná adresa http, https, nebo ftp URL.", + "The field {0} is invalid.": "Pole {0} je neplatné." + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabase.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabase.cs index 1e6cd6f54c..9f2bf33a40 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabase.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabase.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; +using Volo.Abp.Domain.Entities; namespace Volo.Abp.Domain.Repositories.MemoryDb { public interface IMemoryDatabase { - List Collection(); + IMemoryDatabaseCollection Collection() where TEntity : class, IEntity; TKey GenerateNextId(); } diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabaseCollection.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabaseCollection.cs new file mode 100644 index 0000000000..a2f4d5f270 --- /dev/null +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDatabaseCollection.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Volo.Abp.Domain.Repositories.MemoryDb +{ + public interface IMemoryDatabaseCollection : IEnumerable + { + void Add(TEntity entity); + + void Update(TEntity entity); + + void Remove(TEntity entity); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbRepository.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbRepository.cs index dd241b25a5..c179b68931 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbRepository.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbRepository.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb { IMemoryDatabase Database { get; } - List Collection { get; } + IMemoryDatabaseCollection Collection { get; } } public interface IMemoryDbRepository : IMemoryDbRepository, IRepository diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbSerializer.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbSerializer.cs new file mode 100644 index 0000000000..2854a7893f --- /dev/null +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/IMemoryDbSerializer.cs @@ -0,0 +1,11 @@ +using System; + +namespace Volo.Abp.Domain.Repositories.MemoryDb +{ + public interface IMemoryDbSerializer + { + byte[] Serialize(object obj); + + object Deserialize(byte[] value, Type type); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabase.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabase.cs index be16beef85..6c2aa1417c 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabase.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabase.cs @@ -1,24 +1,33 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities; namespace Volo.Abp.Domain.Repositories.MemoryDb { - public class MemoryDatabase : IMemoryDatabase + public class MemoryDatabase : IMemoryDatabase, ITransientDependency { private readonly ConcurrentDictionary _sets; private readonly ConcurrentDictionary _entityIdGenerators; - public MemoryDatabase() + private readonly IServiceProvider _serviceProvider; + + public MemoryDatabase(IServiceProvider serviceProvider) { + _serviceProvider = serviceProvider; _sets = new ConcurrentDictionary(); _entityIdGenerators = new ConcurrentDictionary(); } - public List Collection() + public IMemoryDatabaseCollection Collection() + where TEntity : class, IEntity { - return _sets.GetOrAdd(typeof(TEntity), _ => new List()) as List; + return _sets.GetOrAdd(typeof(TEntity), + _ => _serviceProvider.GetRequiredService>()) as + IMemoryDatabaseCollection; } public TKey GenerateNextId() diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs new file mode 100644 index 0000000000..11c7f992a9 --- /dev/null +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseCollection.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Volo.Abp.Domain.Entities; + +namespace Volo.Abp.Domain.Repositories.MemoryDb +{ + public class MemoryDatabaseCollection : IMemoryDatabaseCollection + where TEntity : class, IEntity + { + private readonly Dictionary _dictionary = new Dictionary(); + + private readonly IMemoryDbSerializer _memoryDbSerializer; + + public MemoryDatabaseCollection(IMemoryDbSerializer memoryDbSerializer) + { + _memoryDbSerializer = memoryDbSerializer; + } + + public IEnumerator GetEnumerator() + { + foreach (var entity in _dictionary.Values) + { + yield return _memoryDbSerializer.Deserialize(entity, typeof(TEntity)).As(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(TEntity entity) + { + _dictionary.Add(GetEntityKey(entity), _memoryDbSerializer.Serialize(entity)); + } + + public void Update(TEntity entity) + { + if (_dictionary.ContainsKey(GetEntityKey(entity))) + { + _dictionary[GetEntityKey(entity)] = _memoryDbSerializer.Serialize(entity); + } + } + + public void Remove(TEntity entity) + { + _dictionary.Remove(GetEntityKey(entity)); + } + + private string GetEntityKey(TEntity entity) + { + return entity.GetKeys().JoinAsString(","); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseManager.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseManager.cs index 4926abebf3..7507f3d625 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseManager.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDatabaseManager.cs @@ -1,19 +1,25 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.Domain.Repositories.MemoryDb { - public static class MemoryDatabaseManager + public class MemoryDatabaseManager : ISingletonDependency { - private static ConcurrentDictionary _databases; + private readonly ConcurrentDictionary _databases = + new ConcurrentDictionary(); - static MemoryDatabaseManager() + private readonly IServiceProvider _serviceProvider; + + public MemoryDatabaseManager(IServiceProvider serviceProvider) { - _databases = new ConcurrentDictionary(); + _serviceProvider = serviceProvider; } - public static IMemoryDatabase Get(string databaseName) + public IMemoryDatabase Get(string databaseName) { - return _databases.GetOrAdd(databaseName, _ => new MemoryDatabase()); + return _databases.GetOrAdd(databaseName, _ => _serviceProvider.GetRequiredService()); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs index c658a34ef4..0f82aa798b 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs @@ -14,7 +14,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb { //TODO: Add dbcontext just like mongodb implementation! - public virtual List Collection => Database.Collection(); + public virtual IMemoryDatabaseCollection Collection => Database.Collection(); public virtual IMemoryDatabase Database => DatabaseProvider.GetDatabase(); @@ -33,6 +33,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public override TEntity Update(TEntity entity, bool autoSave = false) { + Collection.Update(entity); return entity; } @@ -48,7 +49,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public override long GetCount() { - return Collection.Count; + return Collection.Count(); } protected override IQueryable GetQueryable() diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/Utf8JsonMemoryDbSerializer.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/Utf8JsonMemoryDbSerializer.cs new file mode 100644 index 0000000000..9964e6219b --- /dev/null +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/Utf8JsonMemoryDbSerializer.cs @@ -0,0 +1,27 @@ +using System; +using System.Text; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Json; + +namespace Volo.Abp.Domain.Repositories.MemoryDb +{ + public class Utf8JsonMemoryDbSerializer : IMemoryDbSerializer, ITransientDependency + { + private readonly IJsonSerializer _jsonSerializer; + + public Utf8JsonMemoryDbSerializer(IJsonSerializer jsonSerializer) + { + _jsonSerializer = jsonSerializer; + } + + byte[] IMemoryDbSerializer.Serialize(object obj) + { + return Encoding.UTF8.GetBytes(_jsonSerializer.Serialize(obj)); + } + + public object Deserialize(byte[] value, Type type) + { + return _jsonSerializer.Deserialize(type, Encoding.UTF8.GetString(value)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDbCoreRepositoryExtensions.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDbCoreRepositoryExtensions.cs index 6f940ca146..1547a581e5 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDbCoreRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDbCoreRepositoryExtensions.cs @@ -13,7 +13,7 @@ namespace Volo.Abp.Domain.Repositories return repository.ToMemoryDbRepository().Database; } - public static List GetCollection(this IBasicRepository repository) + public static IMemoryDatabaseCollection GetCollection(this IBasicRepository repository) where TEntity : class, IEntity { return repository.ToMemoryDbRepository().Collection; diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs index d4f679a5b7..bd8b4b98a4 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/MemoryDb/AbpMemoryDbModule.cs @@ -1,19 +1,19 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Domain; +using Volo.Abp.Domain.Repositories.MemoryDb; +using Volo.Abp.Json; using Volo.Abp.Modularity; using Volo.Abp.Uow.MemoryDb; namespace Volo.Abp.MemoryDb { - /* TODO: Consider to store objects as binary serialized in the memory, which makes unit tests more realistic. - */ - [DependsOn(typeof(AbpDddDomainModule))] public class AbpMemoryDbModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.TryAddTransient(typeof(IMemoryDatabaseProvider<>), typeof(UnitOfWorkMemoryDatabaseProvider<>)); + context.Services.TryAddTransient(typeof(IMemoryDatabaseCollection<>), typeof(MemoryDatabaseCollection<>)); } } } diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Uow/MemoryDb/UnitOfWorkMemoryDatabaseProvider.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Uow/MemoryDb/UnitOfWorkMemoryDatabaseProvider.cs index 386ec77b12..24c1cdaeb9 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Uow/MemoryDb/UnitOfWorkMemoryDatabaseProvider.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Uow/MemoryDb/UnitOfWorkMemoryDatabaseProvider.cs @@ -11,15 +11,18 @@ namespace Volo.Abp.Uow.MemoryDb private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IConnectionStringResolver _connectionStringResolver; + private readonly MemoryDatabaseManager _memoryDatabaseManager; public UnitOfWorkMemoryDatabaseProvider( IUnitOfWorkManager unitOfWorkManager, IConnectionStringResolver connectionStringResolver, - TMemoryDbContext dbContext) + TMemoryDbContext dbContext, + MemoryDatabaseManager memoryDatabaseManager) { _unitOfWorkManager = unitOfWorkManager; _connectionStringResolver = connectionStringResolver; DbContext = dbContext; + _memoryDatabaseManager = memoryDatabaseManager; } public IMemoryDatabase GetDatabase() @@ -36,7 +39,7 @@ namespace Volo.Abp.Uow.MemoryDb var databaseApi = unitOfWork.GetOrAddDatabaseApi( dbContextKey, () => new MemoryDbDatabaseApi( - MemoryDatabaseManager.Get(connectionString) + _memoryDatabaseManager.Get(connectionString) )); return ((MemoryDbDatabaseApi)databaseApi).Database; diff --git a/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj b/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj index f2f59a9a64..9492e6fbf3 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj +++ b/framework/src/Volo.Abp.MongoDB/Volo.Abp.MongoDB.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs index 991db17cef..50155c6df5 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs @@ -1,4 +1,5 @@ using MongoDB.Driver; +using MongoDB.Driver.Linq; using Volo.Abp.Domain.Entities; namespace Volo.Abp.Domain.Repositories.MongoDB @@ -9,6 +10,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB IMongoDatabase Database { get; } IMongoCollection Collection { get; } + + IMongoQueryable GetMongoQueryable(); } public interface IMongoDbRepository : IMongoDbRepository, IRepository diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index 194f604847..2e993ebecb 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -263,12 +263,10 @@ namespace Volo.Abp.Domain.Repositories.MongoDB protected override IQueryable GetQueryable() { - return ApplyDataFilters( - Collection.AsQueryable() - ); + return GetMongoQueryable(); } - protected virtual IMongoQueryable GetMongoQueryable() + public virtual IMongoQueryable GetMongoQueryable() { return ApplyDataFilters( Collection.AsQueryable() diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs index b33a8d8444..12da0afaa2 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs @@ -1,5 +1,6 @@ using System; using MongoDB.Driver; +using MongoDB.Driver.Linq; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories.MongoDB; @@ -19,6 +20,12 @@ namespace Volo.Abp.Domain.Repositories return repository.ToMongoDbRepository().Collection; } + public static IMongoQueryable GetMongoQueryable(this IBasicRepository repository) + where TEntity : class, IEntity + { + return repository.ToMongoDbRepository().GetMongoQueryable(); + } + public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) where TEntity : class, IEntity { diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs index f53e524914..a450a4bc43 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpBsonClassMapExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using MongoDB.Bson.Serialization; using Volo.Abp.Data; @@ -9,34 +10,50 @@ namespace Volo.Abp.MongoDB public static void ConfigureAbpConventions(this BsonClassMap map) { map.AutoMap(); - map.ConfigureExtraProperties(); + map.TryConfigureExtraProperties(); } - public static void ConfigureExtraProperties(this BsonClassMap map) + public static bool TryConfigureExtraProperties(this BsonClassMap map) where TEntity : class, IHasExtraProperties { + var property = map.ClassType.GetProperty( + nameof(IHasExtraProperties.ExtraProperties), + BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty + ); + + if (property?.DeclaringType != map.ClassType) + { + return false; + } + map.SetExtraElementsMember(new BsonMemberMap( map, typeof(TEntity).GetMember(nameof(IHasExtraProperties.ExtraProperties))[0]) ); + + return true; } - /// - /// Configures SetExtraElementsMember if the - /// implements the interface. - /// Otherwise, does nothing - /// - public static void ConfigureExtraProperties(this BsonClassMap map) + public static bool TryConfigureExtraProperties(this BsonClassMap map) { - if (map.ClassType.IsAssignableTo()) + if (!map.ClassType.IsAssignableTo()) { - map.SetExtraElementsMember( - new BsonMemberMap( - map, - map.ClassType.GetMember(nameof(IHasExtraProperties.ExtraProperties))[0] - ) - ); + return false; } + + var property = map.ClassType.GetProperty( + nameof(IHasExtraProperties.ExtraProperties), + BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty + ); + + if (property?.DeclaringType != map.ClassType) + { + return false; + } + + map.MapExtraElementsMember(property); + + return true; } } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs index 788d0bdbea..43ffbabf4a 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/MongoModelBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Volo.Abp.Domain.Entities; namespace Volo.Abp.MongoDB { @@ -19,24 +20,40 @@ namespace Volo.Abp.MongoDB public MongoDbContextModel Build() { - var entityModels = _entityModelBuilders - .Select(x => x.Value) - .Cast() - .ToImmutableDictionary(x => x.EntityType, x => x); - - foreach (var entityModel in entityModels.Values) + lock (SyncObj) { - var map = entityModel.As().GetMap(); - lock (SyncObj) + var entityModels = _entityModelBuilders + .Select(x => x.Value) + .Cast() + .ToImmutableDictionary(x => x.EntityType, x => x); + + var baseClasses = new List(); + + foreach (var entityModel in entityModels.Values) { + var map = entityModel.As().GetMap(); if (!BsonClassMap.IsClassMapRegistered(map.ClassType)) { BsonClassMap.RegisterClassMap(map); } + + baseClasses.AddRange(entityModel.EntityType.GetBaseClasses(includeObject: false)); } - } - return new MongoDbContextModel(entityModels); + baseClasses = baseClasses.Distinct().ToList(); + + foreach (var baseClass in baseClasses) + { + if (!BsonClassMap.IsClassMapRegistered(baseClass)) + { + var map = new BsonClassMap(baseClass); + map.ConfigureAbpConventions(); + BsonClassMap.RegisterClassMap(map); + } + } + + return new MongoDbContextModel(entityModels); + } } public virtual void Entity(Action> buildAction = null) diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionOptions.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionOptions.cs index 91768d37e8..67eae167c3 100644 --- a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionOptions.cs +++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionOptions.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.Security.Encryption /// /// Default password to encrypt/decrypt texts. - /// It's recommented to set to another value for security. + /// It's recommended to set to another value for security. /// Default value: "gsKnGZ041HLL4IM8" /// public string DefaultPassPhrase { get; set; } diff --git a/framework/src/Volo.Abp.Specifications/Volo.Abp.Specifications.csproj b/framework/src/Volo.Abp.Specifications/Volo.Abp.Specifications.csproj new file mode 100644 index 0000000000..97c236cfc5 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo.Abp.Specifications.csproj @@ -0,0 +1,20 @@ + + + + + + netstandard2.0 + Volo.Abp.Specifications + Volo.Abp.Specifications + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AbpSpecificationsModule.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AbpSpecificationsModule.cs new file mode 100644 index 0000000000..6f55973be1 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AbpSpecificationsModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.Specifications +{ + public class AbpSpecificationsModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AndNotSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AndNotSpecification.cs new file mode 100644 index 0000000000..e0d51bd5c3 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AndNotSpecification.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the combined specification which indicates that the first specification + /// can be satisifed by the given object whereas the second one cannot. + /// + /// The type of the object to which the specification is applied. + public class AndNotSpecification : CompositeSpecification + { + /// + /// Constructs a new instance of class. + /// + /// The first specification. + /// The second specification. + public AndNotSpecification(ISpecification left, ISpecification right) : base(left, right) + { + } + + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public override Expression> ToExpression() + { + var rightExpression = Right.ToExpression(); + + var bodyNot = Expression.Not(rightExpression.Body); + var bodyNotExpression = Expression.Lambda>(bodyNot, rightExpression.Parameters); + + return Left.ToExpression().And(bodyNotExpression); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AndSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AndSpecification.cs new file mode 100644 index 0000000000..621287d634 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AndSpecification.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the combined specification which indicates that both of the given + /// specifications should be satisfied by the given object. + /// + /// The type of the object to which the specification is applied. + public class AndSpecification : CompositeSpecification + { + /// + /// Constructs a new instance of class. + /// + /// The first specification. + /// The second specification. + public AndSpecification(ISpecification left, ISpecification right) : base(left, right) + { + } + + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public override Expression> ToExpression() + { + return Left.ToExpression().And(Right.ToExpression()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AnySpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AnySpecification.cs new file mode 100644 index 0000000000..caf6a5f094 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/AnySpecification.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the specification that can be satisfied by the given object + /// in any circumstance. + /// + /// The type of the object to which the specification is applied. + public sealed class AnySpecification : Specification + { + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public override Expression> ToExpression() + { + return o => true; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/CompositeSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/CompositeSpecification.cs new file mode 100644 index 0000000000..01c1e46d33 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/CompositeSpecification.cs @@ -0,0 +1,30 @@ +namespace Volo.Abp.Specifications +{ + /// + /// Represents the base class for composite specifications. + /// + /// The type of the object to which the specification is applied. + public abstract class CompositeSpecification : Specification, ICompositeSpecification + { + /// + /// Constructs a new instance of class. + /// + /// The first specification. + /// The second specification. + protected CompositeSpecification(ISpecification left, ISpecification right) + { + Left = left; + Right = right; + } + + /// + /// Gets the first specification. + /// + public ISpecification Left { get; } + + /// + /// Gets the second specification. + /// + public ISpecification Right { get; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ExpressionFuncExtender.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ExpressionFuncExtender.cs new file mode 100644 index 0000000000..dd169d5320 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ExpressionFuncExtender.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the extender for Expression[Func[T, bool]] type. + /// This is part of the solution which solves + /// the expression parameter problem when going to Entity Framework. + /// For more information about this solution please refer to http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx. + /// + public static class ExpressionFuncExtender + { + private static Expression Compose(this Expression first, Expression second, + Func merge) + { + // build parameter map (from parameters of second to parameters of first) + var map = first.Parameters.Select((f, i) => new {f, s = second.Parameters[i]}) + .ToDictionary(p => p.s, p => p.f); + + // replace parameters in the second lambda expression with parameters from the first + var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); + + // apply composition of lambda expression bodies to parameters from the first expression + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } + + /// + /// Combines two given expressions by using the AND semantics. + /// + /// The type of the object. + /// The first part of the expression. + /// The second part of the expression. + /// The combined expression. + public static Expression> And(this Expression> first, + Expression> second) + { + return first.Compose(second, Expression.AndAlso); + } + + /// + /// Combines two given expressions by using the OR semantics. + /// + /// The type of the object. + /// The first part of the expression. + /// The second part of the expression. + /// The combined expression. + public static Expression> Or(this Expression> first, + Expression> second) + { + return first.Compose(second, Expression.OrElse); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ExpressionSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ExpressionSpecification.cs new file mode 100644 index 0000000000..14c1bff040 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ExpressionSpecification.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the specification which is represented by the corresponding + /// LINQ expression. + /// + /// The type of the object to which the specification is applied. + public class ExpressionSpecification : Specification + { + private readonly Expression> _expression; + + /// + /// Initializes a new instance of ExpressionSpecification<T> class. + /// + /// The LINQ expression which represents the current + /// specification. + public ExpressionSpecification(Expression> expression) + { + _expression = expression; + } + + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public override Expression> ToExpression() + { + return _expression; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ICompositeSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ICompositeSpecification.cs new file mode 100644 index 0000000000..2c73409d34 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ICompositeSpecification.cs @@ -0,0 +1,19 @@ +namespace Volo.Abp.Specifications +{ + /// + /// Represents that the implemented classes are composite specifications. + /// + /// The type of the object to which the specification is applied. + public interface ICompositeSpecification : ISpecification + { + /// + /// Gets the left side of the specification. + /// + ISpecification Left { get; } + + /// + /// Gets the right side of the specification. + /// + ISpecification Right { get; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ISpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ISpecification.cs new file mode 100644 index 0000000000..d19e28347e --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ISpecification.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents that the implemented classes are specifications. For more + /// information about the specification pattern, please refer to + /// http://martinfowler.com/apsupp/spec.pdf. + /// + /// The type of the object to which the specification is applied. + public interface ISpecification + { + /// + /// Returns a value which indicates whether the specification + /// is satisfied by the given object. + /// + /// The object to which the specification is applied. + /// True if the specification is satisfied, otherwise false. + bool IsSatisfiedBy(T obj); + + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + Expression> ToExpression(); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ISpecificationParser.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ISpecificationParser.cs new file mode 100644 index 0000000000..d2c61034e0 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ISpecificationParser.cs @@ -0,0 +1,19 @@ +namespace Volo.Abp.Specifications +{ + /// + /// Represents that the implemented classes are specification parsers that + /// parses the given specification to a domain specific criteria object, such + /// as the ICriteria instance in NHibernate. + /// + /// The type of the domain specific criteria. + public interface ISpecificationParser + { + /// + /// Parses the given specification to a domain specific criteria object. + /// + /// The type of the object to which the specification is applied. + /// The specified specification instance. + /// The instance of the domain specific criteria. + TCriteria Parse(ISpecification specification); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/NoneSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/NoneSpecification.cs new file mode 100644 index 0000000000..fc2e74fdd6 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/NoneSpecification.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the specification that can be satisfied by the given object + /// in no circumstance. + /// + /// The type of the object to which the specification is applied. + public sealed class NoneSpecification : Specification + { + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public override Expression> ToExpression() + { + return o => false; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/NotSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/NotSpecification.cs new file mode 100644 index 0000000000..34658885cb --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/NotSpecification.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the specification which indicates the semantics opposite to the given specification. + /// + /// The type of the object to which the specification is applied. + public class NotSpecification : Specification + { + private readonly ISpecification _specification; + + /// + /// Initializes a new instance of class. + /// + /// The specification to be reversed. + public NotSpecification(ISpecification specification) + { + _specification = specification; + } + + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public override Expression> ToExpression() + { + var expression = _specification.ToExpression(); + return Expression.Lambda>( + Expression.Not(expression.Body), + expression.Parameters + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/OrSpecification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/OrSpecification.cs new file mode 100644 index 0000000000..a6c85f8326 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/OrSpecification.cs @@ -0,0 +1,31 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the combined specification which indicates that either of the given + /// specification should be satisfied by the given object. + /// + /// The type of the object to which the specification is applied. + public class OrSpecification : CompositeSpecification + { + /// + /// Initializes a new instance of class. + /// + /// The first specification. + /// The second specification. + public OrSpecification(ISpecification left, ISpecification right) : base(left, right) + { + } + + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public override Expression> ToExpression() + { + return Left.ToExpression().Or(Right.ToExpression()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ParameterRebinder.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ParameterRebinder.cs new file mode 100644 index 0000000000..603e80cf69 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/ParameterRebinder.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the parameter rebinder used for rebinding the parameters + /// for the given expressions. This is part of the solution which solves + /// the expression parameter problem when going to Entity Framework. + /// For more information about this solution please refer to http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx. + /// + internal class ParameterRebinder : ExpressionVisitor + { + private readonly Dictionary _map; + + internal ParameterRebinder(Dictionary map) + { + _map = map ?? new Dictionary(); + } + + internal static Expression ReplaceParameters(Dictionary map, + Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } + + protected override Expression VisitParameter(ParameterExpression p) + { + if (_map.TryGetValue(p, out var replacement)) + { + p = replacement; + } + + return base.VisitParameter(p); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/Specification.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/Specification.cs new file mode 100644 index 0000000000..b1ab72caea --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/Specification.cs @@ -0,0 +1,38 @@ +using System; +using System.Linq.Expressions; + +namespace Volo.Abp.Specifications +{ + /// + /// Represents the base class for specifications. + /// + /// The type of the object to which the specification is applied. + public abstract class Specification : ISpecification + { + /// + /// Returns a value which indicates whether the specification + /// is satisfied by the given object. + /// + /// The object to which the specification is applied. + /// True if the specification is satisfied, otherwise false. + public virtual bool IsSatisfiedBy(T obj) + { + return ToExpression().Compile()(obj); + } + + /// + /// Gets the LINQ expression which represents the current specification. + /// + /// The LINQ expression. + public abstract Expression> ToExpression(); + + /// + /// Implicitly converts a specification to expression. + /// + /// + public static implicit operator Expression>(Specification specification) + { + return specification.ToExpression(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/SpecificationExtensions.cs b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/SpecificationExtensions.cs new file mode 100644 index 0000000000..229b3e7a72 --- /dev/null +++ b/framework/src/Volo.Abp.Specifications/Volo/Abp/Specifications/SpecificationExtensions.cs @@ -0,0 +1,72 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.Specifications +{ + public static class SpecificationExtensions + { + /// + /// Combines the current specification instance with another specification instance + /// and returns the combined specification which represents that both the current and + /// the given specification must be satisfied by the given object. + /// + /// The specification + /// The specification instance with which the current specification is combined. + /// The combined specification instance. + public static ISpecification And([NotNull] this ISpecification specification, + [NotNull] ISpecification other) + { + Check.NotNull(specification, nameof(specification)); + Check.NotNull(other, nameof(other)); + + return new AndSpecification(specification, other); + } + + /// + /// Combines the current specification instance with another specification instance + /// and returns the combined specification which represents that either the current or + /// the given specification should be satisfied by the given object. + /// + /// The specification + /// The specification instance with which the current specification + /// is combined. + /// The combined specification instance. + public static ISpecification Or([NotNull] this ISpecification specification, + [NotNull] ISpecification other) + { + Check.NotNull(specification, nameof(specification)); + Check.NotNull(other, nameof(other)); + + return new OrSpecification(specification, other); + } + + /// + /// Combines the current specification instance with another specification instance + /// and returns the combined specification which represents that the current specification + /// should be satisfied by the given object but the specified specification should not. + /// + /// The specification + /// The specification instance with which the current specification + /// is combined. + /// The combined specification instance. + public static ISpecification AndNot([NotNull] this ISpecification specification, + [NotNull] ISpecification other) + { + Check.NotNull(specification, nameof(specification)); + Check.NotNull(other, nameof(other)); + + return new AndNotSpecification(specification, other); + } + + /// + /// Reverses the current specification instance and returns a specification which represents + /// the semantics opposite to the current specification. + /// + /// The reversed specification instance. + public static ISpecification Not([NotNull] this ISpecification specification) + { + Check.NotNull(specification, nameof(specification)); + + return new NotSpecification(specification); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/DisableDateTimeNormalizationAttribute.cs b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/DisableDateTimeNormalizationAttribute.cs new file mode 100644 index 0000000000..6e9811b538 --- /dev/null +++ b/framework/src/Volo.Abp.Timing/Volo/Abp/Timing/DisableDateTimeNormalizationAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace Volo.Abp.Timing +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Parameter)] + public class DisableDateTimeNormalizationAttribute : Attribute + { + + } +} 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 new file mode 100644 index 0000000000..b2c46465c0 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "Menu:Administration": "Administrace" + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pt-BR.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pt-BR.json new file mode 100644 index 0000000000..d8eacbc96d --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pt-BR.json @@ -0,0 +1,6 @@ +{ + "culture": "pt-BR", + "texts": { + "Menu:Administration": "Administração" + } +} \ 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 new file mode 100644 index 0000000000..b67fc568b3 --- /dev/null +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json @@ -0,0 +1,51 @@ +{ + "culture": "cs", + "texts": { + "InternalServerErrorMessage": "Během požadavku se vyskytla vnitřní chyba!", + "ValidationErrorMessage": "Neplatný požadavek!", + "ValidationNarrativeErrorMessageTitle": "Během ověřování byly zjištěny následující chyby.", + "DefaultErrorMessage": "Vyskytla se chyba!", + "DefaultErrorMessageDetail": "Server neposkytl detaily k chybě.", + "DefaultErrorMessage401": "Nejste přihlášeni!", + "DefaultErrorMessage401Detail": "K vykonání této operace se musíte nejprve přihlásit.", + "DefaultErrorMessage403": "Nemáte oprávnění!", + "DefaultErrorMessage403Detail": "Není vám dovoleno vykonat tuto operaci!", + "DefaultErrorMessage404": "Prostředek nenalezen!", + "DefaultErrorMessage404Detail": "Vyžádaný prostředek nebyl na serveru nalezen!", + "EntityNotFoundErrorMessage": "Neexistující entita {0} s id = {1}!", + "Error": "Chyba", + "AreYouSure": "Jste si jisti?", + "Cancel": "Zrušit", + "Yes": "Ano", + "No": "Ne", + "Close": "Zavřít", + "Save": "Uložit", + "SavingWithThreeDot": "Ukládám...", + "Actions": "Akce", + "Delete": "Smazat", + "Edit": "Upravit", + "Refresh": "Obnovit", + "ProcessingWithThreeDot": "Zpracovávám...", + "LoadingWithThreeDot": "Nahrávám...", + "Welcome": "Vítejte", + "Login": "Přihlášení", + "Register": "Registrovat", + "Logout": "Odhlásit", + "Submit": "Odeslat", + "Back": "Zpět", + "PagerSearch": "Vyhledat", + "PagerNext": "Další", + "PagerPrevious": "Předchozí", + "PagerFirst": "První", + "PagerLast": "Poslední", + "PagerInfo": "Zobrazeno od _START_ do _END_ z celkem _TOTAL_ 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", + "PagerShowMenuEntries": "Zobrazit _MENU_ záznamů", + "DatatableActionDropdownDefaultText": "Akce", + "ChangePassword": "Změnit heslo", + "PersonalInfo": "Můj profil", + "AreYouSureYouWantToCancelEditingWarningMessage": "Máte neuložené změny." + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs index 4218a9693f..df867c2320 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkFailedEventArgs.cs @@ -9,7 +9,7 @@ namespace Volo.Abp.Uow public class UnitOfWorkFailedEventArgs : UnitOfWorkEventArgs { /// - /// Exception that caused failure. This is set only if an error occured during . + /// Exception that caused failure. This is set only if an error occurred during . /// Can be null if there is no exception, but is not called. /// Can be null if another exception occurred during the UOW. /// diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs b/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs index b6d60cd848..6217e82805 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs @@ -1,6 +1,7 @@ using JetBrains.Annotations; using System.IO; using System.Text; +using System.Threading.Tasks; using Volo.Abp; namespace Microsoft.Extensions.FileProviders @@ -30,5 +31,31 @@ namespace Microsoft.Extensions.FileProviders } } } + + /// + /// Reads file content as byte[]. + /// + public static byte[] ReadBytes([NotNull] this IFileInfo fileInfo) + { + Check.NotNull(fileInfo, nameof(fileInfo)); + + using (var stream = fileInfo.CreateReadStream()) + { + return stream.GetAllBytes(); + } + } + + /// + /// Reads file content as byte[]. + /// + public static async Task ReadBytesAsync([NotNull] this IFileInfo fileInfo) + { + Check.NotNull(fileInfo, nameof(fileInfo)); + + using (var stream = fileInfo.CreateReadStream()) + { + return await stream.GetAllBytesAsync(); + } + } } } diff --git a/framework/test/AbpTestBase/AbpTestBase.csproj b/framework/test/AbpTestBase/AbpTestBase.csproj index 5e9761d304..4a25e99676 100644 --- a/framework/test/AbpTestBase/AbpTestBase.csproj +++ b/framework/test/AbpTestBase/AbpTestBase.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj index 1ca29422b9..806e426f82 100644 --- a/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Authentication.OAuth.Tests/Volo.Abp.AspNetCore.Authentication.OAuth.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj index b7ba4ddd36..b0d3baaad9 100644 --- a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo.Abp.AspNetCore.MultiTenancy.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj index 551dfd9f2a..bd0a8c1ca0 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj @@ -21,7 +21,7 @@ - + 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 new file mode 100644 index 0000000000..65c0603c50 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/cs.json @@ -0,0 +1,7 @@ +{ + "culture": "cs", + "texts": { + "BirthDate": "Narozeniny", + "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 b9d01a86c4..6bb9a5ae94 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 @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Shouldly; @@ -16,6 +17,14 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation return Task.FromResult(model.Value1); } + [HttpGet] + [Route("object-result-action-with-custom_validate")] + public Task ObjectResultActionWithCustomValidate(CustomValidateModel model) + { + ModelState.IsValid.ShouldBeTrue(); //AbpValidationFilter throws exception otherwise + return Task.FromResult(model.Value1); + } + [HttpGet] [Route("action-result-action")] public IActionResult ActionResultAction(ValidationTest1Model model) @@ -29,5 +38,19 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation [MinLength(2)] public string Value1 { get; set; } } + + public class CustomValidateModel : IValidatableObject + { + public string Value1 { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (Value1 != "hello") + { + yield return new ValidationResult("Value1 should be hello"); + } + } + } + } } \ 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 19a4bcd608..5177868843 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 @@ -47,5 +47,16 @@ namespace Volo.Abp.AspNetCore.Mvc.Validation var result = await GetResponseAsStringAsync("/api/validation-test/action-result-action"); //Missed the value1 result.ShouldBe("ModelState.IsValid: false"); } + + [Fact] + public async Task Should_Return_Custom_Validate_Errors() + { + var result = await GetResponseAsObjectAsync( + "/api/validation-test/object-result-action-with-custom_validate?value1=abc", HttpStatusCode.BadRequest); //value1 should be hello. + + result.Error.ValidationErrors.Length.ShouldBeGreaterThan(0); + result.Error.ValidationErrors.ShouldContain(x => x.Message == "Value1 should be hello"); + } + } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj index 9d5b607bfd..a25b7d7d8f 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Tests.csproj @@ -20,7 +20,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml.cs index c61423c6a6..adfd91c688 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/DynamicForms.cshtml.cs @@ -123,6 +123,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components [Display(Name = "My Car Type")] public CarType MyCarType { get; set; } + [Required] + [Display(Name = "My Car Type Nullable")] + public CarType? MyCarTypeNullable { get; set; } + [Required] [AbpRadioButton(Inline = true)] [Display(Name = "Your Car Type")] @@ -139,6 +143,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components [SelectItems(nameof(CountryList))] [Display(Name = "Neighbor Countries")] public List NeighborCountries { get; set; } + + [DynamicFormIgnore] + public string IgnoredProperty { get; set; } } public enum CarType diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs index e786c24f29..4ee4949d50 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Program.cs @@ -1,21 +1,46 @@ -using System.IO; +using System; +using System.IO; using Microsoft.AspNetCore.Hosting; +using Serilog; +using Serilog.Events; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo { public class Program { - public static void Main(string[] args) + public static int Main(string[] args) { - BuildWebHostInternal(args).Run(); + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() //TODO: Should be configurable! + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.File("Logs/logs.txt") + .CreateLogger(); + + try + { + Log.Information("Starting web host."); + BuildWebHostInternal(args).Run(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } - public static IWebHost BuildWebHostInternal(string[] args) => + internal static IWebHost BuildWebHostInternal(string[] args) => new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup() + .UseSerilog() .Build(); } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json index bf3d843c8b..07a310a660 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Properties/launchSettings.json @@ -1,9 +1,9 @@ { "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, + "windowsAuthentication": false, + "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:49211/", + "applicationUrl": "http://localhost:51339", "sslPort": 0 } }, @@ -15,13 +15,13 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo": { + "Volo.AbpIo.Commercial.Web": { "commandName": "Project", "launchBrowser": true, + "applicationUrl": "http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:49212/" + } } } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs index 52c11d7896..ffd3ca47be 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Startup.cs @@ -1,5 +1,4 @@ using System; -using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj index 3b6d429dd0..c5a4731256 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.csproj @@ -2,24 +2,12 @@ netcoreapp2.2 - $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; - Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo - Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo - true - true - false - false - false - true - true - false - - - - + + + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj index 7ee7949c19..74c0285f34 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo.Abp.AspNetCore.Mvc.UI.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj index 7ffebd4224..d32284ed87 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo.Abp.AspNetCore.Mvc.Versioning.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs index 728b119a19..3ea9103e9e 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/AbpAspNetCoreMvcVersioningTestModule.cs @@ -16,26 +16,34 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning )] public class AbpAspNetCoreMvcVersioningTestModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) + public override void PreConfigureServices(ServiceConfigurationContext context) { - Configure(options => + PreConfigure(options => { //2.0 Version options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => { - opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.TodoAppService).Namespace; + opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.v2.TodoAppService).Namespace; opts.ApiVersions.Add(new ApiVersion(2, 0)); }); - //1.0 Compatability version + //1.0 Compatibility version options.ConventionalControllers.Create(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly, opts => { - opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat.TodoAppService).Namespace; + opts.TypePredicate = t => t.Namespace == typeof(Volo.Abp.AspNetCore.Mvc.Versioning.App.v1.TodoAppService).Namespace; opts.ApiVersions.Add(new ApiVersion(1, 0)); }); }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + context.Services.ExecutePreConfiguredActions(options); + }); - context.Services.AddApiVersioning(options => + context.Services.AddAbpApiVersioning(options => { options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; @@ -43,7 +51,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning //options.ApiVersionReader = new HeaderApiVersionReader("api-version"); //Supports header too //options.ApiVersionReader = new MediaTypeApiVersionReader(); //Supports accept header too - options.ConfigureAbp(context.Services); + var mvcOptions = context.Services.ExecutePreConfiguredActions(); + options.ConfigureAbp(mvcOptions); }); context.Services.AddHttpClientProxies(typeof(AbpAspNetCoreMvcVersioningTestModule).Assembly); diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs deleted file mode 100644 index 0a8ed290ce..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/ITodoAppService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Volo.Abp.Application.Services; - -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat -{ - public interface ITodoAppService : IApplicationService - { - string Get(int id); - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs deleted file mode 100644 index 8d25b6facd..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/Compat/TodoAppService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Volo.Abp.ApiVersioning; -using Volo.Abp.Application.Services; - -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat -{ - public class TodoAppService : ApplicationService, ITodoAppService - { - private readonly IRequestedApiVersion _requestedApiVersion; - - public TodoAppService(IRequestedApiVersion requestedApiVersion) - { - _requestedApiVersion = requestedApiVersion; - } - - public string Get(int id) - { - return "Compat-" + id + "-" + GetVersionOrNone(); - } - - private string GetVersionOrNone() - { - return _requestedApiVersion.Current ?? "NONE"; - } - } -} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs index fb39ae53ed..db652e95bd 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/HelloController.cs @@ -5,13 +5,33 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.App { [ApiVersion("1.0")] [ApiVersion("2.0")] + [ApiController] [Route("api/v{apiVersion:apiVersion}/[controller]")] public class HelloController : AbpController, IHelloController { + [HttpGet] + public Task GetAsync() + { + return Task.FromResult($"Get-{HttpContext.GetRequestedApiVersion().ToString()}"); + } + + [HttpPost] + [MapToApiVersion("1.0")] + public Task PostAsyncV1() + { + return PostAsync(); + } + [HttpPost] - public Task PostAsync() + [MapToApiVersion("2.0")] + public Task PostAsyncV2() + { + return PostAsync(); + } + + private Task PostAsync() { - return Task.FromResult($"42-{HttpContext.GetRequestedApiVersion().ToString()}"); + return Task.FromResult($"Post-{HttpContext.GetRequestedApiVersion().ToString()}"); } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs index bd4871aac6..17c2226edd 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/IHelloController.cs @@ -4,6 +4,10 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.App { public interface IHelloController : IRemoteService { - Task PostAsync(); + Task GetAsync(); + + Task PostAsyncV1(); + + Task PostAsyncV2(); } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs deleted file mode 100644 index f4de87629d..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/ITodoAppService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Volo.Abp.Application.Services; - -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App -{ - public interface ITodoAppService : IApplicationService - { - string Get(int id); - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs deleted file mode 100644 index e4b08eed99..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/TodoAppService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Volo.Abp.ApiVersioning; -using Volo.Abp.Application.Services; - -namespace Volo.Abp.AspNetCore.Mvc.Versioning.App -{ - public class TodoAppService : ApplicationService, ITodoAppService - { - private readonly IRequestedApiVersion _requestedApiVersion; - - public TodoAppService(IRequestedApiVersion requestedApiVersion) - { - _requestedApiVersion = requestedApiVersion; - } - - public string Get(int id) - { - return id + "-" + GetVersionOrNone(); - } - - private string GetVersionOrNone() - { - return _requestedApiVersion.Current ?? "NONE"; - } - } -} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/ITodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/ITodoAppService.cs new file mode 100644 index 0000000000..3e82da0329 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/ITodoAppService.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v1 +{ + public interface ITodoAppService : IApplicationService + { + string Get(int id); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/TodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/TodoAppService.cs new file mode 100644 index 0000000000..f3f900148d --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v1/TodoAppService.cs @@ -0,0 +1,25 @@ +using Volo.Abp.ApiVersioning; +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v1 +{ + public class TodoAppService : ApplicationService, ITodoAppService + { + private readonly IRequestedApiVersion _requestedApiVersion; + + public TodoAppService(IRequestedApiVersion requestedApiVersion) + { + _requestedApiVersion = requestedApiVersion; + } + + public string Get(int id) + { + return $"Compat-{id}-{GetVersionOrNone()}"; + } + + private string GetVersionOrNone() + { + return _requestedApiVersion.Current ?? "NONE"; + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/ITodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/ITodoAppService.cs new file mode 100644 index 0000000000..ae4afea6e1 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/ITodoAppService.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v2 +{ + public interface ITodoAppService : IApplicationService + { + string Get(int id); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/TodoAppService.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/TodoAppService.cs new file mode 100644 index 0000000000..b97af873b7 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/App/v2/TodoAppService.cs @@ -0,0 +1,25 @@ +using Volo.Abp.ApiVersioning; +using Volo.Abp.Application.Services; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.App.v2 +{ + public class TodoAppService : ApplicationService, ITodoAppService + { + private readonly IRequestedApiVersion _requestedApiVersion; + + public TodoAppService(IRequestedApiVersion requestedApiVersion) + { + _requestedApiVersion = requestedApiVersion; + } + + public string Get(int id) + { + return id + "-" + GetVersionOrNone(); + } + + private string GetVersionOrNone() + { + return _requestedApiVersion.Current ?? "NONE"; + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs deleted file mode 100644 index 3d88941997..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/Compat/TodoAppService_Tests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using Volo.Abp.AspNetCore.Mvc.Versioning.App.Compat; -using Xunit; - -namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.Compat -{ - public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase - { - private readonly ITodoAppService _todoAppService; - - public TodoAppService_Tests() - { - _todoAppService = ServiceProvider.GetRequiredService(); - } - - [Fact(Skip = "It stopped working after ASP.NET Core 2.2 Upgrade. Should work on that.")] - public void Get() - { - _todoAppService.Get(42).ShouldBe("Compat-42-1.0"); - } - } -} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs index 519bfffd11..0dc4057bde 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/HelloController_Tests.cs @@ -8,17 +8,29 @@ namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test { public class HelloController_Tests: AspNetCoreMvcVersioningTestBase { - private readonly IHelloController _todoAppService; + private readonly IHelloController _helloController; public HelloController_Tests() { - _todoAppService = ServiceProvider.GetRequiredService(); + _helloController = ServiceProvider.GetRequiredService(); } [Fact] - public async Task PostAsync() + public async Task GetAsync() { - (await _todoAppService.PostAsync()).ShouldBe("42-2.0"); + (await _helloController.GetAsync()).ShouldBe("Get-2.0"); + } + + [Fact] + public async Task PostAsyncV1() + { + (await _helloController.PostAsyncV1()).ShouldBe("Post-1.0"); + } + + [Fact] + public async Task PostAsyncV2() + { + (await _helloController.PostAsyncV2()).ShouldBe("Post-2.0"); } } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs deleted file mode 100644 index 2bccbd41b2..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/TodoAppService_Tests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using Volo.Abp.AspNetCore.Mvc.Versioning.App; -using Xunit; - -namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test -{ - public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase - { - private readonly ITodoAppService _todoAppService; - - public TodoAppService_Tests() - { - _todoAppService = ServiceProvider.GetRequiredService(); - } - - [Fact(Skip = "It stopped working after ASP.NET Core 2.2 Upgrade. Should work on that.")] - public void Get() - { - _todoAppService.Get(42).ShouldBe("42-2.0"); - } - } -} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v1/TodoAppService_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v1/TodoAppService_Tests.cs new file mode 100644 index 0000000000..7b461a3902 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v1/TodoAppService_Tests.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.AspNetCore.Mvc.Versioning.App.v1; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.v1 +{ + public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase + { + private readonly ITodoAppService _todoAppService; + + public TodoAppService_Tests() + { + _todoAppService = ServiceProvider.GetRequiredService(); + } + + [Fact] + public void Get() + { + _todoAppService.Get(42).ShouldBe("Compat-42-1.0"); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v2/TodoAppService_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v2/TodoAppService_Tests.cs new file mode 100644 index 0000000000..f4cd122afc --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Versioning.Tests/Volo/Abp/AspNetCore/Mvc/Versioning/Test/v2/TodoAppService_Tests.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.AspNetCore.Mvc.Versioning.App.v2; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Versioning.Test.v2 +{ + public class TodoAppService_Tests : AspNetCoreMvcVersioningTestBase + { + private readonly ITodoAppService _todoAppService; + + public TodoAppService_Tests() + { + _todoAppService = ServiceProvider.GetRequiredService(); + } + + [Fact] + public void Get() + { + _todoAppService.Get(42).ShouldBe("42-2.0"); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj index 7ade7ea81b..9c7544a2f6 100644 --- a/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Tests/Volo.Abp.AspNetCore.Tests.csproj @@ -14,7 +14,7 @@ - + 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 35b66f5bfe..cb099c5168 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 @@ -16,7 +16,7 @@ - + diff --git a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs index 141bf01688..23eba6c53b 100644 --- a/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs +++ b/framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditingInterceptor_Tests.cs @@ -37,7 +37,7 @@ namespace Volo.Abp.Auditing using (var scope = _auditingManager.BeginScope()) { - await myAuditedObject1.DoItAsync(new InputObject { Value1 = "fourty-two", Value2 = 42 }); + await myAuditedObject1.DoItAsync(new InputObject { Value1 = "forty-two", Value2 = 42 }); await scope.SaveAsync(); } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj b/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj index 160262de19..88925bb67d 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo.Abp.Authorization.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj b/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj index 1f2757b34e..9eb89d4af9 100644 --- a/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj +++ b/framework/test/Volo.Abp.AutoMapper.Tests/Volo.Abp.AutoMapper.Tests.csproj @@ -14,7 +14,7 @@ - + \ No newline at end of file diff --git a/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj b/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj index 26a16f83cc..c92d40f8be 100644 --- a/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj +++ b/framework/test/Volo.Abp.Autofac.Tests/Volo.Abp.Autofac.Tests.csproj @@ -15,7 +15,7 @@ - + \ No newline at end of file diff --git a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj index 8c6a8170b8..7fa0474264 100644 --- a/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj +++ b/framework/test/Volo.Abp.BackgroundJobs.Tests/Volo.Abp.BackgroundJobs.Tests.csproj @@ -16,7 +16,7 @@ - + \ No newline at end of file diff --git a/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj b/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj index 49f5d2454a..2b4696092c 100644 --- a/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj +++ b/framework/test/Volo.Abp.Caching.Tests/Volo.Abp.Caching.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj b/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj index c96b26d272..c5a45236c2 100644 --- a/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj +++ b/framework/test/Volo.Abp.Castle.Core.Tests/Volo.Abp.Castle.Core.Tests.csproj @@ -14,7 +14,7 @@ - + \ No newline at end of file diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj b/framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj new file mode 100644 index 0000000000..bb2664cdc1 --- /dev/null +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo.Abp.Cli.Core.Tests.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.2 + latest + + + + + + + + + + diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/AbpCliTestBase.cs b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/AbpCliTestBase.cs new file mode 100644 index 0000000000..6f6393b7ae --- /dev/null +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/AbpCliTestBase.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Cli +{ + public class AbpCliTestBase : AbpIntegratedTest + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/AbpCliTestModule.cs b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/AbpCliTestModule.cs new file mode 100644 index 0000000000..3fc7dcd457 --- /dev/null +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/AbpCliTestModule.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.Cli +{ + [DependsOn( + typeof(AbpTestBaseModule), + typeof(AbpCliCoreModule) + )] + public class AbpCliTestModule : AbpModule + { + + } +} diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs new file mode 100644 index 0000000000..c8d31990cf --- /dev/null +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs @@ -0,0 +1,68 @@ +using Shouldly; +using System.Linq; +using Volo.Abp.Cli.Args; +using Xunit; + +namespace Volo.Abp.Cli +{ + public class CommandLineArgumentParser_Tests : AbpCliTestBase + { + private readonly ICommandLineArgumentParser _commandLineArgumentParser; + + public CommandLineArgumentParser_Tests() + { + _commandLineArgumentParser = GetRequiredService(); + } + + [Theory] + [InlineData(null)] + [InlineData(new object[] { new string[0] })] + public void Should_Parse_Empty_Arguments(string[] args) + { + var commandLineArgs = _commandLineArgumentParser.Parse(args); + commandLineArgs.Command.ShouldBeNull(); + commandLineArgs.Target.ShouldBeNull(); + commandLineArgs.Options.Any().ShouldBeFalse(); + } + + [Fact] + public void Should_Work_With_Only_Command() + { + var commandLineArgs = _commandLineArgumentParser.Parse(new[] { "mycommand" }); + commandLineArgs.Command.ShouldBe("mycommand"); + commandLineArgs.Target.ShouldBeNull(); + commandLineArgs.Options.Any().ShouldBeFalse(); + } + + [Fact] + public void Should_Work_With_Command_And_Target() + { + var commandLineArgs = _commandLineArgumentParser.Parse(new[] { "mycommand", "mytarget" }); + commandLineArgs.Command.ShouldBe("mycommand"); + commandLineArgs.Target.ShouldBe("mytarget"); + commandLineArgs.Options.Any().ShouldBeFalse(); + } + + [Fact] + public void Should_Work_With_Command_And_Target_And_Options() + { + var commandLineArgs = _commandLineArgumentParser.Parse(new[] + { + "mycommand", "mytarget", "-a", "value1", "-b", "--optionC", "value2" + }); + + commandLineArgs.Command.ShouldBe("mycommand"); + commandLineArgs.Target.ShouldBe("mytarget"); + commandLineArgs.Options.Any().ShouldBeTrue(); + + commandLineArgs.Options.ShouldContainKey("a"); + commandLineArgs.Options["a"].ShouldBe("value1"); + + commandLineArgs.Options.ShouldContainKey("b"); + commandLineArgs.Options["b"].ShouldBeNull(); + + commandLineArgs.Options.ShouldContainKey("optionC"); + commandLineArgs.Options["optionC"].ShouldBe("value2"); + } + } +} diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectFinder_Tests.cs b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectFinder_Tests.cs new file mode 100644 index 0000000000..5576960dfa --- /dev/null +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/ProjectFinder_Tests.cs @@ -0,0 +1,46 @@ +using System.IO; +using Shouldly; +using Volo.Abp.Cli.ProjectModification; +using Xunit; + +namespace Volo.Abp.Cli +{ + public class ProjectFinder_Tests + { + [Fact] + public void FindNpmTargetProjectFile_Tests() + { + var npmTargets = ProjectFinder.FindNpmTargetProjectFile( + new [] + { + GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.Host.csproj"), + GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.Web.Host.csproj"), + GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.HttpApi.Host.csproj"), + GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.Application.Contracts.csproj"), + GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.Application.csproj"), + GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.EntityFrameworkCore.csproj") + } + ); + + npmTargets.ShouldContain(GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.Host.csproj")); + npmTargets.ShouldContain(GetNormalizedPath(@"c:\temp\project\folder\src\Acme.PhoneBook.Web.Host.csproj")); + } + + [Fact] + public void GetAssemblyName_Tests() + { + var assemblyName = ProjectFinder.GetAssemblyName( + GetNormalizedPath( + @"c:\temp\project\folder\src\Acme.PhoneBook.Host.csproj" + ) + ); + + assemblyName.ShouldBe("Acme.PhoneBook.Host"); + } + + private static string GetNormalizedPath(string path) + { + return path.Replace('\\', Path.DirectorySeparatorChar); + } + } +} diff --git a/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/AbpConventionalDependencyInjectionExtensions_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/AbpConventionalDependencyInjectionExtensions_Tests.cs index 369ee98cb9..dc02cf4e2d 100644 --- a/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/AbpConventionalDependencyInjectionExtensions_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Microsoft/Extensions/DependencyInjection/AbpConventionalDependencyInjectionExtensions_Tests.cs @@ -85,7 +85,7 @@ namespace Microsoft.Extensions.DependencyInjection //Assert - //Check descriptons in service collection + //Check descriptions in service collection var descriptions = _services.Where(s => s.ServiceType == typeof(IMyService)).ToList(); descriptions.Count.ShouldBe(2); descriptions[0].ImplementationType.ShouldBe(typeof(FirstImplOfMyService)); @@ -113,7 +113,7 @@ namespace Microsoft.Extensions.DependencyInjection //Assert - //Check descriptons in service collection + //Check descriptions in service collection var descriptions = _services.Where(s => s.ServiceType == typeof(IMyService)).ToList(); descriptions.Count.ShouldBe(1); descriptions[0].ImplementationType.ShouldBe(typeof(FirstImplOfMyService)); @@ -139,7 +139,7 @@ namespace Microsoft.Extensions.DependencyInjection //Assert - //Check descriptons in service collection + //Check descriptions in service collection var descriptions = _services.Where(s => s.ServiceType == typeof(IMyService)).ToList(); descriptions.Count.ShouldBe(1); descriptions[0].ImplementationType.ShouldBe(typeof(MyServiceReplacesIMyService)); diff --git a/framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs b/framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs new file mode 100644 index 0000000000..382440a3f4 --- /dev/null +++ b/framework/test/Volo.Abp.Core.Tests/System/AbpTypeExtensions_Tests.cs @@ -0,0 +1,32 @@ +using Shouldly; +using Xunit; + +namespace System +{ + public class AbpTypeExtensions_Tests + { + [Fact] + public void GetBaseClasses() + { + var baseClasses = typeof(MyClass).GetBaseClasses(includeObject: false); + baseClasses.Length.ShouldBe(2); + baseClasses[0].ShouldBe(typeof(MyBaseClass1)); + baseClasses[1].ShouldBe(typeof(MyBaseClass2)); + } + + public abstract class MyBaseClass1 + { + + } + + public class MyBaseClass2 : MyBaseClass1 + { + + } + + public class MyClass : MyBaseClass2 + { + + } + } +} diff --git a/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj b/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj index f043c10c13..bd58cb0d71 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj +++ b/framework/test/Volo.Abp.Core.Tests/Volo.Abp.Core.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoRegistrationHelper_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoRegistrationHelper_Tests.cs index 529290d0a1..16dec3ea7d 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoRegistrationHelper_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/DependencyInjection/AutoRegistrationHelper_Tests.cs @@ -1,6 +1,4 @@ -using Microsoft.Extensions.DependencyInjection; -using Shouldly; -using System.Linq; +using Shouldly; using Xunit; namespace Volo.Abp.DependencyInjection @@ -12,7 +10,7 @@ namespace Volo.Abp.DependencyInjection { //Act - var exposedServices = AutoRegistrationHelper.GetExposedServices(new ServiceCollection(), typeof(DefaultDerivedService)).ToList(); + var exposedServices = ExposedServiceExplorer.GetExposedServices(typeof(DefaultDerivedService)); //Assert @@ -27,7 +25,7 @@ namespace Volo.Abp.DependencyInjection { //Act - var exposedServices = AutoRegistrationHelper.GetExposedServices(new ServiceCollection(), typeof(ExplicitDerivedService)).ToList(); + var exposedServices = ExposedServiceExplorer.GetExposedServices(typeof(ExplicitDerivedService)); //Assert diff --git a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs index 842940cfaf..333ea2e84a 100644 --- a/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/ReflectionHelper_Tests.cs @@ -1,7 +1,102 @@ -namespace Volo.Abp.Reflection +using System.Linq; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Reflection { public class ReflectionHelper_Tests { //TODO: ... + + + [Fact] + public void GetPublicConstantsRecursively_Test() + { + var constants = ReflectionHelper.GetPublicConstantsRecursively(typeof(BaseRole)); + + constants.ShouldNotBeEmpty(); + constants.Length.ShouldBe(1); + constants.ShouldContain(x => x == "DefaultBaseRoleName"); + } + + [Fact] + public void GetPublicConstantsRecursively_Inherit_Test() + { + var constants = ReflectionHelper.GetPublicConstantsRecursively(typeof(Roles)); + + constants.ShouldNotBeEmpty(); + constants.Length.ShouldBe(2); + constants.ShouldContain(x => x == "DefaultBaseRoleName"); + constants.ShouldContain(x => x == "DefaultRoleName"); + } + + + [Fact] + public void GetPublicConstantsRecursively_NestedTypes_Test() + { + var constants = ReflectionHelper.GetPublicConstantsRecursively(typeof(IdentityPermissions)); + + constants.ShouldNotBeEmpty(); + constants.Except(IdentityPermissions.GetAll()).Count().ShouldBe(0); + } } -} + + + public class BaseRole + { + public const string BaseRoleName = "DefaultBaseRoleName"; + } + + public class Roles : BaseRole + { + public const string RoleName = "DefaultRoleName"; + } + + public static class IdentityPermissions + { + public const string GroupName = "AbpIdentity"; + + public static class Roles + { + public const string Default = GroupName + ".Roles"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + public const string ManagePermissions = Default + ".ManagePermissions"; + } + + public static class Users + { + public const string Default = GroupName + ".Users"; + public const string Create = Default + ".Create"; + public const string Update = Default + ".Update"; + public const string Delete = Default + ".Delete"; + public const string ManagePermissions = Default + ".ManagePermissions"; + } + + public static class UserLookup + { + public const string Default = GroupName + ".UserLookup"; + } + + public static string[] GetAll() + { + return new[] + { + GroupName, + Roles.Default, + Roles.Create, + Roles.Update, + Roles.Delete, + Roles.ManagePermissions, + Users.Default, + Users.Create, + Users.Update, + Users.Delete, + Users.ManagePermissions, + UserLookup.Default + }; + } + } + +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj b/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj index 1cef1431c2..13c9cb2ef3 100644 --- a/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj +++ b/framework/test/Volo.Abp.Data.Tests/Volo.Abp.Data.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj b/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj index ae203870fe..ad4141f96f 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo.Abp.Ddd.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj index 5083782360..ca155ab951 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj @@ -11,14 +11,17 @@ - - + + + + + - + diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs index a36c664e8a..831258c5d5 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs @@ -1,6 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Emailing.Templates; -using Volo.Abp.Emailing.Templates.Virtual; +using Volo.Abp.Emailing.Localization; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -18,12 +17,13 @@ namespace Volo.Abp.Emailing options.FileSets.AddEmbedded(); }); - Configure(options => + Configure(options => { - options.Templates["template1"] = - new EmailTemplateDefinition("template1") - .SetVirtualFilePath("/Volo/Abp/Emailing/TestTemplates/template1.html"); + options.Resources + .Add() + .AddVirtualJson("/Volo/Abp/Emailing/Localization"); }); + } } -} +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs new file mode 100644 index 0000000000..403aaad0bd --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Emailing.Templates; +using Xunit; + +namespace Volo.Abp.Emailing +{ + public class EmailTemplateRender_Tests : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + private readonly ITemplateRender _templateRender; + + public EmailTemplateRender_Tests() + { + _templateRender = GetRequiredService(); + } + + [Fact] + public async Task RenderAsync() + { + var template = "Hello {{email}} {{ for order in orders }}{{ order.id }}:{{ order.name }},{{ end }}"; + + var model = new ModelClass + { + Email = "john@abp.io", + Orders = new List + { + new ModelClass.Order + { + Id = "1", + Name = "iphone" + }, + new ModelClass.Order + { + Id = "2", + Name = "ipad" + } + } + }; + + var result = await _templateRender.RenderAsync(template, model); + result.ShouldBe("Hello john@abp.io 1:iphone,2:ipad,"); + } + + public class ModelClass + { + public string Email { get; set; } + + public List Orders { get; set; } + + public class Order + { + public string Id { get; set; } + + public string Name { get; set; } + } + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs index a09fb48ed1..62a2cb3eb6 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs @@ -7,6 +7,11 @@ namespace Volo.Abp.Emailing { public class EmailTemplateStore_Tests : AbpIntegratedTest { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + private readonly IEmailTemplateProvider _emailTemplateProvider; public EmailTemplateStore_Tests() @@ -17,8 +22,31 @@ namespace Volo.Abp.Emailing [Fact] public async Task Should_Get_Registered_Template() { - var template = await _emailTemplateProvider.GetAsync("template1"); - template.Content.ShouldContain("This is a test template!"); + var template = await _emailTemplateProvider.GetAsync("template1", "tr"); + template.Content.ShouldContain("Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın."); + } + + [Fact] + public async Task Should_Get_Default_Culture_Template() + { + var template = await _emailTemplateProvider.GetAsync("template1", "zh-Hans"); + template.Content.ShouldContain("Please confirm your email address by clicking the link below."); + } + + [Fact] + public async Task Should_Get_Registered_Template_With_Layout() + { + var template = await _emailTemplateProvider.GetAsync("template2", "en"); + + template.Content.ShouldContain("\r\n " + "Please confirm your email address by clicking the link below."); + } + + + [Fact] + public async Task Should_Get_Registered_Template_With_Localize() + { + var template = await _emailTemplateProvider.GetAsync("template3", "tr"); + template.Content.ShouldContain("Merhaba Abp"); } } } diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs new file mode 100644 index 0000000000..bdc75f9f2f --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.Emailing.Localization +{ + public class AbpEmailingTestResource + { + } +} 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..b03a2b3fb1 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "hello": "hello" + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..38f7cf7e5b --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "hello": "hello" + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json new file mode 100644 index 0000000000..0e33dee138 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json @@ -0,0 +1,6 @@ +{ + "culture": "pt-BR", + "texts": { + "hello": "Olá" + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json new file mode 100644 index 0000000000..6c3c94cfdf --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json @@ -0,0 +1,6 @@ +{ + "culture": "tr", + "texts": { + "hello": "Merhaba" + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs new file mode 100644 index 0000000000..60c9a2ccf3 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs @@ -0,0 +1,24 @@ +using Volo.Abp.Emailing.Localization; +using Volo.Abp.Emailing.Templates; +using Volo.Abp.Emailing.Templates.VirtualFiles; + +namespace Volo.Abp.Emailing +{ + public class TestEmailTemplateProvider : EmailTemplateDefinitionProvider + { + public override void Define(IEmailTemplateDefinitionContext context) + { + var template1 = new EmailTemplateDefinition("template1", defaultCultureName: "en", layout: null) + .AddTemplateVirtualFiles("/Volo/Abp/Emailing/TestTemplates/Template1"); + context.Add(template1); + + var template2 = new EmailTemplateDefinition("template2", layout: StandardEmailTemplates.DefaultLayout) + .AddTemplateVirtualFiles("/Volo/Abp/Emailing/TestTemplates/Template2"); + context.Add(template2); + + var template3 = new EmailTemplateDefinition("template3", layout: null, singleTemplateFile: true, localizationResource: typeof(AbpEmailingTestResource)) + .AddTemplateVirtualFile("/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl"); + context.Add(template3); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl new file mode 100644 index 0000000000..49a951e8c0 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl @@ -0,0 +1,4 @@ +Please confirm your email address by clicking the link below. +We may need to send you critical information about our service and it is important that we have an accurate email address. + +Confirm email address \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl new file mode 100644 index 0000000000..3e572aedbb --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl @@ -0,0 +1,4 @@ +Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın. +Size hizmetimizle ilgili kritik bilgileri göndermemiz gerekebilir ve doğru bir e-posta adresimizin olması önemlidir. + +E-posta adresini onayla \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl new file mode 100644 index 0000000000..49a951e8c0 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl @@ -0,0 +1,4 @@ +Please confirm your email address by clicking the link below. +We may need to send you critical information about our service and it is important that we have an accurate email address. + +Confirm email address \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl new file mode 100644 index 0000000000..f30f512848 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl @@ -0,0 +1 @@ +{{#L:hello}} Abp \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/template1.html b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/template1.html deleted file mode 100644 index 5c98393c69..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/template1.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - This is a test template! - - \ No newline at end of file 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 4162f8319b..3f32baa6aa 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 @@ -20,8 +20,8 @@ - - + + \ No newline at end of file diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Specifications_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Specifications_Tests.cs new file mode 100644 index 0000000000..d9de34f1ce --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/Repository_Specifications_Tests.cs @@ -0,0 +1,8 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.EntityFrameworkCore.Repositories +{ + public class Repository_Specifications_Tests : Repository_Specifications_Tests + { + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj b/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj index dc8ab54f51..e810d5ef76 100644 --- a/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo.Abp.EventBus.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj index 3632aa2bf3..ece1f124d5 100644 --- a/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj +++ b/framework/test/Volo.Abp.Features.Tests/Volo.Abp.Features.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj b/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj index ab2f82eefa..b0bc864896 100644 --- a/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo.Abp.FluentValidation.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj b/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj index e0a8ea60de..4997863830 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo.Abp.Http.Client.Tests.csproj @@ -15,7 +15,7 @@ - + \ No newline at end of file diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj b/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj index f5b87ab423..98a01e2fc7 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj +++ b/framework/test/Volo.Abp.Localization.Tests/Volo.Abp.Localization.Tests.csproj @@ -18,7 +18,7 @@ - + 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 new file mode 100644 index 0000000000..6f249267c0 --- /dev/null +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/cs.json @@ -0,0 +1,7 @@ +{ + "culture": "cs", + "texts": { + "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/CountryNames/en.json b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json index ead8fc2af8..9bc0f26698 100644 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/en.json @@ -2,6 +2,6 @@ "culture": "en", "texts": { "USA": "United States of America", - "Brazil": "Brasil" + "Brazil": "Brazil" } } \ 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 new file mode 100644 index 0000000000..9fd414c139 --- /dev/null +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/cs.json @@ -0,0 +1,7 @@ +{ + "culture": "cs", + "texts": { + "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 new file mode 100644 index 0000000000..88593c2ba8 --- /dev/null +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/cs.json @@ -0,0 +1,11 @@ +{ + "culture": "cs", + "texts": { + "Hello {0}.": "Ahoj {0}.", + "Car": "Auto", + "CarPlural": "Auta", + "MaxLenghtErrorMessage": "Toto pole může mít nanejvýš '{0}' znaků", + "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 new file mode 100644 index 0000000000..8054f6de70 --- /dev/null +++ b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "SeeYou": "Měj se" + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj b/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj index 0a61a38420..062cfaf730 100644 --- a/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo.Abp.MemoryDb.Tests.csproj @@ -16,7 +16,7 @@ - + \ No newline at end of file diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Repositories/Repository_Specifications_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Repositories/Repository_Specifications_Tests.cs new file mode 100644 index 0000000000..e3183127a0 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Repositories/Repository_Specifications_Tests.cs @@ -0,0 +1,8 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.Repositories +{ + public class Repository_Specifications_Tests : Repository_Specifications_Tests + { + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj b/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj index f2508f81ca..5ca5401bac 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo.Abp.MongoDB.Tests.csproj @@ -19,8 +19,8 @@ - - + + \ No newline at end of file diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs new file mode 100644 index 0000000000..6529b4c5a9 --- /dev/null +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs @@ -0,0 +1,8 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MongoDB.Repositories +{ + public class Repository_Specifications_Tests : Repository_Specifications_Tests + { + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj b/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj index 6b18627f7e..631ad1ed81 100644 --- a/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj +++ b/framework/test/Volo.Abp.MultiTenancy.Tests/Volo.Abp.MultiTenancy.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj b/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj index 7fabca78ba..5339246c52 100644 --- a/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj +++ b/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj b/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj index a9fe791822..99db6083c2 100644 --- a/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj +++ b/framework/test/Volo.Abp.Serialization.Tests/Volo.Abp.Serialization.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj b/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj index 7f83c36c9a..1e9bd3f4a0 100644 --- a/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj +++ b/framework/test/Volo.Abp.Settings.Tests/Volo.Abp.Settings.Tests.csproj @@ -16,7 +16,7 @@ - + diff --git a/framework/test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj b/framework/test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj new file mode 100644 index 0000000000..6f2cbe09f8 --- /dev/null +++ b/framework/test/Volo.Abp.Specifications.Tests/Volo.Abp.Specifications.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.2 + latest + Volo.Abp.Specifications.Tests + Volo.Abp.Specifications.Tests + true + false + false + false + + + + + + + + + + \ No newline at end of file diff --git a/framework/test/Volo.Abp.Specifications.Tests/Volo/Abp/Specifications/Specifications_Tests.cs b/framework/test/Volo.Abp.Specifications.Tests/Volo/Abp/Specifications/Specifications_Tests.cs new file mode 100644 index 0000000000..dbe4fc6b5d --- /dev/null +++ b/framework/test/Volo.Abp.Specifications.Tests/Volo/Abp/Specifications/Specifications_Tests.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using Shouldly; +using Xunit; + +namespace Volo.Abp.Specifications +{ + public class Specification_Tests + { + private readonly IQueryable _customers; + + public Specification_Tests() + { + _customers = new List + { + new Customer("John", 17, 47000, "England"), + new Customer("Tuana", 2, 500, "Turkey"), + new Customer("Martin", 43, 16000, "USA"), + new Customer("Lee", 32, 24502, "China"), + new Customer("Douglas", 42, 42000, "England"), + new Customer("Abelard", 14, 2332, "German"), + new Customer("Neo", 16, 120000, "USA"), + new Customer("Daan", 39, 6000, "Netherlands"), + new Customer("Alessandro", 22, 8271, "Italy"), + new Customer("Noah", 33, 82192, "Belgium") + }.AsQueryable(); + } + + [Fact] + public void Any_Should_Return_All() + { + _customers + .Where(new AnySpecification()) //Implicitly converted to Expression! + .Count() + .ShouldBe(_customers.Count()); + } + + [Fact] + public void None_Should_Return_Empty() + { + _customers + .Where(new NoneSpecification().ToExpression()) + .Count() + .ShouldBe(0); + } + + [Fact] + public void Not_Should_Return_Reverse_Result() + { + _customers + .Where(new EuropeanCustomerSpecification().Not().ToExpression()) + .Count() + .ShouldBe(3); + } + + [Fact] + public void Should_Support_Native_Expressions_And_Combinations() + { + _customers + .Where(new ExpressionSpecification(c => c.Age >= 18).ToExpression()) + .Count() + .ShouldBe(6); + + _customers + .Where(new EuropeanCustomerSpecification().And(new ExpressionSpecification(c => c.Age >= 18)).ToExpression()) + .Count() + .ShouldBe(4); + } + + [Fact] + public void CustomSpecification_Test() + { + _customers + .Where(new EuropeanCustomerSpecification().ToExpression()) + .Count() + .ShouldBe(7); + + _customers + .Where(new Age18PlusCustomerSpecification().ToExpression()) + .Count() + .ShouldBe(6); + + _customers + .Where(new BalanceCustomerSpecification(10000, 30000).ToExpression()) + .Count() + .ShouldBe(2); + + _customers + .Where(new PremiumCustomerSpecification().ToExpression()) + .Count() + .ShouldBe(3); + } + + [Fact] + public void IsSatisfiedBy_Tests() + { + new PremiumCustomerSpecification().IsSatisfiedBy(new Customer("David", 49, 55000, "USA")).ShouldBeTrue(); + + new PremiumCustomerSpecification().IsSatisfiedBy(new Customer("David", 49, 200, "USA")).ShouldBeFalse(); + new PremiumCustomerSpecification().IsSatisfiedBy(new Customer("David", 12, 55000, "USA")).ShouldBeFalse(); + } + + [Fact] + public void CustomSpecification_Composite_Tests() + { + _customers + .Where(new EuropeanCustomerSpecification().And(new Age18PlusCustomerSpecification()).ToExpression()) + .Count() + .ShouldBe(4); + + _customers + .Where(new EuropeanCustomerSpecification().Not().And(new Age18PlusCustomerSpecification()).ToExpression()) + .Count() + .ShouldBe(2); + + _customers + .Where(new Age18PlusCustomerSpecification().AndNot(new EuropeanCustomerSpecification()).ToExpression()) + .Count() + .ShouldBe(2); + } + + private class Customer + { + public string Name { get; private set; } + + public byte Age { get; private set; } + + public long Balance { get; private set; } + + public string Location { get; private set; } + + public Customer(string name, byte age, long balance, string location) + { + Name = name; + Age = age; + Balance = balance; + Location = location; + } + } + + private class EuropeanCustomerSpecification : Specification + { + public override Expression> ToExpression() + { + return c => c.Location == "England" || + c.Location == "Turkey" || + c.Location == "German" || + c.Location == "Netherlands" || + c.Location == "Italy" || + c.Location == "Belgium"; + } + } + + private class Age18PlusCustomerSpecification : Specification + { + public override Expression> ToExpression() + { + return c => c.Age >= 18; + } + } + + private class BalanceCustomerSpecification : Specification + { + public int MinBalance { get; } + + public int MaxBalance { get; } + + public BalanceCustomerSpecification(int minBalance, int maxBalance) + { + MinBalance = minBalance; + MaxBalance = maxBalance; + } + + public override Expression> ToExpression() + { + return c => c.Balance >= MinBalance && c.Balance <= MaxBalance; + } + } + + private class PremiumCustomerSpecification : AndSpecification + { + public PremiumCustomerSpecification() + : base(new Age18PlusCustomerSpecification(), new BalanceCustomerSpecification(20000, int.MaxValue)) + { + } + } + } +} diff --git a/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj b/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj index e63096f8c4..3985910708 100644 --- a/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj +++ b/framework/test/Volo.Abp.TestApp.Tests/Volo.Abp.TestApp.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs b/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs index 9dbfc8936e..c58b1b5f85 100644 --- a/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs +++ b/framework/test/Volo.Abp.TestApp.Tests/Volo/Abp/TestApp/Application/PersonAppService_Tests.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.TestApp.Application } [Fact] - public async Task GetAll() + public async Task GetList() { var people = await _peopleAppService.GetListAsync(new PagedAndSortedResultRequestDto()); people.Items.Count.ShouldBeGreaterThan(0); diff --git a/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj b/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj index 243bd70a8c..4e7b58ee84 100644 --- a/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj +++ b/framework/test/Volo.Abp.TestApp/Volo.Abp.TestApp.csproj @@ -16,8 +16,9 @@ + - + diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs index 05c6afb7dc..ef76becbc5 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs @@ -35,7 +35,7 @@ namespace Volo.Abp.TestApp.Application var phone = new Phone(person.Id, phoneDto.Number, phoneDto.Type); person.Phones.Add(phone); - + Repository.Update(person); return ObjectMapper.Map(phone); } @@ -43,6 +43,7 @@ namespace Volo.Abp.TestApp.Application { var person = await GetEntityByIdAsync(id); person.Phones.RemoveAll(p => p.Number == number); + Repository.Update(person); } public Task GetWithComplexType(GetWithComplexTypeInput input) diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Specifications_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Specifications_Tests.cs new file mode 100644 index 0000000000..a3e7907135 --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Specifications_Tests.cs @@ -0,0 +1,40 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using Shouldly; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Modularity; +using Volo.Abp.Specifications; +using Volo.Abp.TestApp.Domain; +using Xunit; + +namespace Volo.Abp.TestApp.Testing +{ + public abstract class Repository_Specifications_Tests : TestAppTestBase + where TStartupModule : IAbpModule + { + protected readonly IRepository CityRepository; + + protected Repository_Specifications_Tests() + { + CityRepository = GetRequiredService>(); + } + + [Fact] + public void SpecificationWithRepository_Test() + { + WithUnitOfWork(() => + { + CityRepository.Count(new CitySpecification().ToExpression()).ShouldBe(1); + }); + } + } + + public class CitySpecification : Specification + { + public override Expression> ToExpression() + { + return city => city.Name == "Istanbul"; + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj index b308eae3a2..10318fbf90 100644 --- a/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj +++ b/framework/test/Volo.Abp.UI.Navigation.Tests/Volo.Abp.UI.Navigation.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj b/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj index 40575e7b1c..f3dede4b21 100644 --- a/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj +++ b/framework/test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj b/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj index 5891413963..60308b96ae 100644 --- a/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj +++ b/framework/test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj index 7f0be985bf..895b8231ce 100644 --- a/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj +++ b/framework/test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/cs.json b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/cs.json new file mode 100644 index 0000000000..f0ec8ad410 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/cs.json @@ -0,0 +1,21 @@ +{ + "culture": "cs", + "texts": { + "UserName": "Uživatelské jméno", + "EmailAddress": "Email", + "UserNameOrEmailAddress": "Uživatelské jméno nebo email", + "Password": "Heslo", + "RememberMe": "Zapamatovat si mě", + "UseAnotherServiceToLogin": "Použít jinou službu k přihlášení", + "UserLockedOutMessage": "Tento účet byl uzamčen z důvodu neúspěšných přihlášení. Přihlaste se prosím později.", + "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.", + "Login": "Přihlásit", + "Cancel": "Zrušit", + "Register": "Registrovat", + "InvalidLoginRequest": "Neplatný požadavek k přihlášení", + "ThereAreNoLoginSchemesConfiguredForThisClient": "Pro tohoto klienta nejsou nakonfigurovány žádné přihlašovací schémata.", + "LogInUsingYourProviderAccount": "Přihlásit pomocí vašeho účtu {0}" + } +} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/en.json b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/en.json index 1055309b22..892e60f89e 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/en.json +++ b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/en.json @@ -14,7 +14,6 @@ "Login": "Login", "Cancel": "Cancel", "Register": "Register", - "UseAnotherServiceToLogIn.": "Use another service to log in.", "InvalidLoginRequest": "Invalid login request", "ThereAreNoLoginSchemesConfiguredForThisClient": "There are no login schemes configured for this client.", "LogInUsingYourProviderAccount": "Log in using your {0} account" diff --git a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/pt-BR.json b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/pt-BR.json index fec54c0476..0513d99afd 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/pt-BR.json +++ b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/pt-BR.json @@ -14,7 +14,6 @@ "Login": "Entrar", "Cancel": "Cancelar", "Register": "Registrar-se", - "UseAnotherServiceToLogIn.": "Use outro serviço para entrar.", "InvalidLoginRequest": "Requisição de acesso inválida!", "ThereAreNoLoginSchemesConfiguredForThisClient": "Não existe um esquema de acesso para este usuário", "LogInUsingYourProviderAccount": "Acessse utilizando sua conta {0}" diff --git a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json index dfc1c9486a..3b78c3a55d 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json +++ b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/tr.json @@ -14,7 +14,6 @@ "Login": "Giriş yap", "Cancel": "İptal", "Register": "Kayıt ol", - "UseAnotherServiceToLogIn.": "Başka bir servisle giriş yap.", "InvalidLoginRequest": "Başarısız giriş isteği", "ThereAreNoLoginSchemesConfiguredForThisClient": "Bu client için konfigüre edilmiş giriş şeması bulunamadı.", "LogInUsingYourProviderAccount": "{0} hesabınızla giriş yapın." diff --git a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json index 01e0b6b57d..6aee740de6 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json +++ b/modules/account/src/Volo.Abp.Account.Web/Localization/Resources/AbpAccount/Web/zh-Hans.json @@ -14,7 +14,6 @@ "Login": "登录", "Cancel": "取消", "Register": "注册", - "UseAnotherServiceToLogIn.": "使用其他服务登录.", "InvalidLoginRequest": "登录请求无效", "ThereAreNoLoginSchemesConfiguredForThisClient": "没有为此客户端配置登录方案.", "LogInUsingYourProviderAccount": "使用你的{0}帐户登录" diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditingStore.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditingStore.cs index 59f3ae5431..ecb07300f0 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditingStore.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/AuditingStore.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Volo.Abp.Auditing; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; @@ -16,29 +17,42 @@ namespace Volo.Abp.AuditLogging private readonly IAuditLogRepository _auditLogRepository; private readonly IGuidGenerator _guidGenerator; private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly AbpAuditingOptions Options; public AuditingStore( IAuditLogRepository auditLogRepository, IGuidGenerator guidGenerator, - IUnitOfWorkManager unitOfWorkManager) + IUnitOfWorkManager unitOfWorkManager, + IOptions options) { _auditLogRepository = auditLogRepository; _guidGenerator = guidGenerator; _unitOfWorkManager = unitOfWorkManager; + Options = options.Value; Logger = NullLogger.Instance; } + private async Task SaveLogAsync(AuditLogInfo auditInfo) + { + using (var uow = _unitOfWorkManager.Begin(requiresNew: true)) + { + var auditLog = new AuditLog(_guidGenerator, auditInfo); + await _auditLogRepository.InsertAsync(auditLog); + await uow.SaveChangesAsync(); + } + } public async Task SaveAsync(AuditLogInfo auditInfo) { + if (!Options.HideErrors) + { + await SaveLogAsync(auditInfo); + return; + } + try { - using (var uow = _unitOfWorkManager.Begin(requiresNew: true)) - { - var auditLog = new AuditLog(_guidGenerator, auditInfo); - await _auditLogRepository.InsertAsync(auditLog); - await uow.SaveChangesAsync(); - } + await SaveLogAsync(auditInfo); } catch (Exception ex) { 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 247752f848..32ee40e070 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 @@ -15,7 +15,7 @@ - + diff --git a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Volo.Abp.BackgroundJobs.DemoApp.HangFire.csproj b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Volo.Abp.BackgroundJobs.DemoApp.HangFire.csproj index a080318e11..8134b0c512 100644 --- a/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Volo.Abp.BackgroundJobs.DemoApp.HangFire.csproj +++ b/modules/background-jobs/app/Volo.Abp.BackgroundJobs.DemoApp.HangFire/Volo.Abp.BackgroundJobs.DemoApp.HangFire.csproj @@ -6,7 +6,7 @@ - + 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 c823e9a53a..3540aa8e9c 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,10 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobRecord.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobRecord.cs index 0c18e9f877..c3684d9cfe 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobRecord.cs +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobRecord.cs @@ -39,7 +39,7 @@ namespace Volo.Abp.BackgroundJobs public virtual DateTime? LastTryTime { get; set; } /// - /// This is true if this job is continously failed and will not be executed again. + /// This is true if this job is continuously failed and will not be executed again. /// public virtual bool IsAbandoned { get; set; } diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobStore.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobStore.cs index 5277b5f4f4..fed3718f85 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobStore.cs +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/BackgroundJobStore.cs @@ -19,33 +19,33 @@ namespace Volo.Abp.BackgroundJobs BackgroundJobRepository = backgroundJobRepository; } - public async Task FindAsync(Guid jobId) + public virtual async Task FindAsync(Guid jobId) { return ObjectMapper.Map( await BackgroundJobRepository.FindAsync(jobId) ); } - public async Task InsertAsync(BackgroundJobInfo jobInfo) + public virtual async Task InsertAsync(BackgroundJobInfo jobInfo) { await BackgroundJobRepository.InsertAsync( ObjectMapper.Map(jobInfo) ); } - public async Task> GetWaitingJobsAsync(int maxResultCount) + public virtual async Task> GetWaitingJobsAsync(int maxResultCount) { return ObjectMapper.Map, List>( await BackgroundJobRepository.GetWaitingListAsync(maxResultCount) ); } - public async Task DeleteAsync(Guid jobId) + public virtual async Task DeleteAsync(Guid jobId) { await BackgroundJobRepository.DeleteAsync(jobId); } - public async Task UpdateAsync(BackgroundJobInfo jobInfo) + public virtual async Task UpdateAsync(BackgroundJobInfo jobInfo) { await BackgroundJobRepository.UpdateAsync( ObjectMapper.Map(jobInfo) diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj index 07501ec85f..a8ff824305 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.Domain.Tests/Volo.Abp.BackgroundJobs.Domain.Tests.csproj @@ -12,7 +12,7 @@ - + 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 6fad02af58..722d94f88e 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 @@ -13,10 +13,10 @@ - - - - + + + + diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj index 00c9d6ffc2..f3fb433d10 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo.Abp.BackgroundJobs.MongoDB.Tests.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj index 9c9e420c65..1f4db1af41 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.TestBase/Volo.Abp.BackgroundJobs.TestBase.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs b/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs index 248d1433cc..1bbc4a93a3 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs +++ b/modules/blogging/app/Volo.BloggingTestApp/BloggingTestAppModule.cs @@ -98,7 +98,14 @@ namespace Volo.BloggingTestApp options.CustomSchemaIds(type => type.FullName); }); - var cultures = new List { new CultureInfo("en"), new CultureInfo("tr") }; + var cultures = new List + { + new CultureInfo("cs"), + new CultureInfo("en"), + new CultureInfo("tr"), + new CultureInfo("zh-Hans") + }; + Configure(options => { options.DefaultRequestCulture = new RequestCulture("en"); diff --git a/modules/blogging/app/Volo.BloggingTestApp/Branding/BloggingTestAppBrandingProvider.cs b/modules/blogging/app/Volo.BloggingTestApp/Branding/BloggingTestAppBrandingProvider.cs new file mode 100644 index 0000000000..e5a8046ff7 --- /dev/null +++ b/modules/blogging/app/Volo.BloggingTestApp/Branding/BloggingTestAppBrandingProvider.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc.Localization; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; +using Volo.Blogging.Localization; + +namespace Volo.BloggingTestApp.Branding +{ + [Dependency(ReplaceServices = true)] + public class BloggingTestAppBrandingProvider : DefaultBrandingProvider + { + public IHtmlLocalizer L { get; set; } + public BloggingTestAppBrandingProvider(IHtmlLocalizer localizer) + { + L = localizer; + } + public override string AppName => L["Blogs"].Value; + } +} diff --git a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj index 040cfaf4a8..dd796956bd 100644 --- a/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj +++ b/modules/blogging/app/Volo.BloggingTestApp/Volo.BloggingTestApp.csproj @@ -16,10 +16,10 @@ - + - + diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissions.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissions.cs index e73dc334da..fae85acf88 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissions.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/BloggingPermissions.cs @@ -1,4 +1,6 @@ -namespace Volo.Blogging +using Volo.Abp.Reflection; + +namespace Volo.Blogging { public class BloggingPermissions { @@ -39,27 +41,7 @@ public static string[] GetAll() { - return new[] - { - GroupName, - Blogs.Default, - Blogs.Management, - Blogs.Delete, - Blogs.Update, - Blogs.Create, - Posts.Default, - Posts.Delete, - Posts.Update, - Posts.Create, - Tags.Default, - Tags.Delete, - Tags.Update, - Tags.Create, - Comments.Default, - Comments.Delete, - Comments.Update, - Comments.Create - }; + return ReflectionHelper.GetPublicConstantsRecursively(typeof(BloggingPermissions)); } } } 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 new file mode 100644 index 0000000000..9b781b029e --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Localization/Resources/Blogging/ApplicationContracts/cs.json @@ -0,0 +1,14 @@ +{ + "culture": "cs", + "texts": { + "Permission:Blogging": "Blog", + "Permission:Blogs": "Blogy", + "Permission:Posts": "Příspěvky", + "Permission:Tags": "Tagy", + "Permission:Comments": "Komentáře", + "Permission:Management": "Správa", + "Permission:Edit": "Upravit", + "Permission:Create": "Vytvořit", + "Permission:Delete": "Smazat" + } +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs index d8b9273f0c..b188fe4c3e 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Files/FileAppService.cs @@ -23,6 +23,17 @@ namespace Volo.Blogging.Files { Check.NotNullOrWhiteSpace(name, nameof(name)); + if (!Directory.Exists(Options.FileUploadLocalFolder)) + { + Directory.CreateDirectory(Options.FileUploadLocalFolder); + return Task.FromResult( + new RawFileDto + { + Bytes = new byte[0] + } + ); + } + var filePath = Path.Combine(Options.FileUploadLocalFolder, name); return Task.FromResult( @@ -53,6 +64,11 @@ namespace Volo.Blogging.Files var uniqueFileName = GenerateUniqueFileName(Path.GetExtension(input.Name)); var filePath = Path.Combine(Options.FileUploadLocalFolder, uniqueFileName); + if (!Directory.Exists(Options.FileUploadLocalFolder)) + { + Directory.CreateDirectory(Options.FileUploadLocalFolder); + } + File.WriteAllBytes(filePath, input.Bytes); //TODO: Previously was using WriteAllBytesAsync, but it's only in .netcore. return Task.FromResult(new FileUploadOutputDto diff --git a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs index 425fb58575..acc400ef3f 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs @@ -54,7 +54,7 @@ namespace Volo.Blogging.EntityFrameworkCore b.Property(x => x.Url).IsRequired().HasMaxLength(PostConsts.MaxUrlLength).HasColumnName(nameof(Post.Url)); b.Property(x => x.Content).IsRequired(false).HasMaxLength(PostConsts.MaxContentLength).HasColumnName(nameof(Post.Content)); - b.HasMany(question => question.Tags).WithOne().HasForeignKey(qt => qt.PostId); + b.HasMany(p => p.Tags).WithOne().HasForeignKey(qt => qt.PostId); b.HasOne().WithMany().IsRequired().HasForeignKey(p => p.BlogId); }); diff --git a/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs b/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs index ac7cc9e210..550bf059f5 100644 --- a/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs +++ b/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; @@ -44,10 +45,14 @@ namespace Volo.Blogging 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", "简体中文")); options.Resources .Get() .AddBaseTypes(typeof(AbpValidationResource)) - .AddBaseTypes(typeof(AbpUiModule)) + .AddBaseTypes(typeof(AbpUiResource)) .AddVirtualJson("/Localization/Resources/Blogging/Web"); }); diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/cs.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/cs.json new file mode 100644 index 0000000000..5e49b65288 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/cs.json @@ -0,0 +1,47 @@ +{ + "culture": "cs", + "texts": { + "Menu:Blogs": "Blogy", + "Menu:BlogManagement": "Správa blogu", + "Title": "Nadpis", + "Delete": "Smazat", + "Reply": "Odpovědět", + "ReplyTo": "Odpověď pro {0}", + "ContinueReading": "Číst dále", + "DaysAgo": "před {0} dny", + "YearsAgo": "před {0} roky", + "MonthsAgo": "před {0} měsíci", + "WeeksAgo": "před {0} týdny", + "MinutesAgo": "před {0} minutami", + "SecondsAgo": "před {0} sekundami", + "HoursAgo": "před {0} hodinami", + "Now": "nyní", + "Content": "Obsah", + "SeeAll": "Zobrazit vše", + "PopularTags": "Populární tagy", + "WiewsWithCount": "{0} zobrazení", + "LastPosts": "Poslední příspěvky", + "LeaveComment": "Přidat komentář", + "TagsInThisArticle": "Tagy v článku", + "Posts": "Příspěvky", + "Edit": "Upravit", + "BLOG": "BLOG", + "CommentDeletionWarningMessage": "Komentáře budou smazány.", + "PostDeletionWarningMessage": "Příspěvek bude smazán.", + "BlogDeletionWarningMessage": "Blog bude smazán.", + "AreYouSure": "Jste si jistí?", + "CommentWithCount": "{0} komentářů", + "Comment": "Komentář", + "ShareOnTwitter": "Sdílet na Twitteru", + "CoverImage": "Obrázek obálky", + "CreateANewPost": "Vytvořit nový příspěvek", + "CreateANewBlog": "Vytvořit nový blog", + "WhatIsNew": "Co je nového?", + "Name": "Název", + "ShortName": "Krátký název", + "CreationTime": "Vytvořeno", + "Description": "Popis", + "Blogs": "Blogy", + "Tags": "Tagy" + } +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json index 444c3d80af..b84b1f2483 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/en.json @@ -36,6 +36,12 @@ "CoverImage": "Cover Image", "CreateANewPost": "Create A New Post", "CreateANewBlog": "Create A New Blog", - "WhatIsNew": "What is new?" + "WhatIsNew": "What is new?", + "Name": "Name", + "ShortName": "Short Name", + "CreationTime": "Creation Time", + "Description": "Description", + "Blogs": "Blogs", + "Tags": "Tags" } } \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json index cf98e56b06..62416f49a5 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/pt-BR.json @@ -36,6 +36,12 @@ "CoverImage": "Imagem de Capa", "CreateANewPost": "Criar uma nova Postagem", "CreateANewBlog": "Criar um novo Blog", - "WhatIsNew": "O que há de novo?" + "WhatIsNew": "O que há de novo?", + "Name": "Nome", + "ShortName": "Nome Curto", + "CreationTime": "Hora da criação", + "Description": "Descrição", + "Blogs": "Blogs", + "Tags": "Etiquetas" } } \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json index dc5df7f0d0..7747638587 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/tr.json @@ -36,6 +36,12 @@ "CoverImage": "Kapak resmi", "CreateANewPost": "Yeni Yazı oluştur", "CreateANewBlog": "Yeni Blog Ekle", - "WhatIsNew": "Yeniler" + "WhatIsNew": "Yeniler", + "Name": "İsim", + "ShortName": "Kısa isim", + "CreationTime": "Oluşturulma zamanı", + "Description": "Açıklama", + "Blogs": "Bloglar", + "Tags": "Etiketler" } } \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/zh-Hans.json b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/zh-Hans.json index a67e325497..2ea614b648 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/zh-Hans.json +++ b/modules/blogging/src/Volo.Blogging.Web/Localization/Resources/Blogging/Web/zh-Hans.json @@ -36,6 +36,12 @@ "CoverImage": "封面图", "CreateANewPost": "创建一个新帖子", "CreateANewBlog": "创建一个新博客", - "WhatIsNew": "最新消息" - } - } \ No newline at end of file + "WhatIsNew": "最新消息", + "Name": "名称", + "ShortName": "简称", + "CreationTime": "创建时间", + "Description": "描述", + "Blogs": "博客", + "Tags": "标签" + } +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/BloggingPage.cs b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/BloggingPage.cs index 03214d1ee3..88133e0e84 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/BloggingPage.cs +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/BloggingPage.cs @@ -55,6 +55,11 @@ namespace Volo.Blogging.Pages.Blog public IHtmlContent RenderMarkdownToHtml(string content) { + if(content.IsNullOrWhiteSpace()) + { + return new HtmlString(""); + } + byte[] bytes = Encoding.Default.GetBytes(content); var utf8Content = Encoding.UTF8.GetString(bytes); diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml index fadaaab269..97f06b6dc0 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Index.cshtml @@ -1,12 +1,12 @@ @page @using Volo.Blogging.Pages.Blog -@*inherits BloggingPage*@ +@inherits BloggingPage @model IndexModel @{ ViewBag.PageTitle = "Blog"; }

- Blogs + @L["Blogs"]

    diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs index b35d07993d..2ccf8cff8f 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -61,7 +62,7 @@ namespace Volo.Blogging.Pages.Blog.Posts var editedPost = await _postAppService.UpdateAsync(Post.Id, post); var blog = await _blogAppService.GetAsync(editedPost.BlogId); - return Redirect(Url.Content($"~/blog/{blog.ShortName}/{editedPost.Url}")); + return Redirect(Url.Content($"~/blog/{WebUtility.UrlEncode(blog.ShortName)}/{WebUtility.UrlEncode(editedPost.Url)}")); } } diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml index c2aebc7192..6526c79ef1 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml @@ -166,7 +166,7 @@ @post.Title

    - @(GetShortContent(post.Content)) + @Html.Raw(GetShortContent(post.Content))

    } -
    +
    @@ -116,7 +116,7 @@
    -
    +
    @if (Model.Navigation == null || Model.Navigation.Content.IsNullOrEmpty()) @@ -132,6 +132,7 @@ project-name="@Model.ProjectName" project-format="@Model.Project.Format" selected-document-name="@Model.DocumentNameWithExtension" + language="@Model.LanguageCode" id="sidebar-scroll" class="nav nav-list">
} @@ -143,7 +144,6 @@ @if (Model.Document != null) {
-
@@ -183,6 +190,12 @@
+ @if (Model.DocumentLanguageIsDifferent) + { + + Document in the language you wanted is not found. Document in the default language is shown. + + } @Html.Raw(Model.Document.Content)
diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs index 378903d121..83a1f62f27 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.Extensions.Options; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; using Volo.Docs.Documents; using Volo.Docs.HtmlConverting; @@ -23,8 +24,17 @@ namespace Volo.Docs.Pages.Documents.Project [BindProperty(SupportsGet = true)] public string DocumentName { get; set; } + [BindProperty(SupportsGet = true)] + public string LanguageCode { get; set; } + + public string DefaultLanguageCode { get; set; } + public ProjectDto Project { get; set; } + public LanguageConfig LanguageConfig { get; set; } + + public List LanguageSelectListItems { get; set; } + public string DocumentNameWithExtension { get; private set; } public DocumentWithDetailsDto Document { get; private set; } @@ -37,27 +47,56 @@ namespace Volo.Docs.Pages.Documents.Project public VersionInfoViewModel LatestVersionInfo { get; private set; } + public string DocumentsUrlPrefix { get; set; } + + public bool DocumentLanguageIsDifferent { get; set; } + private readonly IDocumentAppService _documentAppService; private readonly IDocumentToHtmlConverterFactory _documentToHtmlConverterFactory; private readonly IProjectAppService _projectAppService; + private readonly DocsUrlOptions _options; public IndexModel( IDocumentAppService documentAppService, IDocumentToHtmlConverterFactory documentToHtmlConverterFactory, - IProjectAppService projectAppService) + IProjectAppService projectAppService, + IOptions options) { _documentAppService = documentAppService; _documentToHtmlConverterFactory = documentToHtmlConverterFactory; _projectAppService = projectAppService; + _options = options.Value; } - public async Task OnGetAsync() + public async Task OnGetAsync() { + DocumentsUrlPrefix = _options.GetFormattedRoutePrefix(); + await SetProjectAsync(); await SetProjectsAsync(); await SetVersionAsync(); + await SetLanguageList(); + + if (IsDefaultDocument()) + { + return RedirectToDefaultDocument(); + } + + if (!CheckLanguage()) + { + return RedirectToDefaultLanguage(); + } + await SetDocumentAsync(); await SetNavigationAsync(); + SetLanguageSelectListItems(); + + return Page(); + } + + private bool IsDefaultDocument() + { + return DocumentName == Project.DefaultDocumentName; } private async Task SetProjectAsync() @@ -65,6 +104,43 @@ namespace Volo.Docs.Pages.Documents.Project Project = await _projectAppService.GetAsync(ProjectName); } + private async Task SetLanguageList() + { + LanguageConfig = await _projectAppService.GetLanguageListAsync(ProjectName, Version); + SetDefaultLanguageCode(); + } + + private void SetDefaultLanguageCode() + { + DefaultLanguageCode = (LanguageConfig.Languages.FirstOrDefault(l => l.IsDefault) ?? LanguageConfig.Languages.First()).Code; + } + + private bool CheckLanguage() + { + return LanguageConfig.Languages.Any(l => l.Code == LanguageCode); + } + + private IActionResult RedirectToDefaultLanguage() + { + return RedirectToPage(new + { + projectName = ProjectName, + version = Version, + languageCode = DefaultLanguageCode + }); + } + + private IActionResult RedirectToDefaultDocument() + { + return RedirectToPage(new + { + projectName = ProjectName, + version = Version, + documentName = "", + languageCode = DefaultLanguageCode + }); + } + private async Task SetProjectsAsync() { var projects = await _projectAppService.GetListAsync(); @@ -72,7 +148,7 @@ namespace Volo.Docs.Pages.Documents.Project ProjectSelectItems = projects.Items.Select(p => new SelectListItem { Text = p.Name, - Value = p.Id != Project.Id ? "/documents/" + p.ShortName + "/" + DocsAppConsts.Latest : null, + Value = p.Id != Project.Id ? "/documents/" + LanguageCode + "/" + p.ShortName + "/" + DocsAppConsts.Latest : null, Selected = p.Id == Project.Id }).ToList(); } @@ -133,6 +209,7 @@ namespace Volo.Docs.Pages.Documents.Project new GetNavigationDocumentInput { ProjectId = Project.Id, + LanguageCode = LanguageCode, Version = Version } ); @@ -154,7 +231,7 @@ namespace Volo.Docs.Pages.Documents.Project version = DocsAppConsts.Latest; } - var link = "/documents/" + ProjectName + "/" + version; + var link = DocumentsUrlPrefix + LanguageCode + "/" + ProjectName + "/" + version; if (documentName != null) { @@ -178,21 +255,17 @@ namespace Volo.Docs.Pages.Documents.Project private async Task SetDocumentAsync() { - if (DocumentName.IsNullOrWhiteSpace()) - { - DocumentName = Project.DefaultDocumentName; - } - DocumentNameWithExtension = DocumentName + "." + Project.Format; try { - if (DocumentNameWithExtension.IsNullOrWhiteSpace()) + if (DocumentName.IsNullOrWhiteSpace()) { Document = await _documentAppService.GetDefaultAsync( new GetDefaultDocumentInput { ProjectId = Project.Id, + LanguageCode = LanguageCode, Version = Version } ); @@ -204,6 +277,7 @@ namespace Volo.Docs.Pages.Documents.Project { ProjectId = Project.Id, Name = DocumentNameWithExtension, + LanguageCode = LanguageCode, Version = Version } ); @@ -211,17 +285,49 @@ namespace Volo.Docs.Pages.Documents.Project } catch (DocumentNotFoundException) { - //TODO: Handle it! - throw; + if (LanguageCode != DefaultLanguageCode) + { + Document = await _documentAppService.GetAsync( + new GetDocumentInput + { + ProjectId = Project.Id, + Name = DocumentNameWithExtension, + LanguageCode = DefaultLanguageCode, + Version = Version + } + ); + + DocumentLanguageIsDifferent = true; + } + else + { + throw; + } } ConvertDocumentContentToHtml(); } + private void SetLanguageSelectListItems() + { + LanguageSelectListItems = new List(); + + foreach (var language in LanguageConfig.Languages) + { + LanguageSelectListItems.Add( + new SelectListItem( + language.DisplayName, + "/" + DocumentsUrlPrefix + language.Code + "/" + Project.ShortName + "/" + Version + "/" + DocumentName, + language.Code == LanguageCode + ) + ); + } + } + private void ConvertDocumentContentToHtml() { var converter = _documentToHtmlConverterFactory.Create(Document.Format ?? Project.Format); - var content = converter.Convert(Project, Document, GetSpecificVersionOrLatest()); + var content = converter.Convert(Project, Document, GetSpecificVersionOrLatest(), LanguageCode); content = HtmlNormalizer.ReplaceImageSources( content, diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js index 32ce1933a7..8eb2083749 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.js @@ -1,11 +1,11 @@ (function ($) { $(function () { - var initNavigationFilter = function (navigationContainerId) { var $navigation = $("#" + navigationContainerId); + var getShownDocumentLinks = function () { return $navigation.find(".mCSB_container > li a:visible").not(".tree-toggle"); }; diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs index 1f8bfeca77..76d1f9f68f 100644 --- a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs +++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs @@ -28,7 +28,7 @@ namespace Volo.Docs var project = await _projectRepository.FindAsync(_testData.PorjectId); project.ShouldNotBeNull(); - var document = await store.GetDocumentAsync(project, "index2", "0.123.0"); + var document = await store.GetDocumentAsync(project, "index2", "en", "0.123.0"); document.ShouldNotBeNull(); document.Title.ShouldBe("index2"); @@ -60,7 +60,7 @@ namespace Volo.Docs var project = await _projectRepository.FindAsync(_testData.PorjectId); project.ShouldNotBeNull(); - var documentResource = await store.GetResource(project, "index.md", "0.123.0"); + var documentResource = await store.GetResource(project, "index.md", "en", "0.123.0"); documentResource.ShouldNotBeNull(); documentResource.Content.ShouldBe(new byte[] 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 666c1e813f..71a466828b 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 @@ -8,8 +8,8 @@ - - + + diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/IFeatureAppService.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/IFeatureAppService.cs index a30b2397fa..2c337f1b2a 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/IFeatureAppService.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/IFeatureAppService.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; using JetBrains.Annotations; using Volo.Abp.Application.Services; diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/cs.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/cs.json new file mode 100644 index 0000000000..6a060a54c1 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/Localization/ApplicationContracts/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/cs.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/cs.json new file mode 100644 index 0000000000..6a060a54c1 --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/Localization/Domain/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs new file mode 100644 index 0000000000..ea0bfd16ee --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace Volo.Abp.FeatureManagement +{ + [RemoteService] + [Area("abp")] + public class FeaturesController : AbpController, IFeatureAppService + { + private readonly IFeatureAppService _featureAppService; + + public FeaturesController(IFeatureAppService featureAppService) + { + _featureAppService = featureAppService; + } + + public Task GetAsync(string providerName, string providerKey) + { + return _featureAppService.GetAsync(providerName, providerKey); + } + + public Task UpdateAsync(string providerName, string providerKey, UpdateFeaturesDto input) + { + return _featureAppService.UpdateAsync(providerName, providerKey, input); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/cs.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/cs.json new file mode 100644 index 0000000000..8cbecf08ab --- /dev/null +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "Features": "Funkce" + } +} \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/pt-BR.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/pt-BR.json index 6d746df04c..caaacd6fd4 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/pt-BR.json +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/pt-BR.json @@ -1,6 +1,6 @@ { "culture": "pt-BR", "texts": { - + "Features": "Funcionalidades" } } \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/zh-Hans.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/zh-Hans.json index 25137f1ee3..e9e1aab620 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/zh-Hans.json +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/zh-Hans.json @@ -1,6 +1,6 @@ { - "culture": "zh-Hans", - "texts": { - - } -} \ No newline at end of file + "culture": "zh-Hans", + "texts": { + "Features": "特性" + } +} 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 27d7612baa..5566f756d8 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 @@ -15,8 +15,8 @@ - - + + diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityPermissions.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityPermissions.cs index 339122c2a8..e22869295f 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityPermissions.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityPermissions.cs @@ -1,4 +1,6 @@ -namespace Volo.Abp.Identity +using Volo.Abp.Reflection; + +namespace Volo.Abp.Identity { public static class IdentityPermissions { @@ -29,21 +31,7 @@ public static string[] GetAll() { - return new[] - { - GroupName, - Roles.Default, - Roles.Create, - Roles.Update, - Roles.Delete, - Roles.ManagePermissions, - Users.Default, - Users.Create, - Users.Update, - Users.Delete, - Users.ManagePermissions, - UserLookup.Default - }; + return ReflectionHelper.GetPublicConstantsRecursively(typeof(IdentityPermissions)); } } } \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/cs.json b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/cs.json new file mode 100644 index 0000000000..8580872991 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/Localization/ApplicationContracts/cs.json @@ -0,0 +1,14 @@ +{ + "culture": "cs", + "texts": { + "Volo.Abp.Identity:010001": "Nemůžete smazat svůj vlastní účet!", + "Permission:IdentityManagement": "Správa identit", + "Permission:RoleManagement": "Správa rolí", + "Permission:Create": "Vytvořit", + "Permission:Edit": "Upravit", + "Permission:Delete": "Smazat", + "Permission:ChangePermissions": "Změnit oprávnění", + "Permission:UserManagement": "Správa uživatelů", + "Permission:UserLookup": "Vyhledání uživatele" + } +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Localization/Domain/cs.json b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Localization/Domain/cs.json new file mode 100644 index 0000000000..f6fdea467d --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/Localization/Domain/cs.json @@ -0,0 +1,33 @@ +{ + "culture": "cs", + "texts": { + "Identity.DefaultError": "Došlo k neznámé chybě.", + "Identity.ConcurrencyFailure": "Selhání optimistické souběžnosti, objekt byl změněn.", + "Identity.DuplicateEmail": "Email '{0}' již existuje.", + "Identity.DuplicateRoleName": "Role '{0}' již existuje.", + "Identity.DuplicateUserName": "Uživatelské jméno '{0}' již existuje.", + "Identity.InvalidEmail": "Email '{0}' je neplatný.", + "Identity.InvalidPasswordHasherCompatibilityMode": "Dodaný PasswordHasherCompatibilityMode je neplatný.", + "Identity.InvalidPasswordHasherIterationCount": "Počet iterací musí být kladné celé číslo.", + "Identity.InvalidRoleName": "Role '{0}' je neplatná.", + "Identity.InvalidToken": "Neplatný token.", + "Identity.InvalidUserName": "Uživatelské jméno '{0}' je neplatné, může obsahovat pouze písmena a číslice.", + "Identity.LoginAlreadyAssociated": "Uživatel s tímto přihlášením již existuje.", + "Identity.PasswordMismatch": "Chybné heslo.", + "Identity.PasswordRequiresDigit": "Hesla musí obsahovat alespoň jednu číslici ('0'-'9').", + "Identity.PasswordRequiresLower": "Hesla musí obsahovat alespoň jedno malé písmeno ('a'-'z').", + "Identity.PasswordRequiresNonAlphanumeric": "Hesla musí obsahovat alespoň jeden nealfanumerický znak.", + "Identity.PasswordRequiresUpper": "Hesla musí obsahovat alespoň jedno velké písmeno ('A'-'Z').", + "Identity.PasswordTooShort": "Hesla musí být dlouhá alespoň {0} znaků.", + "Identity.RoleNotFound": "Role {0} neexistuje.", + "Identity.UserAlreadyHasPassword": "Uživatel již má nastavené heslo.", + "Identity.UserAlreadyInRole": "Uživatel již je v roli '{0}'.", + "Identity.UserLockedOut": "Uživatel je uzamčen.", + "Identity.UserLockoutNotEnabled": "Uzamčení není umožněno pro tohoto uživatele.", + "Identity.UserNameNotFound": "Uživatel {0} neexistuje.", + "Identity.UserNotInRole": "Uživatel není v roli '{0}'.", + "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." + } +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs index 7e0085189f..d0e39030bd 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityRoleController.cs @@ -10,6 +10,7 @@ namespace Volo.Abp.Identity [RemoteService] [Area("identity")] [ControllerName("Role")] + [Route("api/identity/role")] public class IdentityRoleController : AbpController, IIdentityRoleAppService { private readonly IIdentityRoleAppService _roleAppService; @@ -19,31 +20,41 @@ namespace Volo.Abp.Identity _roleAppService = roleAppService; } + [HttpGet] + [Route("{id}")] public virtual Task GetAsync(Guid id) { return _roleAppService.GetAsync(id); } + [HttpGet] public virtual Task> GetListAsync(GetIdentityRolesInput input) { return _roleAppService.GetListAsync(input); } + [HttpPost] public virtual Task CreateAsync(IdentityRoleCreateDto input) { return _roleAppService.CreateAsync(input); } + [HttpPut] + [Route("{id}")] public virtual Task UpdateAsync(Guid id, IdentityRoleUpdateDto input) { return _roleAppService.UpdateAsync(id, input); } + [HttpDelete] + [Route("{id}")] public virtual Task DeleteAsync(Guid id) { return _roleAppService.DeleteAsync(id); } + [HttpGet] + [Route("all")] public virtual Task> GetAllListAsync() { return _roleAppService.GetAllListAsync(); diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserController.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserController.cs index acbc211b0f..18fd43e1fe 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserController.cs +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Application.Dtos; @@ -9,6 +9,7 @@ namespace Volo.Abp.Identity [RemoteService] [Area("identity")] [ControllerName("User")] + [Route("api/identity/user")] public class IdentityUserController : AbpController, IIdentityUserAppService { private readonly IIdentityUserAppService _userAppService; @@ -18,48 +19,62 @@ namespace Volo.Abp.Identity _userAppService = userAppService; } + [HttpGet] + [Route("{id}")] public virtual Task GetAsync(Guid id) { return _userAppService.GetAsync(id); } + [HttpGet] public virtual Task> GetListAsync(GetIdentityUsersInput input) { return _userAppService.GetListAsync(input); } + [HttpPost] public virtual Task CreateAsync(IdentityUserCreateDto input) { return _userAppService.CreateAsync(input); } + [HttpPut] + [Route("{id}")] public virtual Task UpdateAsync(Guid id, IdentityUserUpdateDto input) { return _userAppService.UpdateAsync(id, input); } + [HttpDelete] + [Route("{id}")] public virtual Task DeleteAsync(Guid id) { return _userAppService.DeleteAsync(id); } + [HttpGet] + [Route("{id}/roles")] public virtual Task> GetRolesAsync(Guid id) { return _userAppService.GetRolesAsync(id); } + [HttpPut] + [Route("{id}/roles")] public virtual Task UpdateRolesAsync(Guid id, IdentityUserUpdateRolesDto input) { return _userAppService.UpdateRolesAsync(id, input); } [HttpGet] + [Route("by-username/{userName}")] public virtual Task FindByUsernameAsync(string username) { return _userAppService.FindByUsernameAsync(username); } [HttpGet] + [Route("by-email/{email}")] public virtual Task FindByEmailAsync(string email) { return _userAppService.FindByEmailAsync(email); diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Localization/Resources/AbpIdentity/cs.json b/modules/identity/src/Volo.Abp.Identity.Web/Localization/Resources/AbpIdentity/cs.json new file mode 100644 index 0000000000..30217206f4 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Web/Localization/Resources/AbpIdentity/cs.json @@ -0,0 +1,39 @@ +{ + "culture": "cs", + "texts": { + "Menu:IdentityManagement": "Správa identit", + "Users": "Uživatelé", + "NewUser": "Nový uživatel", + "UserName": "Uživatelské jméno", + "EmailAddress": "Email", + "PhoneNumber": "Telefonní číslo", + "UserInformations": "Uživatelské informace", + "DisplayName:IsDefault": "Výchozí", + "DisplayName:IsStatic": "Statické", + "DisplayName:IsPublic": "Veřejné", + "Roles": "Role", + "Password": "Heslo", + "PersonalInfo": "Můj profil", + "PersonalSettings": "Osobní nastavení", + "UserDeletionConfirmationMessage": "Uživatel '{0}' bude smazán. Jste si jistí?", + "RoleDeletionConfirmationMessage": "Role '{0}' bude smazána. Jste si jistí?", + "DisplayName:RoleName": "Název role", + "DisplayName:UserName": "Uživatelské jméno", + "DisplayName:Name": "Jméno", + "DisplayName:Surname": "Příjmení", + "DisplayName:Password": "Heslo", + "DisplayName:Email": "Email", + "DisplayName:PhoneNumber": "Telefonní číslo", + "DisplayName:TwoFactorEnabled": "Dvoufázové ověřování", + "DisplayName:LockoutEnabled": "Zamčení účtu po neúspěšných pokusech", + "NewRole": "Nová role", + "RoleName": "Název role", + "CreationTime": "Vytvořeno", + "Permissions": "Oprávnění", + "DisplayName:CurrentPassword": "Stávající heslo", + "DisplayName:NewPassword": "Nové heslo", + "DisplayName:NewPasswordConfirm": "Potvrzení nového hesla", + "PasswordChangedMessage": "Vaše heslo bylo úspěšně změněno.", + "PersonalSettingsSavedMessage": "Vaše osobní nastavení bylo úspěšně uloženo." + } +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js index 476b484128..385b53f257 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js @@ -57,7 +57,17 @@ } }, { - data: "name" + data: "name", + render: function (data, type, row) { + var name = '' + data + ''; + if (row.isDefault) { + name += '' + l('DisplayName:IsDefault') + ''; + } + if (row.isPublic) { + name += '' + l('DisplayName:IsPublic') + ''; + } + return name; + } } ] })); 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 8d4ce0c3fc..7a9219d556 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 @@ -22,8 +22,8 @@ - - + + diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs index dc06ce130d..7ddc619cfa 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs @@ -3,7 +3,6 @@ using IdentityServer4.Stores; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; using Volo.Abp.Caching; -using Volo.Abp.Domain; using Volo.Abp.Identity; using Volo.Abp.IdentityServer.Clients; using Volo.Abp.Modularity; diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs index eb14ebc2ba..ab8bb6ffbd 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs @@ -22,15 +22,16 @@ namespace Volo.Abp.IdentityServer.Grants public virtual async Task StoreAsync(IdentityServer4.Models.PersistedGrant grant) { - var entity = _objectMapper.Map(grant); - var existing = await _persistentGrantRepository.FindByKeyAsync(grant.Key); - if (existing == null) + var entity = await _persistentGrantRepository.FindByKeyAsync(grant.Key); + if (entity == null) { + entity = _objectMapper.Map(grant); entity.Id = _guidGenerator.Create(); await _persistentGrantRepository.InsertAsync(entity); } else { + _objectMapper.Map(grant, entity); await _persistentGrantRepository.UpdateAsync(entity); } } diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Clients/PersistentGrant_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Clients/PersistentGrant_Tests.cs index 9a87e30d64..4621daa3f4 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Clients/PersistentGrant_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/Clients/PersistentGrant_Tests.cs @@ -78,6 +78,46 @@ namespace Volo.Abp.IdentityServer.Clients persistedGrant.CreationTime.Second.ShouldBe(21); } + + [Fact] + public async Task StoreAsync_Should_Store_PersistedGrant_When_Exists() + { + //Act + await _persistedGrantStore.StoreAsync(new PersistedGrant + { + Key = "PersistedGrantKey1", + ClientId = "TestClientId-PersistedGrantKey1", + Type = "TestType-PersistedGrantKey1", + SubjectId = "TestSubject", + Data = "TestData-PersistedGrantKey1", + Expiration = new DateTime(2018, 1, 6, 21, 22, 23), + CreationTime = new DateTime(2018, 1, 5, 19, 20, 21) + }); + + //Assert + var persistedGrant = await _persistedGrantStore.GetAsync("PersistedGrantKey1"); + persistedGrant.Key.ShouldBe("PersistedGrantKey1"); + persistedGrant.ClientId.ShouldBe("TestClientId-PersistedGrantKey1"); + persistedGrant.Type.ShouldBe("TestType-PersistedGrantKey1"); + persistedGrant.SubjectId.ShouldBe("TestSubject"); + persistedGrant.Data.ShouldBe("TestData-PersistedGrantKey1"); + + persistedGrant.Expiration.HasValue.ShouldBe(true); + persistedGrant.Expiration.Value.Year.ShouldBe(2018); + persistedGrant.Expiration.Value.Month.ShouldBe(1); + persistedGrant.Expiration.Value.Day.ShouldBe(6); + persistedGrant.Expiration.Value.Hour.ShouldBe(21); + persistedGrant.Expiration.Value.Minute.ShouldBe(22); + persistedGrant.Expiration.Value.Second.ShouldBe(23); + + persistedGrant.CreationTime.Year.ShouldBe(2018); + persistedGrant.CreationTime.Month.ShouldBe(1); + persistedGrant.CreationTime.Day.ShouldBe(5); + persistedGrant.CreationTime.Hour.ShouldBe(19); + persistedGrant.CreationTime.Minute.ShouldBe(20); + persistedGrant.CreationTime.Second.ShouldBe(21); + } + [Fact] public async Task GetAllAsync_Should_Get_All_PersistedGrants_For_A_Given_SubjectId() { 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 faa5eb21aa..5a12ee07ed 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 @@ -20,7 +20,7 @@ - + diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/cs.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/cs.json new file mode 100644 index 0000000000..4c500d06eb --- /dev/null +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/cs.json @@ -0,0 +1,10 @@ +{ + "culture": "cs", + "texts": { + "Permissions": "Oprávnění", + "OnlyProviderPermissons": "Pouze tento poskytovatel", + "All": "Všechna", + "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/Localization/Resources/AbpPermissionManagement/pt-BR.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/pt-BR.json index 93adef2871..ac1a771466 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/pt-BR.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/pt-BR.json @@ -3,6 +3,8 @@ "texts": { "Permissions": "Permissões", "OnlyProviderPermissons": "Apenas este provedor", - "All": "Todos" + "All": "Todos", + "SelectAllInAllTabs": "Conceder todas as permissões", + "SelectAllInThisTab": "Selecionar todos" } } \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json index 8384078b86..11c52c43ed 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Localization/Resources/AbpPermissionManagement/zh-Hans.json @@ -1,8 +1,10 @@ { - "culture": "zh-Hans", - "texts": { - "Permissions": "权限", - "OnlyProviderPermissons": "只有这个提供商", - "All": "所有" - } - } \ No newline at end of file + "culture": "zh-Hans", + "texts": { + "Permissions": "权限", + "OnlyProviderPermissons": "只有这个提供商", + "All": "所有", + "SelectAllInAllTabs": "授予所有权限", + "SelectAllInThisTab": "全选" + } +} 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 ca64ea94f2..8285a006ed 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.Tests/Volo.Abp.PermissionManagement.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo.Abp.PermissionManagement.Tests.csproj index a0ba681b8d..f6846f3678 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo.Abp.PermissionManagement.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Tests/Volo.Abp.PermissionManagement.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/cs.json b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/cs.json new file mode 100644 index 0000000000..a0357eda0c --- /dev/null +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Localization/Resources/AbpSettingManagement/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + "Settings": "Nastavení" + } +} \ No newline at end of file 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 79ed300398..9d622e6a7a 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 @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using System.Linq; @@ -11,17 +12,21 @@ namespace Volo.Abp.SettingManagement.Web.Navigation { public class SettingManagementMainMenuContributor : IMenuContributor { - public Task ConfigureMenuAsync(MenuConfigurationContext context) + public async Task ConfigureMenuAsync(MenuConfigurationContext context) { if (context.Menu.Name != StandardMenus.Main) { - return Task.CompletedTask; + return; } var settingManagementPageOptions = context.ServiceProvider.GetRequiredService>().Value; - if (!settingManagementPageOptions.Contributors.Any()) + var settingPageCreationContext = new SettingPageCreationContext(context.ServiceProvider); + if ( + !settingManagementPageOptions.Contributors.Any() || + !(await CheckAnyOfPagePermissionsGranted(settingManagementPageOptions, settingPageCreationContext)) + ) { - return Task.CompletedTask; + return; } var l = context.ServiceProvider.GetRequiredService>(); @@ -36,8 +41,20 @@ namespace Volo.Abp.SettingManagement.Web.Navigation icon: "fa fa-cog" ) ); + } - return Task.CompletedTask; + private async Task CheckAnyOfPagePermissionsGranted( + SettingManagementPageOptions settingManagementPageOptions, + SettingPageCreationContext settingPageCreationContext) + { + foreach (var contributor in settingManagementPageOptions.Contributors) + { + if (await contributor.CheckPermissionsAsync(settingPageCreationContext)) + { + return true; + } + } + return false; } } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/ISettingPageContributor.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/ISettingPageContributor.cs index e641ee2ade..f6260cc816 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/ISettingPageContributor.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Web/Pages/SettingManagement/ISettingPageContributor.cs @@ -5,5 +5,7 @@ namespace Volo.Abp.SettingManagement.Web.Pages.SettingManagement public interface ISettingPageContributor { Task ConfigureAsync(SettingPageCreationContext context); + + Task CheckPermissionsAsync(SettingPageCreationContext context); } } \ No newline at end of file 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 43e0ceb087..53ff012ea6 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 @@ -19,7 +19,7 @@ - + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/cs.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/cs.json new file mode 100644 index 0000000000..415294c34a --- /dev/null +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/cs.json @@ -0,0 +1,11 @@ +{ + "culture": "cs", + "texts": { + "Permission:TenantManagement": "Správa tenantů", + "Permission:Create": "Vytvořit", + "Permission:Edit": "Upravit", + "Permission:Delete": "Smazat", + "Permission:ManageConnectionStrings": "Spravovat connection stringy", + "Permission:ManageFeatures": "Spravovat funkce" + } +} \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/pt-BR.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/pt-BR.json index bf461ab29e..6631be849a 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/pt-BR.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/pt-BR.json @@ -5,6 +5,7 @@ "Permission:Create": "Criar", "Permission:Edit": "Editar", "Permission:Delete": "Excluir", + "Permission:ManageConnectionStrings": "Gerenciar conexões (connection strings)", "Permission:ManageFeatures": "Gerenciar Funcionalidades" } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/zh-Hans.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/zh-Hans.json index e670d9f206..ca1c428755 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/zh-Hans.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/zh-Hans.json @@ -5,6 +5,7 @@ "Permission:Create": "创建", "Permission:Edit": "编辑", "Permission:Delete": "删除", + "Permission:ManageConnectionStrings": "管理连接字符串", "Permission:ManageFeatures": "管理功能" } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs index 6bc90271bc..49afb47c25 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs @@ -1,4 +1,6 @@ -namespace Volo.Abp.TenantManagement +using Volo.Abp.Reflection; + +namespace Volo.Abp.TenantManagement { public static class TenantManagementPermissions { @@ -16,16 +18,7 @@ public static string[] GetAll() { - return new[] - { - GroupName, - Tenants.Default, - Tenants.Create, - Tenants.Update, - Tenants.Delete, - Tenants.ManageFeatures, - Tenants.ManageConnectionStrings - }; + return ReflectionHelper.GetPublicConstantsRecursively(typeof(TenantManagementPermissions)); } } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs index 855fa24bb3..1549664c27 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs @@ -5,6 +5,7 @@ using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AutoMapper; using Volo.Abp.FeatureManagement; +using Volo.Abp.FeatureManagement.Localization; using Volo.Abp.Localization; using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Modularity; @@ -47,6 +48,7 @@ namespace Volo.Abp.TenantManagement.Web .Get() .AddBaseTypes( typeof(AbpValidationResource), + typeof(AbpFeatureManagementResource), typeof(AbpUiResource) ).AddVirtualJson("/Localization/Resources/AbpTenantManagement/Web"); }); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/cs.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/cs.json new file mode 100644 index 0000000000..df819c5798 --- /dev/null +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Localization/Resources/AbpTenantManagement/Web/cs.json @@ -0,0 +1,14 @@ +{ + "culture": "cs", + "texts": { + "Menu:TenantManagement": "Správa tenantů", + "Tenants": "Tenanty", + "NewTenant": "Nový tenant", + "TenantName": "Název tenantu", + "DisplayName:TenantName": "Název tenantu", + "TenantDeletionConfirmationMessage": "Tenant '{0}' bude smazán. Jste si jistí?", + "ConnectionStrings": "Connection stringy", + "DisplayName:DefaultConnectionString": "Výchozí connection string", + "DisplayName:UseSharedDatabase": "Použít sdílenou databázi" + } +} \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs index a2beeda27d..a4db743263 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/ConnectionStringsModal.cshtml.cs @@ -52,7 +52,7 @@ namespace Volo.Abp.TenantManagement.Web.Pages.TenantManagement.Tenants public bool UseSharedDatabase { get; set; } - [StringLength(TenantConnectionStringConsts.MaxNameLength)] + [StringLength(TenantConnectionStringConsts.MaxValueLength)] public string DefaultConnectionString { get; set; } } } 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 6a43666860..85d334e610 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 @@ -18,8 +18,8 @@ - - + + diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUsersDomainModule.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUsersDomainModule.cs index d3d6a01185..962c08c018 100644 --- a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUsersDomainModule.cs +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/AbpUsersDomainModule.cs @@ -1,12 +1,12 @@ -using Volo.Abp.Modularity; -using Volo.Abp.Security; +using Volo.Abp.Domain; +using Volo.Abp.Modularity; namespace Volo.Abp.Users { [DependsOn( typeof(AbpUsersDomainSharedModule), typeof(AbpUsersAbstractionModule), - typeof(AbpSecurityModule) + typeof(AbpDddDomainModule) )] public class AbpUsersDomainModule : AbpModule { 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 8571a8bd34..1bbdc59392 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/nupkg/common.ps1 b/nupkg/common.ps1 index e83acaa57c..e4ecc832fe 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -37,10 +37,12 @@ $projects = ( "framework/src/Volo.Abp.AspNetCore.Mvc.UI", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling", + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Dashboards", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic", "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared", + "framework/src/Volo.Abp.AspNetCore.Mvc.UI.Widgets", "framework/src/Volo.Abp.AspNetCore.TestBase", "framework/src/Volo.Abp.Auditing", "framework/src/Volo.Abp.Authorization", @@ -53,6 +55,7 @@ $projects = ( "framework/src/Volo.Abp.BackgroundWorkers", "framework/src/Volo.Abp.Caching", "framework/src/Volo.Abp.Castle.Core", + "framework/src/Volo.Abp.Cli.Core", "framework/src/Volo.Abp.Cli", "framework/src/Volo.Abp.Core", "framework/src/Volo.Abp.Data", @@ -63,9 +66,11 @@ $projects = ( "framework/src/Volo.Abp.EntityFrameworkCore.SqlServer", "framework/src/Volo.Abp.EntityFrameworkCore.MySQL", "framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql", + "framework/src/Volo.Abp.EntityFrameworkCore.Sqlite", "framework/src/Volo.Abp.EventBus", "framework/src/Volo.Abp.EventBus.RabbitMQ", "framework/src/Volo.Abp.Features", + "framework/src/Volo.Abp.FluentValidation", "framework/src/Volo.Abp.Guids", "framework/src/Volo.Abp.HangFire", "framework/src/Volo.Abp.Http", @@ -85,6 +90,7 @@ $projects = ( "framework/src/Volo.Abp.Serialization", "framework/src/Volo.Abp.Settings", "framework/src/Volo.Abp.Sms", + "framework/src/Volo.Abp.Specifications", "framework/src/Volo.Abp.TestBase", "framework/src/Volo.Abp.Threading", "framework/src/Volo.Abp.Timing", diff --git a/samples/BookStore/.gitignore b/samples/BookStore/.gitignore index 78d93c60d8..5e2a431b54 100644 --- a/samples/BookStore/.gitignore +++ b/samples/BookStore/.gitignore @@ -252,4 +252,7 @@ paket-files/ *.sln.iml # BookStore -src/Acme.BookStore.Web/Logs/logs.txt +src/Acme.BookStore.Web/Logs/* +src/Acme.BookStore.Web.Host/Logs/* +src/Acme.BookStore.IdentityServer/Logs/* +src/Acme.BookStore.HttpApi.Host/Logs/* \ No newline at end of file diff --git a/samples/BookStore/Acme.BookStore.sln b/samples/BookStore/Acme.BookStore.sln index 74836646a2..1de66eeb59 100644 --- a/samples/BookStore/Acme.BookStore.sln +++ b/samples/BookStore/Acme.BookStore.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.1 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28922.388 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain", "src\Acme.BookStore.Domain\Acme.BookStore.Domain.csproj", "{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}" EndProject @@ -19,7 +19,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Application. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Web.Tests", "test\Acme.BookStore.Web.Tests\Acme.BookStore.Web.Tests.csproj", "{5F1B28C6-8D0C-4155-92D0-252F7EA5F674}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Acme.BookStore.EntityFrameworkCore.DbMigrations", "src\Acme.BookStore.EntityFrameworkCore.DbMigrations\Acme.BookStore.EntityFrameworkCore.DbMigrations.csproj", "{0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.EntityFrameworkCore.DbMigrations", "src\Acme.BookStore.EntityFrameworkCore.DbMigrations\Acme.BookStore.EntityFrameworkCore.DbMigrations.csproj", "{0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain.Shared", "src\Acme.BookStore.Domain.Shared\Acme.BookStore.Domain.Shared.csproj", "{42F719ED-8413-4895-B5B4-5AB56079BC66}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Application.Contracts", "src\Acme.BookStore.Application.Contracts\Acme.BookStore.Application.Contracts.csproj", "{520659C8-C734-4298-A3DA-B539DB9DFC0B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.HttpApi", "src\Acme.BookStore.HttpApi\Acme.BookStore.HttpApi.csproj", "{4164BDF7-F527-4E85-9CE6-E3C2D7426A27}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.HttpApi.Client", "src\Acme.BookStore.HttpApi.Client\Acme.BookStore.HttpApi.Client.csproj", "{3B5A0094-670D-4BB1-BFDD-61B88A8773DC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.EntityFrameworkCore.Tests", "test\Acme.BookStore.EntityFrameworkCore.Tests\Acme.BookStore.EntityFrameworkCore.Tests.csproj", "{1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.TestBase", "test\Acme.BookStore.TestBase\Acme.BookStore.TestBase.csproj", "{91853F21-9CD9-4132-BC29-A7D5D84FFFE7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.Domain.Tests", "test\Acme.BookStore.Domain.Tests\Acme.BookStore.Domain.Tests.csproj", "{E512F4D9-9375-480F-A2F6-A46509F9D824}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.HttpApi.Client.ConsoleTestApp", "test\Acme.BookStore.HttpApi.Client.ConsoleTestApp\Acme.BookStore.HttpApi.Client.ConsoleTestApp.csproj", "{EF480016-9127-4916-8735-D2466BDBC582}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Acme.BookStore.DbMigrator", "src\Acme.BookStore.DbMigrator\Acme.BookStore.DbMigrator.csproj", "{AA94D832-1CCC-4715-95A9-A483F23A1A5D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -55,6 +73,42 @@ Global {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Debug|Any CPU.Build.0 = Debug|Any CPU {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Release|Any CPU.Build.0 = Release|Any CPU + {42F719ED-8413-4895-B5B4-5AB56079BC66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42F719ED-8413-4895-B5B4-5AB56079BC66}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42F719ED-8413-4895-B5B4-5AB56079BC66}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42F719ED-8413-4895-B5B4-5AB56079BC66}.Release|Any CPU.Build.0 = Release|Any CPU + {520659C8-C734-4298-A3DA-B539DB9DFC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {520659C8-C734-4298-A3DA-B539DB9DFC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {520659C8-C734-4298-A3DA-B539DB9DFC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {520659C8-C734-4298-A3DA-B539DB9DFC0B}.Release|Any CPU.Build.0 = Release|Any CPU + {4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4164BDF7-F527-4E85-9CE6-E3C2D7426A27}.Release|Any CPU.Build.0 = Release|Any CPU + {3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B5A0094-670D-4BB1-BFDD-61B88A8773DC}.Release|Any CPU.Build.0 = Release|Any CPU + {1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81}.Release|Any CPU.Build.0 = Release|Any CPU + {91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91853F21-9CD9-4132-BC29-A7D5D84FFFE7}.Release|Any CPU.Build.0 = Release|Any CPU + {E512F4D9-9375-480F-A2F6-A46509F9D824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E512F4D9-9375-480F-A2F6-A46509F9D824}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E512F4D9-9375-480F-A2F6-A46509F9D824}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E512F4D9-9375-480F-A2F6-A46509F9D824}.Release|Any CPU.Build.0 = Release|Any CPU + {EF480016-9127-4916-8735-D2466BDBC582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF480016-9127-4916-8735-D2466BDBC582}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF480016-9127-4916-8735-D2466BDBC582}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF480016-9127-4916-8735-D2466BDBC582}.Release|Any CPU.Build.0 = Release|Any CPU + {AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA94D832-1CCC-4715-95A9-A483F23A1A5D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -67,6 +121,15 @@ Global {50B2631D-129C-47B3-A587-029CCD6099BC} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} {5F1B28C6-8D0C-4155-92D0-252F7EA5F674} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {42F719ED-8413-4895-B5B4-5AB56079BC66} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {520659C8-C734-4298-A3DA-B539DB9DFC0B} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {4164BDF7-F527-4E85-9CE6-E3C2D7426A27} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {3B5A0094-670D-4BB1-BFDD-61B88A8773DC} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {1FE30EB9-74A9-47F5-A9F6-7B1FAB672D81} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} + {91853F21-9CD9-4132-BC29-A7D5D84FFFE7} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} + {E512F4D9-9375-480F-A2F6-A46509F9D824} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} + {EF480016-9127-4916-8735-D2466BDBC582} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} + {AA94D832-1CCC-4715-95A9-A483F23A1A5D} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F} diff --git a/samples/BookStore/common.props b/samples/BookStore/common.props index 771bc6bd29..7585f303ba 100644 --- a/samples/BookStore/common.props +++ b/samples/BookStore/common.props @@ -1,7 +1,7 @@ latest - 0.16.0 + 1.0.0 $(NoWarn);CS1591 \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Application.Contracts/Acme.BookStore.Application.Contracts.csproj b/samples/BookStore/src/Acme.BookStore.Application.Contracts/Acme.BookStore.Application.Contracts.csproj new file mode 100644 index 0000000000..f08af2efac --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Application.Contracts/Acme.BookStore.Application.Contracts.csproj @@ -0,0 +1,21 @@ + + + + + + netstandard2.0 + Acme.BookStore + + + + + + + + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.Application.Contracts/BookDto.cs b/samples/BookStore/src/Acme.BookStore.Application.Contracts/BookDto.cs new file mode 100644 index 0000000000..5a94fbce1e --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Application.Contracts/BookDto.cs @@ -0,0 +1,16 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Acme.BookStore +{ + public class BookDto : AuditedEntityDto + { + public string Name { get; set; } + + public BookType Type { get; set; } + + public DateTime PublishDate { get; set; } + + public float Price { get; set; } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Application.Contracts/BookStoreApplicationContractsModule.cs b/samples/BookStore/src/Acme.BookStore.Application.Contracts/BookStoreApplicationContractsModule.cs new file mode 100644 index 0000000000..f8af658d37 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Application.Contracts/BookStoreApplicationContractsModule.cs @@ -0,0 +1,20 @@ +using Volo.Abp.FeatureManagement; +using Volo.Abp.Identity; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement; +using Volo.Abp.TenantManagement; + +namespace Acme.BookStore +{ + [DependsOn( + typeof(BookStoreDomainSharedModule), + typeof(AbpFeatureManagementApplicationContractsModule), + typeof(AbpIdentityApplicationContractsModule), + typeof(AbpPermissionManagementApplicationContractsModule), + typeof(AbpTenantManagementApplicationContractsModule) + )] + public class BookStoreApplicationContractsModule : AbpModule + { + + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Application.Contracts/CreateUpdateBookDto.cs b/samples/BookStore/src/Acme.BookStore.Application.Contracts/CreateUpdateBookDto.cs new file mode 100644 index 0000000000..8dc38a9e31 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Application.Contracts/CreateUpdateBookDto.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Acme.BookStore +{ + public class CreateUpdateBookDto + { + [Required] + [StringLength(128)] + public string Name { get; set; } + + [Required] + public BookType Type { get; set; } = BookType.Undefined; + + [Required] + public DateTime PublishDate { get; set; } + + [Required] + public float Price { get; set; } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Application/IBookAppService.cs b/samples/BookStore/src/Acme.BookStore.Application.Contracts/IBookAppService.cs similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Application/IBookAppService.cs rename to samples/BookStore/src/Acme.BookStore.Application.Contracts/IBookAppService.cs diff --git a/samples/BookStore/src/Acme.BookStore.Application/Acme.BookStore.Application.csproj b/samples/BookStore/src/Acme.BookStore.Application/Acme.BookStore.Application.csproj index 9926530966..4b924ed040 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/Acme.BookStore.Application.csproj +++ b/samples/BookStore/src/Acme.BookStore.Application/Acme.BookStore.Application.csproj @@ -9,10 +9,14 @@ - - - - + + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.Application/BookDto.cs b/samples/BookStore/src/Acme.BookStore.Application/BookDto.cs deleted file mode 100644 index 42581b95fb..0000000000 --- a/samples/BookStore/src/Acme.BookStore.Application/BookDto.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Volo.Abp.Application.Dtos; -using Volo.Abp.AutoMapper; - -namespace Acme.BookStore -{ - [AutoMapFrom(typeof(Book))] - public class BookDto : AuditedEntityDto - { - public string Name { get; set; } - - public BookType Type { get; set; } - - public DateTime PublishDate { get; set; } - - public float Price { get; set; } - } -} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationAutoMapperProfile.cs b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationAutoMapperProfile.cs index 2d1a44a8c5..9bc08930cf 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationAutoMapperProfile.cs +++ b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationAutoMapperProfile.cs @@ -6,7 +6,8 @@ namespace Acme.BookStore { public BookStoreApplicationAutoMapperProfile() { - //Configure your AutoMapper mapping configuration here... + CreateMap(); + CreateMap(); } } } diff --git a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs index 4203ef0ccd..a97917f774 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs +++ b/samples/BookStore/src/Acme.BookStore.Application/BookStoreApplicationModule.cs @@ -9,6 +9,7 @@ namespace Acme.BookStore { [DependsOn( typeof(BookStoreDomainModule), + typeof(BookStoreApplicationContractsModule), typeof(AbpIdentityApplicationModule), typeof(AbpPermissionManagementApplicationModule), typeof(AbpTenantManagementApplicationModule), @@ -20,6 +21,10 @@ namespace Acme.BookStore { Configure(options => { + /* Use `true` for the `validate` parameter if you want to + * validate the profile on application startup. + * See http://docs.automapper.org/en/stable/Configuration-validation.html for more info + * about the configuration validation. */ options.AddProfile(); }); } diff --git a/samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs b/samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs deleted file mode 100644 index 0d5dae71cb..0000000000 --- a/samples/BookStore/src/Acme.BookStore.Application/CreateUpdateBookDto.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Volo.Abp.AutoMapper; - -namespace Acme.BookStore -{ - [AutoMapTo(typeof(Book))] - [AutoMapFrom(typeof(BookDto))] - public class CreateUpdateBookDto - { - [Required] - [StringLength(128)] - public string Name { get; set; } - - [Required] - public BookType Type { get; set; } = BookType.Undefined; - - [Required] - public DateTime PublishDate { get; set; } - - [Required] - public float Price { get; set; } - } -} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissionDefinitionProvider.cs b/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissionDefinitionProvider.cs index 9a3c6bf18f..aaf784e56e 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissionDefinitionProvider.cs +++ b/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissionDefinitionProvider.cs @@ -1,4 +1,4 @@ -using Acme.BookStore.Localization.BookStore; +using Acme.BookStore.Localization; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Localization; diff --git a/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissions.cs b/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissions.cs index b1ec575904..b05c4883bd 100644 --- a/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissions.cs +++ b/samples/BookStore/src/Acme.BookStore.Application/Permissions/BookStorePermissions.cs @@ -1,6 +1,4 @@ -using System; - -namespace Acme.BookStore.Permissions +namespace Acme.BookStore.Permissions { public static class BookStorePermissions { @@ -8,11 +6,5 @@ namespace Acme.BookStore.Permissions //Add your own permission names. Example: //public const string MyPermission1 = GroupName + ".MyPermission1"; - - public static string[] GetAll() - { - //Return an array of all permissions - return Array.Empty(); - } } } \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.DbMigrator/Acme.BookStore.DbMigrator.csproj b/samples/BookStore/src/Acme.BookStore.DbMigrator/Acme.BookStore.DbMigrator.csproj new file mode 100644 index 0000000000..7df37ca0a7 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.DbMigrator/Acme.BookStore.DbMigrator.csproj @@ -0,0 +1,34 @@ + + + + + + Exe + netcoreapp2.2 + + + + + + + + + PreserveNewest + Always + + + + + + + + + + + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.DbMigrator/BookStoreDbMigratorModule.cs b/samples/BookStore/src/Acme.BookStore.DbMigrator/BookStoreDbMigratorModule.cs new file mode 100644 index 0000000000..26b843d34c --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.DbMigrator/BookStoreDbMigratorModule.cs @@ -0,0 +1,16 @@ +using Acme.BookStore.EntityFrameworkCore; +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Acme.BookStore.DbMigrator +{ + [DependsOn( + typeof(AbpAutofacModule), + typeof(BookStoreEntityFrameworkCoreDbMigrationsModule), + typeof(BookStoreApplicationContractsModule) + )] + public class BookStoreDbMigratorModule : AbpModule + { + + } +} diff --git a/samples/BookStore/src/Acme.BookStore.DbMigrator/Program.cs b/samples/BookStore/src/Acme.BookStore.DbMigrator/Program.cs new file mode 100644 index 0000000000..d3c4034c8a --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.DbMigrator/Program.cs @@ -0,0 +1,53 @@ +using System.IO; +using Microsoft.Extensions.DependencyInjection; +using Acme.BookStore.Data; +using Serilog; +using Serilog.Events; +using Volo.Abp; +using Volo.Abp.Threading; + +namespace Acme.BookStore.DbMigrator +{ + class Program + { + static void Main(string[] args) + { + ConfigureLogging(); + + using (var application = AbpApplicationFactory.Create(options => + { + options.UseAutofac(); + options.Services.AddLogging(c => c.AddSerilog()); + })) + { + application.Initialize(); + + AsyncHelper.RunSync( + () => application + .ServiceProvider + .GetRequiredService() + .MigrateAsync() + ); + + application.Shutdown(); + } + } + + private static void ConfigureLogging() + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("Volo.Abp", LogEventLevel.Warning) +#if DEBUG + .MinimumLevel.Override("Acme.BookStore", LogEventLevel.Debug) +#else + .MinimumLevel.Override("Acme.BookStore", LogEventLevel.Information) +#endif + .Enrich.FromLogContext() + .WriteTo.File(Path.Combine(Directory.GetCurrentDirectory(), "Logs/logs.txt")) + .WriteTo.Console() + .CreateLogger(); + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.DbMigrator/appsettings.json b/samples/BookStore/src/Acme.BookStore.DbMigrator/appsettings.json new file mode 100644 index 0000000000..05d7313b10 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.DbMigrator/appsettings.json @@ -0,0 +1,16 @@ +{ + "ConnectionStrings": { + "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True;MultipleActiveResultSets=true" + }, + "IdentityServer": { + "Clients": { + "BookStore_Web": { + "ClientId": "BookStore_Web", + "RootUrl": "https://localhost:44314/" + }, + "BookStore_ConsoleTestApp": { + "ClientId": "BookStore_ConsoleTestApp" + } + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/Acme.BookStore.Domain.Shared.csproj b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Acme.BookStore.Domain.Shared.csproj new file mode 100644 index 0000000000..aba9ffbd3a --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Acme.BookStore.Domain.Shared.csproj @@ -0,0 +1,26 @@ + + + + + + netstandard2.0 + Acme.BookStore + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookStoreDomainErrorCodes.cs b/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookStoreDomainErrorCodes.cs new file mode 100644 index 0000000000..30f124fbed --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookStoreDomainErrorCodes.cs @@ -0,0 +1,7 @@ +namespace Acme.BookStore +{ + public static class BookStoreDomainErrorCodes + { + /* You can add your business exception error codes here, as constants */ + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookStoreDomainSharedModule.cs b/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookStoreDomainSharedModule.cs new file mode 100644 index 0000000000..f0a6ad7964 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookStoreDomainSharedModule.cs @@ -0,0 +1,45 @@ +using Acme.BookStore.Localization; +using Volo.Abp.AuditLogging; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.FeatureManagement; +using Volo.Abp.Identity; +using Volo.Abp.IdentityServer; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpValidation; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement; +using Volo.Abp.SettingManagement; +using Volo.Abp.TenantManagement; +using Volo.Abp.VirtualFileSystem; + +namespace Acme.BookStore +{ + [DependsOn( + typeof(AbpAuditLoggingDomainSharedModule), + typeof(BackgroundJobsDomainSharedModule), + typeof(AbpFeatureManagementDomainSharedModule), + typeof(AbpIdentityDomainSharedModule), + typeof(AbpIdentityServerDomainSharedModule), + typeof(AbpPermissionManagementDomainSharedModule), + typeof(AbpSettingManagementDomainSharedModule), + typeof(AbpTenantManagementDomainSharedModule) + )] + public class BookStoreDomainSharedModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded("Acme.BookStore"); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddBaseTypes(typeof(AbpValidationResource)) + .AddVirtualJson("/Localization/BookStore"); + }); + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookType.cs b/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookType.cs new file mode 100644 index 0000000000..ba84b4fe2a --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/BookType.cs @@ -0,0 +1,15 @@ +namespace Acme.BookStore +{ + public enum BookType + { + Undefined, + Adventure, + Biography, + Dystopia, + Fantastic, + Horror, + Science, + ScienceFiction, + Poetry + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/cs.json b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/cs.json new file mode 100644 index 0000000000..3e93b9d267 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/cs.json @@ -0,0 +1,20 @@ +{ + "culture": "cs", + "texts": { + "Menu:Home": "Úvod", + "Welcome": "Vítejte", + "LongWelcomeMessage": "Vítejte v aplikaci. Toto je startovací projekt založený na ABP frameworku. Pro více informací, navštivte abp.io.", + "Menu:BookStore": "Knihkupectví", + "Menu:Books": "Knihy", + "Name": "Název", + "Type": "Typ", + "PublishDate": "Publikováno", + "Price": "Cena", + "CreationTime": "Vytvořeno", + "NewBook": "Nová kniha", + "Books": "Knihy", + "Update": "Aktualizovat", + "BookDeletionConfirmationMessage": "Opravdu chcete smazat knihu {0}?", + "SuccessfullyDeleted": "Úspěšně smazáno." + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json new file mode 100644 index 0000000000..9db906dad7 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/en.json @@ -0,0 +1,20 @@ +{ + "culture": "en", + "texts": { + "Menu:Home": "Home", + "Welcome": "Welcome", + "LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.", + "Menu:BookStore": "Book Store", + "Menu:Books": "Books", + "Name": "Name", + "Type": "Type", + "PublishDate": "Publish Date", + "Price": "Price", + "CreationTime": "Creation Time", + "NewBook": "New book", + "Books": "Books", + "Update": "Update", + "BookDeletionConfirmationMessage": "Are you sure to delete the book {0}?", + "SuccessfullyDeleted": "Successfully deleted." + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/pt-BR.json b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/pt-BR.json similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/pt-BR.json rename to samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/pt-BR.json diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/tr.json similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/tr.json rename to samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/tr.json diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/zh-Hans.json b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/zh-Hans.json new file mode 100644 index 0000000000..86fe3582bb --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStore/zh-Hans.json @@ -0,0 +1,20 @@ +{ + "culture": "zh-Hans", + "texts": { + "Menu:Home": "首页", + "Welcome": "欢迎", + "LongWelcomeMessage": "欢迎来到该应用程序. 这是一个基于ABP框架的启动项目. 有关更多信息, 请访问 cn.abp.io.", + "Menu:BookStore": "图书商店", + "Menu:Books": "图书", + "Name": "名称", + "Type": "类型", + "PublishDate": "出版时间", + "Price": "价格", + "CreationTime": "添加时间", + "NewBook": "新书籍", + "Books": "图书", + "Update": "更新", + "BookDeletionConfirmationMessage": "你确定删除书箱: {0} 吗", + "SuccessfullyDeleted": "删除成功." + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStoreResource.cs b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStoreResource.cs new file mode 100644 index 0000000000..cc4b935e58 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/Localization/BookStoreResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace Acme.BookStore.Localization +{ + [LocalizationResourceName("BookStore")] + public class BookStoreResource + { + + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain.Shared/MultiTenancy/MultiTenancyConsts.cs b/samples/BookStore/src/Acme.BookStore.Domain.Shared/MultiTenancy/MultiTenancyConsts.cs new file mode 100644 index 0000000000..9ece5cc53e --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain.Shared/MultiTenancy/MultiTenancyConsts.cs @@ -0,0 +1,11 @@ +namespace Acme.BookStore.MultiTenancy +{ + public static class MultiTenancyConsts + { + /* Enable/disable multi-tenancy easily in a single point. + * If you will never need to multi-tenancy, you can remove + * related modules and code parts, including this file. + */ + public const bool IsEnabled = true; + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Acme.BookStore.Domain.csproj b/samples/BookStore/src/Acme.BookStore.Domain/Acme.BookStore.Domain.csproj index c358910b01..9efa77aa9a 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/Acme.BookStore.Domain.csproj +++ b/samples/BookStore/src/Acme.BookStore.Domain/Acme.BookStore.Domain.csproj @@ -1,25 +1,27 @@  - + netcoreapp2.2 Acme.BookStore - - - - - - - - + - + + + + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Book.cs b/samples/BookStore/src/Acme.BookStore.Domain/Book.cs index 12984a1896..93dd6d9adc 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/Book.cs +++ b/samples/BookStore/src/Acme.BookStore.Domain/Book.cs @@ -1,15 +1,10 @@ using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using Volo.Abp.Domain.Entities.Auditing; namespace Acme.BookStore { - [Table("Books")] public class Book : AuditedAggregateRoot { - [Required] - [StringLength(128)] public string Name { get; set; } public BookType Type { get; set; } diff --git a/samples/BookStore/src/Acme.BookStore.Domain/BookStoreConsts.cs b/samples/BookStore/src/Acme.BookStore.Domain/BookStoreConsts.cs index 5c8cab1e72..96556e1b85 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/BookStoreConsts.cs +++ b/samples/BookStore/src/Acme.BookStore.Domain/BookStoreConsts.cs @@ -5,7 +5,5 @@ public const string DbTablePrefix = "App"; public const string DbSchema = null; - - public const bool IsMultiTenancyEnabled = true; } } diff --git a/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs b/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs index 7575b109e2..950bd62d1b 100644 --- a/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs +++ b/samples/BookStore/src/Acme.BookStore.Domain/BookStoreDomainModule.cs @@ -1,48 +1,37 @@ -using Acme.BookStore.Localization.BookStore; -using Volo.Abp.Auditing; +using Acme.BookStore.MultiTenancy; using Volo.Abp.AuditLogging; using Volo.Abp.BackgroundJobs; using Volo.Abp.FeatureManagement; using Volo.Abp.Identity; -using Volo.Abp.Localization; -using Volo.Abp.Localization.Resources.AbpValidation; +using Volo.Abp.IdentityServer; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.PermissionManagement.Identity; +using Volo.Abp.PermissionManagement.IdentityServer; +using Volo.Abp.SettingManagement; using Volo.Abp.TenantManagement; -using Volo.Abp.VirtualFileSystem; namespace Acme.BookStore { [DependsOn( + typeof(BookStoreDomainSharedModule), + typeof(AbpAuditLoggingDomainModule), + typeof(BackgroundJobsDomainModule), + typeof(AbpFeatureManagementDomainModule), typeof(AbpIdentityDomainModule), typeof(AbpPermissionManagementDomainIdentityModule), - typeof(AbpAuditingModule), - typeof(BackgroundJobsDomainModule), - typeof(AbpAuditLoggingDomainModule), - typeof(AbpTenantManagementDomainModule), - typeof(AbpFeatureManagementDomainModule) + typeof(AbpIdentityServerDomainModule), + typeof(AbpPermissionManagementDomainIdentityServerModule), + typeof(AbpSettingManagementDomainModule), + typeof(AbpTenantManagementDomainModule) )] public class BookStoreDomainModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - Configure(options => - { - options.FileSets.AddEmbedded("Acme.BookStore"); - }); - - Configure(options => - { - options.Resources - .Add("en") - .AddBaseTypes(typeof(AbpValidationResource)) - .AddVirtualJson("/Localization/BookStore"); - }); - Configure(options => { - options.IsEnabled = BookStoreConsts.IsMultiTenancyEnabled; + options.IsEnabled = MultiTenancyConsts.IsEnabled; }); } } diff --git a/samples/BookStore/src/Acme.BookStore.Domain/BookType.cs b/samples/BookStore/src/Acme.BookStore.Domain/BookType.cs deleted file mode 100644 index 75d124d915..0000000000 --- a/samples/BookStore/src/Acme.BookStore.Domain/BookType.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Acme.BookStore -{ - public enum BookType : byte - { - Undefined, - Advanture, - Biography, - Dystopia, - Fantastic, - Horror, - Science, - ScienceFiction, - Poetry - } -} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Data/BookStoreDbMigrationService.cs b/samples/BookStore/src/Acme.BookStore.Domain/Data/BookStoreDbMigrationService.cs new file mode 100644 index 0000000000..4db6b4af83 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain/Data/BookStoreDbMigrationService.cs @@ -0,0 +1,39 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; + +namespace Acme.BookStore.Data +{ + public class BookStoreDbMigrationService : ITransientDependency + { + public ILogger Logger { get; set; } + + private readonly IDataSeeder _dataSeeder; + private readonly IBookStoreDbSchemaMigrator _dbSchemaMigrator; + + public BookStoreDbMigrationService( + IDataSeeder dataSeeder, + IBookStoreDbSchemaMigrator dbSchemaMigrator) + { + _dataSeeder = dataSeeder; + _dbSchemaMigrator = dbSchemaMigrator; + + Logger = NullLogger.Instance; + } + + public async Task MigrateAsync() + { + Logger.LogInformation("Started database migrations..."); + + Logger.LogInformation("Migrating database schema..."); + await _dbSchemaMigrator.MigrateAsync(); + + Logger.LogInformation("Executing database seed..."); + await _dataSeeder.SeedAsync(); + + Logger.LogInformation("Successfully completed database migrations."); + } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Data/IBookStoreDbSchemaMigrator.cs b/samples/BookStore/src/Acme.BookStore.Domain/Data/IBookStoreDbSchemaMigrator.cs new file mode 100644 index 0000000000..5c94a6918e --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain/Data/IBookStoreDbSchemaMigrator.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Acme.BookStore.Data +{ + public interface IBookStoreDbSchemaMigrator + { + Task MigrateAsync(); + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Data/NullBookStoreDbSchemaMigrator.cs b/samples/BookStore/src/Acme.BookStore.Domain/Data/NullBookStoreDbSchemaMigrator.cs new file mode 100644 index 0000000000..f6ed8fc8de --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain/Data/NullBookStoreDbSchemaMigrator.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Acme.BookStore.Data +{ + /* This is used if database provider does't define + * IBookStoreDbSchemaMigrator implementation. + */ + public class NullBookStoreDbSchemaMigrator : IBookStoreDbSchemaMigrator, ITransientDependency + { + public Task MigrateAsync() + { + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain/IdentityServer/IdentityServerDataSeedContributor.cs b/samples/BookStore/src/Acme.BookStore.Domain/IdentityServer/IdentityServerDataSeedContributor.cs new file mode 100644 index 0000000000..45c2420501 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Domain/IdentityServer/IdentityServerDataSeedContributor.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Configuration; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.IdentityServer.ApiResources; +using Volo.Abp.IdentityServer.Clients; +using Volo.Abp.IdentityServer.IdentityResources; +using Volo.Abp.PermissionManagement; +using Volo.Abp.Uow; + +namespace Acme.BookStore.IdentityServer +{ + public class IdentityServerDataSeedContributor : IDataSeedContributor, ITransientDependency + { + private readonly IApiResourceRepository _apiResourceRepository; + private readonly IClientRepository _clientRepository; + private readonly IIdentityResourceDataSeeder _identityResourceDataSeeder; + private readonly IGuidGenerator _guidGenerator; + private readonly IPermissionDataSeeder _permissionDataSeeder; + private readonly IConfigurationAccessor _configurationAccessor; + + public IdentityServerDataSeedContributor( + IClientRepository clientRepository, + IApiResourceRepository apiResourceRepository, + IIdentityResourceDataSeeder identityResourceDataSeeder, + IGuidGenerator guidGenerator, + IPermissionDataSeeder permissionDataSeeder, + IConfigurationAccessor configurationAccessor) + { + _clientRepository = clientRepository; + _apiResourceRepository = apiResourceRepository; + _identityResourceDataSeeder = identityResourceDataSeeder; + _guidGenerator = guidGenerator; + _permissionDataSeeder = permissionDataSeeder; + _configurationAccessor = configurationAccessor; + } + + [UnitOfWork] + public virtual async Task SeedAsync(DataSeedContext context) + { + await _identityResourceDataSeeder.CreateStandardResourcesAsync(); + await CreateApiResourcesAsync(); + await CreateClientsAsync(); + } + + private async Task CreateApiResourcesAsync() + { + var commonApiUserClaims = new[] + { + "email", + "email_verified", + "name", + "phone_number", + "phone_number_verified", + "role" + }; + + await CreateApiResourceAsync("BookStore", commonApiUserClaims); + } + + private async Task CreateApiResourceAsync(string name, IEnumerable claims) + { + var apiResource = await _apiResourceRepository.FindByNameAsync(name); + if (apiResource == null) + { + apiResource = await _apiResourceRepository.InsertAsync( + new ApiResource( + _guidGenerator.Create(), + name, + name + " API" + ), + autoSave: true + ); + } + + foreach (var claim in claims) + { + if (apiResource.FindClaim(claim) == null) + { + apiResource.AddUserClaim(claim); + } + } + + return await _apiResourceRepository.UpdateAsync(apiResource); + } + + private async Task CreateClientsAsync() + { + const string commonSecret = "E5Xd4yMqjP5kjWFKrYgySBju6JVfCzMyFp7n2QmMrME="; + + var commonScopes = new[] + { + "email", + "openid", + "profile", + "role", + "phone", + "address", + "BookStore" + }; + + var configurationSection = _configurationAccessor.Configuration.GetSection("IdentityServer:Clients"); + + //Web Client + var webClientId = configurationSection["BookStore_Web:ClientId"]; + if (!webClientId.IsNullOrWhiteSpace()) + { + var webClientRootUrl = configurationSection["BookStore_Web:RootUrl"].EnsureEndsWith('/'); + + /* BookStore_Web client is only needed if you created a tiered + * solution. Otherwise, you can delete this client. */ + + await CreateClientAsync( + webClientId, + commonScopes, + new[] { "hybrid" }, + commonSecret, + redirectUri: $"{webClientRootUrl}signin-oidc", + postLogoutRedirectUri: $"{webClientRootUrl}signout-callback-oidc" + ); + } + + //Console Test Client + var consoleClientId = configurationSection["BookStore_ConsoleTestApp:ClientId"]; + if (!consoleClientId.IsNullOrWhiteSpace()) + { + await CreateClientAsync( + consoleClientId, + commonScopes, + new[] { "password", "client_credentials" }, + commonSecret + ); + } + } + + private async Task CreateClientAsync( + string name, + IEnumerable scopes, + IEnumerable grantTypes, + string secret, + string redirectUri = null, + string postLogoutRedirectUri = null, + IEnumerable permissions = null) + { + var client = await _clientRepository.FindByCliendIdAsync(name); + if (client == null) + { + client = await _clientRepository.InsertAsync( + new Client( + _guidGenerator.Create(), + name + ) + { + ClientName = name, + ProtocolType = "oidc", + Description = name, + AlwaysIncludeUserClaimsInIdToken = true, + AllowOfflineAccess = true, + AbsoluteRefreshTokenLifetime = 31536000, //365 days + AccessTokenLifetime = 31536000, //365 days + AuthorizationCodeLifetime = 300, + IdentityTokenLifetime = 300, + RequireConsent = false + }, + autoSave: true + ); + } + + foreach (var scope in scopes) + { + if (client.FindScope(scope) == null) + { + client.AddScope(scope); + } + } + + foreach (var grantType in grantTypes) + { + if (client.FindGrantType(grantType) == null) + { + client.AddGrantType(grantType); + } + } + + if (client.FindSecret(secret) == null) + { + client.AddSecret(secret); + } + + if (redirectUri != null) + { + if (client.FindRedirectUri(redirectUri) == null) + { + client.AddRedirectUri(redirectUri); + } + } + + if (postLogoutRedirectUri != null) + { + if (client.FindPostLogoutRedirectUri(postLogoutRedirectUri) == null) + { + client.AddPostLogoutRedirectUri(postLogoutRedirectUri); + } + } + + if (permissions != null) + { + await _permissionDataSeeder.SeedAsync( + ClientPermissionValueProvider.ProviderName, + name, + permissions + ); + } + + return await _clientRepository.UpdateAsync(client); + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/BookStoreResource.cs b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/BookStoreResource.cs deleted file mode 100644 index 068968babf..0000000000 --- a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/BookStoreResource.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Volo.Abp.Localization; - -namespace Acme.BookStore.Localization.BookStore -{ - [LocalizationResourceName("BookStore")] - public class BookStoreResource - { - - } -} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json deleted file mode 100644 index 15dcae40ee..0000000000 --- a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/en.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "culture": "en", - "texts": { - "Menu:Home": "Home", - "Welcome": "Welcome", - "LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.", - "Menu:BookStore": "Book Store", - "Menu:Books": "Books", - "Name": "Name", - "Type": "Type", - "PublishDate": "Publish Date", - "Price": "Price", - "CreationTime": "Creation Time", - "NewBook": "New book", - "Books": "Books", - "Update": "Update", - "BookDeletionConfirmationMessage": "Are you sure to delete this book: {0}", - "SuccessfullyDeleted": "Successfully deleted." - } -} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/zh-Hans.json b/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/zh-Hans.json deleted file mode 100644 index be57798695..0000000000 --- a/samples/BookStore/src/Acme.BookStore.Domain/Localization/BookStore/zh-Hans.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "culture": "zh-Hans", - "texts": { - "Menu:Home": "首页", - "Welcome": "欢迎", - "LongWelcomeMessage": "欢迎来到该应用程序. 这是一个基于ABP框架的启动项目. 有关更多信息, 请访问 cn.abp.io.", - "Menu:BookStore": "图书商店", - "Menu:Books": "图书", - "Name": "名称", - "Type": "类型", - "PublishDate": "出版时间", - "Price": "价格", - "CreationTime": "添加时间", - "NewBook": "新书籍", - "Books": "图书", - "Update": "更新", - "BookDeletionConfirmationMessage": "你确定删除书箱: {0} 吗", - "SuccessfullyDeleted": "删除成功." - } - } \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Acme.BookStore.EntityFrameworkCore.DbMigrations.csproj b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Acme.BookStore.EntityFrameworkCore.DbMigrations.csproj index da6638173c..3b0318a10c 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Acme.BookStore.EntityFrameworkCore.DbMigrations.csproj +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Acme.BookStore.EntityFrameworkCore.DbMigrations.csproj @@ -7,10 +7,6 @@ Acme.BookStore - - - - diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreEntityFrameworkCoreDbMigrationsModule.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreEntityFrameworkCoreDbMigrationsModule.cs new file mode 100644 index 0000000000..88e21f2ce8 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreEntityFrameworkCoreDbMigrationsModule.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Acme.BookStore.EntityFrameworkCore +{ + [DependsOn( + typeof(BookStoreEntityFrameworkCoreModule) + )] + public class BookStoreEntityFrameworkCoreDbMigrationsModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(); + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContext.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContext.cs index a43cc52fb6..541a91a0fd 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContext.cs +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContext.cs @@ -2,14 +2,21 @@ using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.BackgroundJobs.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; using Volo.Abp.Identity; using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.IdentityServer.EntityFrameworkCore; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; using Volo.Abp.TenantManagement.EntityFrameworkCore; namespace Acme.BookStore.EntityFrameworkCore { + /* This DbContext is only used for database migrations. + * It is not used on runtime. See BookStoreDbContext for the runtime DbContext. + * It is a unified model that includes configuration for + * all used modules and your application. + */ public class BookStoreMigrationsDbContext : AbpDbContext { public BookStoreMigrationsDbContext(DbContextOptions options) @@ -29,6 +36,8 @@ namespace Acme.BookStore.EntityFrameworkCore builder.ConfigureBackgroundJobs(); builder.ConfigureAuditLogging(); builder.ConfigureIdentity(); + builder.ConfigureIdentityServer(); + builder.ConfigureFeatureManagement(); builder.ConfigureTenantManagement(); /* Configure customizations for entities from the modules included */ diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContextFactory.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContextFactory.cs index bc1006d729..da72e6fb21 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContextFactory.cs +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/BookStoreMigrationsDbContextFactory.cs @@ -5,6 +5,8 @@ using Microsoft.Extensions.Configuration; namespace Acme.BookStore.EntityFrameworkCore { + /* This class is needed for EF Core console commands + * (like Add-Migration and Update-Database commands) */ public class BookStoreMigrationsDbContextFactory : IDesignTimeDbContextFactory { public BookStoreMigrationsDbContext CreateDbContext(string[] args) diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/EntityFrameworkCoreBookStoreDbSchemaMigrator.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/EntityFrameworkCoreBookStoreDbSchemaMigrator.cs new file mode 100644 index 0000000000..954c8f7a13 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/EntityFrameworkCoreBookStoreDbSchemaMigrator.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Acme.BookStore.Data; +using Volo.Abp.DependencyInjection; + +namespace Acme.BookStore.EntityFrameworkCore +{ + [Dependency(ReplaceServices = true)] + public class EntityFrameworkCoreBookStoreDbSchemaMigrator + : IBookStoreDbSchemaMigrator, ITransientDependency + { + private readonly BookStoreMigrationsDbContext _dbContext; + + public EntityFrameworkCoreBookStoreDbSchemaMigrator(BookStoreMigrationsDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task MigrateAsync() + { + await _dbContext.Database.MigrateAsync(); + } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs deleted file mode 100644 index 37937a0bb6..0000000000 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs +++ /dev/null @@ -1,760 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Acme.BookStore.EntityFrameworkCore; - -namespace Acme.BookStore.Migrations -{ - [DbContext(typeof(BookStoreMigrationsDbContext))] - [Migration("20190402131334_Initial")] - partial class Initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ApplicationName") - .HasColumnName("ApplicationName") - .HasMaxLength(96); - - b.Property("BrowserInfo") - .HasColumnName("BrowserInfo") - .HasMaxLength(512); - - b.Property("ClientId") - .HasColumnName("ClientId") - .HasMaxLength(64); - - b.Property("ClientIpAddress") - .HasColumnName("ClientIpAddress") - .HasMaxLength(64); - - b.Property("ClientName") - .HasColumnName("ClientName") - .HasMaxLength(128); - - b.Property("Comments") - .HasColumnName("Comments") - .HasMaxLength(256); - - b.Property("ConcurrencyStamp"); - - b.Property("CorrelationId") - .HasColumnName("CorrelationId") - .HasMaxLength(64); - - b.Property("Exceptions") - .HasColumnName("Exceptions") - .HasMaxLength(4000); - - b.Property("ExecutionDuration") - .HasColumnName("ExecutionDuration"); - - b.Property("ExecutionTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("HttpMethod") - .HasColumnName("HttpMethod") - .HasMaxLength(16); - - b.Property("HttpStatusCode") - .HasColumnName("HttpStatusCode"); - - b.Property("ImpersonatorTenantId") - .HasColumnName("ImpersonatorTenantId"); - - b.Property("ImpersonatorUserId") - .HasColumnName("ImpersonatorUserId"); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.Property("TenantName"); - - b.Property("Url") - .HasColumnName("Url") - .HasMaxLength(256); - - b.Property("UserId") - .HasColumnName("UserId"); - - b.Property("UserName") - .HasColumnName("UserName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("TenantId", "ExecutionTime"); - - b.HasIndex("TenantId", "UserId", "ExecutionTime"); - - b.ToTable("AbpAuditLogs"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AuditLogId") - .HasColumnName("AuditLogId"); - - b.Property("ExecutionDuration") - .HasColumnName("ExecutionDuration"); - - b.Property("ExecutionTime") - .HasColumnName("ExecutionTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("MethodName") - .HasColumnName("MethodName") - .HasMaxLength(128); - - b.Property("Parameters") - .HasColumnName("Parameters") - .HasMaxLength(2000); - - b.Property("ServiceName") - .HasColumnName("ServiceName") - .HasMaxLength(256); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("AuditLogId"); - - b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); - - b.ToTable("AbpAuditLogActions"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AuditLogId") - .HasColumnName("AuditLogId"); - - b.Property("ChangeTime") - .HasColumnName("ChangeTime"); - - b.Property("ChangeType") - .HasColumnName("ChangeType"); - - b.Property("EntityId") - .IsRequired() - .HasColumnName("EntityId") - .HasMaxLength(128); - - b.Property("EntityTenantId"); - - b.Property("EntityTypeFullName") - .IsRequired() - .HasColumnName("EntityTypeFullName") - .HasMaxLength(128); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("AuditLogId"); - - b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); - - b.ToTable("AbpEntityChanges"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("EntityChangeId"); - - b.Property("NewValue") - .HasColumnName("NewValue") - .HasMaxLength(512); - - b.Property("OriginalValue") - .HasColumnName("OriginalValue") - .HasMaxLength(512); - - b.Property("PropertyName") - .IsRequired() - .HasColumnName("PropertyName") - .HasMaxLength(128); - - b.Property("PropertyTypeFullName") - .IsRequired() - .HasColumnName("PropertyTypeFullName") - .HasMaxLength(64); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("EntityChangeId"); - - b.ToTable("AbpEntityPropertyChanges"); - }); - - modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsAbandoned") - .ValueGeneratedOnAdd() - .HasDefaultValue(false); - - b.Property("JobArgs") - .IsRequired() - .HasMaxLength(1048576); - - b.Property("JobName") - .IsRequired() - .HasMaxLength(128); - - b.Property("LastTryTime"); - - b.Property("NextTryTime"); - - b.Property("Priority") - .ValueGeneratedOnAdd() - .HasDefaultValue((byte)15); - - b.Property("TryCount") - .ValueGeneratedOnAdd() - .HasDefaultValue((short)0); - - b.HasKey("Id"); - - b.HasIndex("IsAbandoned", "NextTryTime"); - - b.ToTable("AbpBackgroundJobs"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("Description") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("Regex") - .HasMaxLength(512); - - b.Property("RegexDescription") - .HasMaxLength(128); - - b.Property("Required"); - - b.Property("ValueType"); - - b.HasKey("Id"); - - b.ToTable("AbpClaimTypes"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDefault") - .HasColumnName("IsDefault"); - - b.Property("IsPublic") - .HasColumnName("IsPublic"); - - b.Property("IsStatic") - .HasColumnName("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("NormalizedName") - .IsRequired() - .HasMaxLength(256); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName"); - - b.ToTable("AbpRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AbpRoleClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount") - .ValueGeneratedOnAdd() - .HasColumnName("AccessFailedCount") - .HasDefaultValue(0); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnName("DeletionTime"); - - b.Property("Email") - .HasColumnName("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("EmailConfirmed") - .HasDefaultValue(false); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnName("IsDeleted") - .HasDefaultValue(false); - - b.Property("LastModificationTime") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnName("LastModifierId"); - - b.Property("LockoutEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("LockoutEnabled") - .HasDefaultValue(false); - - b.Property("LockoutEnd"); - - b.Property("Name") - .HasColumnName("Name") - .HasMaxLength(64); - - b.Property("NormalizedEmail") - .HasColumnName("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .IsRequired() - .HasColumnName("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash") - .HasColumnName("PasswordHash") - .HasMaxLength(256); - - b.Property("PhoneNumber") - .HasColumnName("PhoneNumber") - .HasMaxLength(16); - - b.Property("PhoneNumberConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("PhoneNumberConfirmed") - .HasDefaultValue(false); - - b.Property("SecurityStamp") - .IsRequired() - .HasColumnName("SecurityStamp") - .HasMaxLength(256); - - b.Property("Surname") - .HasColumnName("Surname") - .HasMaxLength(64); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.Property("TwoFactorEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("TwoFactorEnabled") - .HasDefaultValue(false); - - b.Property("UserName") - .IsRequired() - .HasColumnName("UserName") - .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") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("TenantId"); - - b.Property("UserId"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AbpUserClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("ProviderDisplayName") - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(196); - - b.Property("TenantId"); - - b.HasKey("UserId", "LoginProvider"); - - b.HasIndex("LoginProvider", "ProviderKey"); - - b.ToTable("AbpUserLogins"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId", "UserId"); - - b.ToTable("AbpUserRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("Name") - .HasMaxLength(128); - - b.Property("TenantId"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AbpUserTokens"); - }); - - modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(64); - - b.Property("ProviderName") - .IsRequired() - .HasMaxLength(64); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpPermissionGrants"); - }); - - modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .HasMaxLength(64); - - b.Property("ProviderName") - .HasMaxLength(64); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2048); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpSettings"); - }); - - modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnName("DeletionTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnName("IsDeleted") - .HasDefaultValue(false); - - b.Property("LastModificationTime") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnName("LastModifierId"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(64); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("AbpTenants"); - }); - - modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => - { - b.Property("TenantId"); - - b.Property("Name") - .HasMaxLength(64); - - b.Property("Value") - .IsRequired() - .HasMaxLength(1024); - - b.HasKey("TenantId", "Name"); - - b.ToTable("AbpTenantConnectionStrings"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => - { - b.HasOne("Volo.Abp.AuditLogging.AuditLog") - .WithMany("Actions") - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => - { - b.HasOne("Volo.Abp.AuditLogging.AuditLog") - .WithMany("EntityChanges") - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => - { - b.HasOne("Volo.Abp.AuditLogging.EntityChange") - .WithMany("PropertyChanges") - .HasForeignKey("EntityChangeId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany("Claims") - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Claims") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Logins") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Roles") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Tokens") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => - { - b.HasOne("Volo.Abp.TenantManagement.Tenant") - .WithMany("ConnectionStrings") - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs deleted file mode 100644 index fb85c4cc6d..0000000000 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs +++ /dev/null @@ -1,546 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Acme.BookStore.Migrations -{ - public partial class Initial : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AbpAuditLogs", - columns: table => new - { - Id = table.Column(nullable: false), - ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - ApplicationName = table.Column(maxLength: 96, nullable: true), - UserId = table.Column(nullable: true), - UserName = table.Column(maxLength: 256, nullable: true), - TenantId = table.Column(nullable: true), - TenantName = table.Column(nullable: true), - ImpersonatorUserId = table.Column(nullable: true), - ImpersonatorTenantId = table.Column(nullable: true), - ExecutionTime = table.Column(nullable: false), - ExecutionDuration = table.Column(nullable: false), - ClientIpAddress = table.Column(maxLength: 64, nullable: true), - ClientName = table.Column(maxLength: 128, nullable: true), - ClientId = table.Column(maxLength: 64, nullable: true), - CorrelationId = table.Column(maxLength: 64, nullable: true), - BrowserInfo = table.Column(maxLength: 512, nullable: true), - HttpMethod = table.Column(maxLength: 16, nullable: true), - Url = table.Column(maxLength: 256, nullable: true), - Exceptions = table.Column(maxLength: 4000, nullable: true), - Comments = table.Column(maxLength: 256, nullable: true), - HttpStatusCode = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpBackgroundJobs", - columns: table => new - { - Id = table.Column(nullable: false), - ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(nullable: true), - JobName = table.Column(maxLength: 128, nullable: false), - JobArgs = table.Column(maxLength: 1048576, nullable: false), - TryCount = table.Column(nullable: false, defaultValue: (short)0), - CreationTime = table.Column(nullable: false), - NextTryTime = table.Column(nullable: false), - LastTryTime = table.Column(nullable: true), - IsAbandoned = table.Column(nullable: false, defaultValue: false), - Priority = table.Column(nullable: false, defaultValue: (byte)15) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpBackgroundJobs", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpClaimTypes", - columns: table => new - { - Id = table.Column(nullable: false), - ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), - Name = table.Column(maxLength: 256, nullable: false), - Required = table.Column(nullable: false), - IsStatic = table.Column(nullable: false), - Regex = table.Column(maxLength: 512, nullable: true), - RegexDescription = table.Column(maxLength: 128, nullable: true), - Description = table.Column(maxLength: 256, nullable: true), - ValueType = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpPermissionGrants", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - Name = table.Column(maxLength: 128, nullable: false), - ProviderName = table.Column(maxLength: 64, nullable: false), - ProviderKey = table.Column(maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpRoles", - columns: table => new - { - Id = table.Column(nullable: false), - ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), - TenantId = table.Column(nullable: true), - Name = table.Column(maxLength: 256, nullable: false), - NormalizedName = table.Column(maxLength: 256, nullable: false), - IsDefault = table.Column(nullable: false), - IsStatic = table.Column(nullable: false), - IsPublic = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpSettings", - columns: table => new - { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(maxLength: 2048, nullable: false), - ProviderName = table.Column(maxLength: 64, nullable: true), - ProviderKey = table.Column(maxLength: 64, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpSettings", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpTenants", - 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), - Name = table.Column(maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpTenants", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpUsers", - 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), - UserName = table.Column(maxLength: 256, nullable: false), - NormalizedUserName = table.Column(maxLength: 256, nullable: false), - Name = table.Column(maxLength: 64, nullable: true), - Surname = table.Column(maxLength: 64, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false, defaultValue: false), - PasswordHash = table.Column(maxLength: 256, nullable: true), - SecurityStamp = table.Column(maxLength: 256, nullable: false), - PhoneNumber = table.Column(maxLength: 16, nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false, defaultValue: false), - TwoFactorEnabled = table.Column(nullable: false, defaultValue: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false, defaultValue: false), - AccessFailedCount = table.Column(nullable: false, defaultValue: 0) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpAuditLogActions", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - AuditLogId = table.Column(nullable: false), - ServiceName = table.Column(maxLength: 256, nullable: true), - MethodName = table.Column(maxLength: 128, nullable: true), - Parameters = table.Column(maxLength: 2000, nullable: true), - ExecutionTime = table.Column(nullable: false), - ExecutionDuration = table.Column(nullable: false), - ExtraProperties = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpAuditLogActions", x => x.Id); - table.ForeignKey( - name: "FK_AbpAuditLogActions_AbpAuditLogs_AuditLogId", - column: x => x.AuditLogId, - principalTable: "AbpAuditLogs", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpEntityChanges", - columns: table => new - { - Id = table.Column(nullable: false), - AuditLogId = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - ChangeTime = table.Column(nullable: false), - ChangeType = table.Column(nullable: false), - EntityTenantId = table.Column(nullable: true), - EntityId = table.Column(maxLength: 128, nullable: false), - EntityTypeFullName = table.Column(maxLength: 128, nullable: false), - ExtraProperties = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpEntityChanges", x => x.Id); - table.ForeignKey( - name: "FK_AbpEntityChanges_AbpAuditLogs_AuditLogId", - column: x => x.AuditLogId, - principalTable: "AbpAuditLogs", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpRoleClaims", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - ClaimType = table.Column(maxLength: 256, nullable: false), - ClaimValue = table.Column(maxLength: 1024, nullable: true), - RoleId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AbpRoleClaims_AbpRoles_RoleId", - column: x => x.RoleId, - principalTable: "AbpRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpTenantConnectionStrings", - columns: table => new - { - TenantId = table.Column(nullable: false), - Name = table.Column(maxLength: 64, nullable: false), - Value = table.Column(maxLength: 1024, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name }); - table.ForeignKey( - name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId", - column: x => x.TenantId, - principalTable: "AbpTenants", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserClaims", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - ClaimType = table.Column(maxLength: 256, nullable: false), - ClaimValue = table.Column(maxLength: 1024, nullable: true), - UserId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AbpUserClaims_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserLogins", - columns: table => new - { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 64, nullable: false), - TenantId = table.Column(nullable: true), - ProviderKey = table.Column(maxLength: 196, nullable: false), - ProviderDisplayName = table.Column(maxLength: 128, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserLogins", x => new { x.UserId, x.LoginProvider }); - table.ForeignKey( - name: "FK_AbpUserLogins_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserRoles", - columns: table => new - { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false), - TenantId = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AbpUserRoles_AbpRoles_RoleId", - column: x => x.RoleId, - principalTable: "AbpRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AbpUserRoles_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserTokens", - columns: table => new - { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 64, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - TenantId = table.Column(nullable: true), - Value = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AbpUserTokens_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpEntityPropertyChanges", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - EntityChangeId = table.Column(nullable: false), - NewValue = table.Column(maxLength: 512, nullable: true), - OriginalValue = table.Column(maxLength: 512, nullable: true), - PropertyName = table.Column(maxLength: 128, nullable: false), - PropertyTypeFullName = table.Column(maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpEntityPropertyChanges", x => x.Id); - table.ForeignKey( - name: "FK_AbpEntityPropertyChanges_AbpEntityChanges_EntityChangeId", - column: x => x.EntityChangeId, - principalTable: "AbpEntityChanges", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogActions_AuditLogId", - table: "AbpAuditLogActions", - column: "AuditLogId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogActions_TenantId_ServiceName_MethodName_ExecutionTime", - table: "AbpAuditLogActions", - columns: new[] { "TenantId", "ServiceName", "MethodName", "ExecutionTime" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogs_TenantId_ExecutionTime", - table: "AbpAuditLogs", - columns: new[] { "TenantId", "ExecutionTime" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpAuditLogs_TenantId_UserId_ExecutionTime", - table: "AbpAuditLogs", - columns: new[] { "TenantId", "UserId", "ExecutionTime" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpBackgroundJobs_IsAbandoned_NextTryTime", - table: "AbpBackgroundJobs", - columns: new[] { "IsAbandoned", "NextTryTime" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpEntityChanges_AuditLogId", - table: "AbpEntityChanges", - column: "AuditLogId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpEntityChanges_TenantId_EntityTypeFullName_EntityId", - table: "AbpEntityChanges", - columns: new[] { "TenantId", "EntityTypeFullName", "EntityId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpEntityPropertyChanges_EntityChangeId", - table: "AbpEntityPropertyChanges", - column: "EntityChangeId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", - table: "AbpPermissionGrants", - columns: new[] { "Name", "ProviderName", "ProviderKey" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpRoleClaims_RoleId", - table: "AbpRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpRoles_NormalizedName", - table: "AbpRoles", - column: "NormalizedName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpSettings_Name_ProviderName_ProviderKey", - table: "AbpSettings", - columns: new[] { "Name", "ProviderName", "ProviderKey" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpTenants_Name", - table: "AbpTenants", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserClaims_UserId", - table: "AbpUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserLogins_LoginProvider_ProviderKey", - table: "AbpUserLogins", - columns: new[] { "LoginProvider", "ProviderKey" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserRoles_RoleId_UserId", - table: "AbpUserRoles", - columns: new[] { "RoleId", "UserId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_Email", - table: "AbpUsers", - column: "Email"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_NormalizedEmail", - table: "AbpUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_NormalizedUserName", - table: "AbpUsers", - column: "NormalizedUserName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_UserName", - table: "AbpUsers", - column: "UserName"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AbpAuditLogActions"); - - migrationBuilder.DropTable( - name: "AbpBackgroundJobs"); - - migrationBuilder.DropTable( - name: "AbpClaimTypes"); - - migrationBuilder.DropTable( - name: "AbpEntityPropertyChanges"); - - migrationBuilder.DropTable( - name: "AbpPermissionGrants"); - - migrationBuilder.DropTable( - name: "AbpRoleClaims"); - - migrationBuilder.DropTable( - name: "AbpSettings"); - - migrationBuilder.DropTable( - name: "AbpTenantConnectionStrings"); - - migrationBuilder.DropTable( - name: "AbpUserClaims"); - - migrationBuilder.DropTable( - name: "AbpUserLogins"); - - migrationBuilder.DropTable( - name: "AbpUserRoles"); - - migrationBuilder.DropTable( - name: "AbpUserTokens"); - - migrationBuilder.DropTable( - name: "AbpEntityChanges"); - - migrationBuilder.DropTable( - name: "AbpTenants"); - - migrationBuilder.DropTable( - name: "AbpRoles"); - - migrationBuilder.DropTable( - name: "AbpUsers"); - - migrationBuilder.DropTable( - name: "AbpAuditLogs"); - } - } -} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190416070050_Created_Book_Entity.Designer.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190416070050_Created_Book_Entity.Designer.cs deleted file mode 100644 index 2ec683d7ed..0000000000 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190416070050_Created_Book_Entity.Designer.cs +++ /dev/null @@ -1,793 +0,0 @@ -// -using System; -using Acme.BookStore.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Acme.BookStore.Migrations -{ - [DbContext(typeof(BookStoreMigrationsDbContext))] - [Migration("20190416070050_Created_Book_Entity")] - partial class Created_Book_Entity - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.3-servicing-35854") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Acme.BookStore.Book", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp"); - - b.Property("CreationTime"); - - b.Property("CreatorId"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("LastModificationTime"); - - b.Property("LastModifierId"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("Price"); - - b.Property("PublishDate"); - - b.Property("Type"); - - b.HasKey("Id"); - - b.ToTable("AppBooks"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ApplicationName") - .HasColumnName("ApplicationName") - .HasMaxLength(96); - - b.Property("BrowserInfo") - .HasColumnName("BrowserInfo") - .HasMaxLength(512); - - b.Property("ClientId") - .HasColumnName("ClientId") - .HasMaxLength(64); - - b.Property("ClientIpAddress") - .HasColumnName("ClientIpAddress") - .HasMaxLength(64); - - b.Property("ClientName") - .HasColumnName("ClientName") - .HasMaxLength(128); - - b.Property("Comments") - .HasColumnName("Comments") - .HasMaxLength(256); - - b.Property("ConcurrencyStamp"); - - b.Property("CorrelationId") - .HasColumnName("CorrelationId") - .HasMaxLength(64); - - b.Property("Exceptions") - .HasColumnName("Exceptions") - .HasMaxLength(4000); - - b.Property("ExecutionDuration") - .HasColumnName("ExecutionDuration"); - - b.Property("ExecutionTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("HttpMethod") - .HasColumnName("HttpMethod") - .HasMaxLength(16); - - b.Property("HttpStatusCode") - .HasColumnName("HttpStatusCode"); - - b.Property("ImpersonatorTenantId") - .HasColumnName("ImpersonatorTenantId"); - - b.Property("ImpersonatorUserId") - .HasColumnName("ImpersonatorUserId"); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.Property("TenantName"); - - b.Property("Url") - .HasColumnName("Url") - .HasMaxLength(256); - - b.Property("UserId") - .HasColumnName("UserId"); - - b.Property("UserName") - .HasColumnName("UserName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("TenantId", "ExecutionTime"); - - b.HasIndex("TenantId", "UserId", "ExecutionTime"); - - b.ToTable("AbpAuditLogs"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AuditLogId") - .HasColumnName("AuditLogId"); - - b.Property("ExecutionDuration") - .HasColumnName("ExecutionDuration"); - - b.Property("ExecutionTime") - .HasColumnName("ExecutionTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("MethodName") - .HasColumnName("MethodName") - .HasMaxLength(128); - - b.Property("Parameters") - .HasColumnName("Parameters") - .HasMaxLength(2000); - - b.Property("ServiceName") - .HasColumnName("ServiceName") - .HasMaxLength(256); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("AuditLogId"); - - b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); - - b.ToTable("AbpAuditLogActions"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AuditLogId") - .HasColumnName("AuditLogId"); - - b.Property("ChangeTime") - .HasColumnName("ChangeTime"); - - b.Property("ChangeType") - .HasColumnName("ChangeType"); - - b.Property("EntityId") - .IsRequired() - .HasColumnName("EntityId") - .HasMaxLength(128); - - b.Property("EntityTenantId"); - - b.Property("EntityTypeFullName") - .IsRequired() - .HasColumnName("EntityTypeFullName") - .HasMaxLength(128); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("AuditLogId"); - - b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); - - b.ToTable("AbpEntityChanges"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("EntityChangeId"); - - b.Property("NewValue") - .HasColumnName("NewValue") - .HasMaxLength(512); - - b.Property("OriginalValue") - .HasColumnName("OriginalValue") - .HasMaxLength(512); - - b.Property("PropertyName") - .IsRequired() - .HasColumnName("PropertyName") - .HasMaxLength(128); - - b.Property("PropertyTypeFullName") - .IsRequired() - .HasColumnName("PropertyTypeFullName") - .HasMaxLength(64); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("EntityChangeId"); - - b.ToTable("AbpEntityPropertyChanges"); - }); - - modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsAbandoned") - .ValueGeneratedOnAdd() - .HasDefaultValue(false); - - b.Property("JobArgs") - .IsRequired() - .HasMaxLength(1048576); - - b.Property("JobName") - .IsRequired() - .HasMaxLength(128); - - b.Property("LastTryTime"); - - b.Property("NextTryTime"); - - b.Property("Priority") - .ValueGeneratedOnAdd() - .HasDefaultValue((byte)15); - - b.Property("TryCount") - .ValueGeneratedOnAdd() - .HasDefaultValue((short)0); - - b.HasKey("Id"); - - b.HasIndex("IsAbandoned", "NextTryTime"); - - b.ToTable("AbpBackgroundJobs"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("Description") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("Regex") - .HasMaxLength(512); - - b.Property("RegexDescription") - .HasMaxLength(128); - - b.Property("Required"); - - b.Property("ValueType"); - - b.HasKey("Id"); - - b.ToTable("AbpClaimTypes"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDefault") - .HasColumnName("IsDefault"); - - b.Property("IsPublic") - .HasColumnName("IsPublic"); - - b.Property("IsStatic") - .HasColumnName("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("NormalizedName") - .IsRequired() - .HasMaxLength(256); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName"); - - b.ToTable("AbpRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AbpRoleClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount") - .ValueGeneratedOnAdd() - .HasColumnName("AccessFailedCount") - .HasDefaultValue(0); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnName("DeletionTime"); - - b.Property("Email") - .HasColumnName("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("EmailConfirmed") - .HasDefaultValue(false); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnName("IsDeleted") - .HasDefaultValue(false); - - b.Property("LastModificationTime") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnName("LastModifierId"); - - b.Property("LockoutEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("LockoutEnabled") - .HasDefaultValue(false); - - b.Property("LockoutEnd"); - - b.Property("Name") - .HasColumnName("Name") - .HasMaxLength(64); - - b.Property("NormalizedEmail") - .HasColumnName("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .IsRequired() - .HasColumnName("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash") - .HasColumnName("PasswordHash") - .HasMaxLength(256); - - b.Property("PhoneNumber") - .HasColumnName("PhoneNumber") - .HasMaxLength(16); - - b.Property("PhoneNumberConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("PhoneNumberConfirmed") - .HasDefaultValue(false); - - b.Property("SecurityStamp") - .IsRequired() - .HasColumnName("SecurityStamp") - .HasMaxLength(256); - - b.Property("Surname") - .HasColumnName("Surname") - .HasMaxLength(64); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.Property("TwoFactorEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("TwoFactorEnabled") - .HasDefaultValue(false); - - b.Property("UserName") - .IsRequired() - .HasColumnName("UserName") - .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") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("TenantId"); - - b.Property("UserId"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AbpUserClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("ProviderDisplayName") - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(196); - - b.Property("TenantId"); - - b.HasKey("UserId", "LoginProvider"); - - b.HasIndex("LoginProvider", "ProviderKey"); - - b.ToTable("AbpUserLogins"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId", "UserId"); - - b.ToTable("AbpUserRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("Name") - .HasMaxLength(128); - - b.Property("TenantId"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AbpUserTokens"); - }); - - modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(64); - - b.Property("ProviderName") - .IsRequired() - .HasMaxLength(64); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpPermissionGrants"); - }); - - modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .HasMaxLength(64); - - b.Property("ProviderName") - .HasMaxLength(64); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2048); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpSettings"); - }); - - modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnName("DeletionTime"); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnName("IsDeleted") - .HasDefaultValue(false); - - b.Property("LastModificationTime") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnName("LastModifierId"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(64); - - b.HasKey("Id"); - - b.HasIndex("Name") - .IsUnique(); - - b.ToTable("AbpTenants"); - }); - - modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => - { - b.Property("TenantId"); - - b.Property("Name") - .HasMaxLength(64); - - b.Property("Value") - .IsRequired() - .HasMaxLength(1024); - - b.HasKey("TenantId", "Name"); - - b.ToTable("AbpTenantConnectionStrings"); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => - { - b.HasOne("Volo.Abp.AuditLogging.AuditLog") - .WithMany("Actions") - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => - { - b.HasOne("Volo.Abp.AuditLogging.AuditLog") - .WithMany("EntityChanges") - .HasForeignKey("AuditLogId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => - { - b.HasOne("Volo.Abp.AuditLogging.EntityChange") - .WithMany("PropertyChanges") - .HasForeignKey("EntityChangeId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany("Claims") - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Claims") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Logins") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Roles") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Tokens") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => - { - b.HasOne("Volo.Abp.TenantManagement.Tenant") - .WithMany("ConnectionStrings") - .HasForeignKey("TenantId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190416070050_Created_Book_Entity.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190416070050_Created_Book_Entity.cs deleted file mode 100644 index f4e67242e3..0000000000 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190416070050_Created_Book_Entity.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Acme.BookStore.Migrations -{ - public partial class Created_Book_Entity : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AppBooks", - 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), - Name = table.Column(maxLength: 128, nullable: false), - Type = table.Column(nullable: false), - PublishDate = table.Column(nullable: false), - Price = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AppBooks", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AppBooks"); - } - } -} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603112909_Initial.Designer.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603112909_Initial.Designer.cs new file mode 100644 index 0000000000..53187bf54d --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603112909_Initial.Designer.cs @@ -0,0 +1,1374 @@ +// +using System; +using Acme.BookStore.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Acme.BookStore.Migrations +{ + [DbContext(typeof(BookStoreMigrationsDbContext))] + [Migration("20190603112909_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TenantName"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTenantId"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128); + + b.Property("LastTryTime"); + + b.Property("NextTryTime"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs"); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpFeatureValues"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .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") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("DisplayName") + .HasMaxLength(128); + + b.Property("Emphasize"); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("ApiResourceId", "Name"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Name", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AbsoluteRefreshTokenLifetime"); + + b.Property("AccessTokenLifetime"); + + b.Property("AccessTokenType"); + + b.Property("AllowAccessTokensViaBrowser"); + + b.Property("AllowOfflineAccess"); + + b.Property("AllowPlainTextPkce"); + + b.Property("AllowRememberConsent"); + + b.Property("AlwaysIncludeUserClaimsInIdToken"); + + b.Property("AlwaysSendClientClaims"); + + b.Property("AuthorizationCodeLifetime"); + + b.Property("BackChannelLogoutSessionRequired"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(300); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ClientName") + .HasMaxLength(200); + + b.Property("ClientUri") + .HasMaxLength(300); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("EnableLocalLogin"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(300); + + b.Property("IdentityTokenLifetime"); + + b.Property("IncludeJwtId"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(300); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200); + + b.Property("RefreshTokenExpiration"); + + b.Property("RefreshTokenUsage"); + + b.Property("RequireClientSecret"); + + b.Property("RequireConsent"); + + b.Property("RequirePkce"); + + b.Property("SlidingRefreshTokenLifetime"); + + b.Property("UpdateAccessTokenClaimsOnRefresh"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(250); + + b.Property("Value") + .HasMaxLength(250); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId"); + + b.Property("Origin") + .HasMaxLength(150); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId"); + + b.Property("GrantType") + .HasMaxLength(196); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId"); + + b.Property("Provider") + .HasMaxLength(64); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId"); + + b.Property("Key") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("ClientId", "Key"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("RedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId"); + + b.Property("Scope") + .HasMaxLength(196); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime"); + + b.Property("Data") + .IsRequired(); + + b.Property("Expiration"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Id"); + + b.Property("SubjectId") + .HasMaxLength(200); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50); + + b.HasKey("Key"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.Property("IdentityResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Emphasize"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiScope") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId", "Name") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603112909_Initial.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603112909_Initial.cs new file mode 100644 index 0000000000..45cd778b73 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603112909_Initial.cs @@ -0,0 +1,1025 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Acme.BookStore.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpAuditLogs", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + ApplicationName = table.Column(maxLength: 96, nullable: true), + UserId = table.Column(nullable: true), + UserName = table.Column(maxLength: 256, nullable: true), + TenantId = table.Column(nullable: true), + TenantName = table.Column(nullable: true), + ImpersonatorUserId = table.Column(nullable: true), + ImpersonatorTenantId = table.Column(nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ClientIpAddress = table.Column(maxLength: 64, nullable: true), + ClientName = table.Column(maxLength: 128, nullable: true), + ClientId = table.Column(maxLength: 64, nullable: true), + CorrelationId = table.Column(maxLength: 64, nullable: true), + BrowserInfo = table.Column(maxLength: 512, nullable: true), + HttpMethod = table.Column(maxLength: 16, nullable: true), + Url = table.Column(maxLength: 256, nullable: true), + Exceptions = table.Column(maxLength: 4000, nullable: true), + Comments = table.Column(maxLength: 256, nullable: true), + HttpStatusCode = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpBackgroundJobs", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + JobName = table.Column(maxLength: 128, nullable: false), + JobArgs = table.Column(maxLength: 1048576, nullable: false), + TryCount = table.Column(nullable: false, defaultValue: (short)0), + CreationTime = table.Column(nullable: false), + NextTryTime = table.Column(nullable: false), + LastTryTime = table.Column(nullable: true), + IsAbandoned = table.Column(nullable: false, defaultValue: false), + Priority = table.Column(nullable: false, defaultValue: (byte)15) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpBackgroundJobs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpClaimTypes", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + Name = table.Column(maxLength: 256, nullable: false), + Required = table.Column(nullable: false), + IsStatic = table.Column(nullable: false), + Regex = table.Column(maxLength: 512, nullable: true), + RegexDescription = table.Column(maxLength: 128, nullable: true), + Description = table.Column(maxLength: 256, nullable: true), + ValueType = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpFeatureValues", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(maxLength: 128, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: true), + ProviderKey = table.Column(maxLength: 64, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpFeatureValues", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpPermissionGrants", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 128, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: false), + ProviderKey = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpRoles", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 256, nullable: false), + NormalizedName = table.Column(maxLength: 256, nullable: false), + IsDefault = table.Column(nullable: false), + IsStatic = table.Column(nullable: false), + IsPublic = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpSettings", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(maxLength: 2048, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: true), + ProviderKey = table.Column(maxLength: 64, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpSettings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpTenants", + 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), + Name = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpUsers", + 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), + UserName = table.Column(maxLength: 256, nullable: false), + NormalizedUserName = table.Column(maxLength: 256, nullable: false), + Name = table.Column(maxLength: 64, nullable: true), + Surname = table.Column(maxLength: 64, nullable: true), + Email = table.Column(maxLength: 256, nullable: true), + NormalizedEmail = table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false, defaultValue: false), + PasswordHash = table.Column(maxLength: 256, nullable: true), + SecurityStamp = table.Column(maxLength: 256, nullable: false), + PhoneNumber = table.Column(maxLength: 16, nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false, defaultValue: false), + TwoFactorEnabled = table.Column(nullable: false, defaultValue: false), + LockoutEnd = table.Column(nullable: true), + LockoutEnabled = table.Column(nullable: false, defaultValue: false), + AccessFailedCount = table.Column(nullable: false, defaultValue: 0) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiResources", + 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), + Name = table.Column(maxLength: 200, nullable: false), + DisplayName = table.Column(maxLength: 200, nullable: true), + Description = table.Column(maxLength: 1000, nullable: true), + Enabled = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiResources", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClients", + 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), + ClientId = table.Column(maxLength: 200, nullable: false), + ClientName = table.Column(maxLength: 200, nullable: true), + Description = table.Column(maxLength: 1000, nullable: true), + ClientUri = table.Column(maxLength: 300, nullable: true), + LogoUri = table.Column(maxLength: 300, nullable: true), + Enabled = table.Column(nullable: false), + ProtocolType = table.Column(maxLength: 200, nullable: false), + RequireClientSecret = table.Column(nullable: false), + RequireConsent = table.Column(nullable: false), + AllowRememberConsent = table.Column(nullable: false), + AlwaysIncludeUserClaimsInIdToken = table.Column(nullable: false), + RequirePkce = table.Column(nullable: false), + AllowPlainTextPkce = table.Column(nullable: false), + AllowAccessTokensViaBrowser = table.Column(nullable: false), + FrontChannelLogoutUri = table.Column(maxLength: 300, nullable: true), + FrontChannelLogoutSessionRequired = table.Column(nullable: false), + BackChannelLogoutUri = table.Column(maxLength: 300, nullable: true), + BackChannelLogoutSessionRequired = table.Column(nullable: false), + AllowOfflineAccess = table.Column(nullable: false), + IdentityTokenLifetime = table.Column(nullable: false), + AccessTokenLifetime = table.Column(nullable: false), + AuthorizationCodeLifetime = table.Column(nullable: false), + ConsentLifetime = table.Column(nullable: true), + AbsoluteRefreshTokenLifetime = table.Column(nullable: false), + SlidingRefreshTokenLifetime = table.Column(nullable: false), + RefreshTokenUsage = table.Column(nullable: false), + UpdateAccessTokenClaimsOnRefresh = table.Column(nullable: false), + RefreshTokenExpiration = table.Column(nullable: false), + AccessTokenType = table.Column(nullable: false), + EnableLocalLogin = table.Column(nullable: false), + IncludeJwtId = table.Column(nullable: false), + AlwaysSendClientClaims = table.Column(nullable: false), + ClientClaimsPrefix = table.Column(maxLength: 200, nullable: true), + PairWiseSubjectSalt = table.Column(maxLength: 200, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClients", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerIdentityResources", + 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), + Name = table.Column(maxLength: 200, nullable: false), + DisplayName = table.Column(maxLength: 200, nullable: true), + Description = table.Column(maxLength: 1000, nullable: true), + Enabled = table.Column(nullable: false), + Required = table.Column(nullable: false), + Emphasize = table.Column(nullable: false), + ShowInDiscoveryDocument = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerIdentityResources", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerPersistedGrants", + columns: table => new + { + Key = table.Column(maxLength: 200, nullable: false), + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + Type = table.Column(maxLength: 50, nullable: false), + SubjectId = table.Column(maxLength: 200, nullable: true), + ClientId = table.Column(maxLength: 200, nullable: false), + CreationTime = table.Column(nullable: false), + Expiration = table.Column(nullable: true), + Data = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerPersistedGrants", x => x.Key); + }); + + migrationBuilder.CreateTable( + name: "AbpAuditLogActions", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + AuditLogId = table.Column(nullable: false), + ServiceName = table.Column(maxLength: 256, nullable: true), + MethodName = table.Column(maxLength: 128, nullable: true), + Parameters = table.Column(maxLength: 2000, nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogActions", x => x.Id); + table.ForeignKey( + name: "FK_AbpAuditLogActions_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityChanges", + columns: table => new + { + Id = table.Column(nullable: false), + AuditLogId = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ChangeTime = table.Column(nullable: false), + ChangeType = table.Column(nullable: false), + EntityTenantId = table.Column(nullable: true), + EntityId = table.Column(maxLength: 128, nullable: false), + EntityTypeFullName = table.Column(maxLength: 128, nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityChanges_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ClaimType = table.Column(maxLength: 256, nullable: false), + ClaimValue = table.Column(maxLength: 1024, nullable: true), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpRoleClaims_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpTenantConnectionStrings", + columns: table => new + { + TenantId = table.Column(nullable: false), + Name = table.Column(maxLength: 64, nullable: false), + Value = table.Column(maxLength: 1024, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name }); + table.ForeignKey( + name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId", + column: x => x.TenantId, + principalTable: "AbpTenants", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserClaims", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ClaimType = table.Column(maxLength: 256, nullable: false), + ClaimValue = table.Column(maxLength: 1024, nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpUserClaims_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserLogins", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 64, nullable: false), + TenantId = table.Column(nullable: true), + ProviderKey = table.Column(maxLength: 196, nullable: false), + ProviderDisplayName = table.Column(maxLength: 128, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserLogins", x => new { x.UserId, x.LoginProvider }); + table.ForeignKey( + name: "FK_AbpUserLogins_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false), + TenantId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 64, nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + TenantId = table.Column(nullable: true), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AbpUserTokens_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiClaims", + columns: table => new + { + Type = table.Column(maxLength: 196, nullable: false), + ApiResourceId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiClaims", x => new { x.ApiResourceId, x.Type }); + table.ForeignKey( + name: "FK_IdentityServerApiClaims_IdentityServerApiResources_ApiResourceId", + column: x => x.ApiResourceId, + principalTable: "IdentityServerApiResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiScopes", + columns: table => new + { + ApiResourceId = table.Column(nullable: false), + Name = table.Column(maxLength: 196, nullable: false), + DisplayName = table.Column(maxLength: 128, nullable: true), + Description = table.Column(maxLength: 256, nullable: true), + Required = table.Column(nullable: false), + Emphasize = table.Column(nullable: false), + ShowInDiscoveryDocument = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiScopes", x => new { x.ApiResourceId, x.Name }); + table.ForeignKey( + name: "FK_IdentityServerApiScopes_IdentityServerApiResources_ApiResourceId", + column: x => x.ApiResourceId, + principalTable: "IdentityServerApiResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiSecrets", + columns: table => new + { + Type = table.Column(maxLength: 32, nullable: false), + Value = table.Column(maxLength: 196, nullable: false), + ApiResourceId = table.Column(nullable: false), + Description = table.Column(maxLength: 256, nullable: true), + Expiration = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiSecrets", x => new { x.ApiResourceId, x.Type, x.Value }); + table.ForeignKey( + name: "FK_IdentityServerApiSecrets_IdentityServerApiResources_ApiResourceId", + column: x => x.ApiResourceId, + principalTable: "IdentityServerApiResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientClaims", + columns: table => new + { + ClientId = table.Column(nullable: false), + Type = table.Column(maxLength: 250, nullable: false), + Value = table.Column(maxLength: 250, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientClaims", x => new { x.ClientId, x.Type, x.Value }); + table.ForeignKey( + name: "FK_IdentityServerClientClaims_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientCorsOrigins", + columns: table => new + { + ClientId = table.Column(nullable: false), + Origin = table.Column(maxLength: 150, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientCorsOrigins", x => new { x.ClientId, x.Origin }); + table.ForeignKey( + name: "FK_IdentityServerClientCorsOrigins_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientGrantTypes", + columns: table => new + { + ClientId = table.Column(nullable: false), + GrantType = table.Column(maxLength: 196, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientGrantTypes", x => new { x.ClientId, x.GrantType }); + table.ForeignKey( + name: "FK_IdentityServerClientGrantTypes_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientIdPRestrictions", + columns: table => new + { + ClientId = table.Column(nullable: false), + Provider = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientIdPRestrictions", x => new { x.ClientId, x.Provider }); + table.ForeignKey( + name: "FK_IdentityServerClientIdPRestrictions_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientPostLogoutRedirectUris", + columns: table => new + { + ClientId = table.Column(nullable: false), + PostLogoutRedirectUri = table.Column(maxLength: 200, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientPostLogoutRedirectUris", x => new { x.ClientId, x.PostLogoutRedirectUri }); + table.ForeignKey( + name: "FK_IdentityServerClientPostLogoutRedirectUris_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientProperties", + columns: table => new + { + ClientId = table.Column(nullable: false), + Key = table.Column(maxLength: 64, nullable: false), + Value = table.Column(maxLength: 128, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientProperties", x => new { x.ClientId, x.Key }); + table.ForeignKey( + name: "FK_IdentityServerClientProperties_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientRedirectUris", + columns: table => new + { + ClientId = table.Column(nullable: false), + RedirectUri = table.Column(maxLength: 200, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientRedirectUris", x => new { x.ClientId, x.RedirectUri }); + table.ForeignKey( + name: "FK_IdentityServerClientRedirectUris_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientScopes", + columns: table => new + { + ClientId = table.Column(nullable: false), + Scope = table.Column(maxLength: 196, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientScopes", x => new { x.ClientId, x.Scope }); + table.ForeignKey( + name: "FK_IdentityServerClientScopes_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientSecrets", + columns: table => new + { + Type = table.Column(maxLength: 32, nullable: false), + Value = table.Column(maxLength: 196, nullable: false), + ClientId = table.Column(nullable: false), + Description = table.Column(maxLength: 256, nullable: true), + Expiration = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientSecrets", x => new { x.ClientId, x.Type, x.Value }); + table.ForeignKey( + name: "FK_IdentityServerClientSecrets_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerIdentityClaims", + columns: table => new + { + Type = table.Column(maxLength: 196, nullable: false), + IdentityResourceId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerIdentityClaims", x => new { x.IdentityResourceId, x.Type }); + table.ForeignKey( + name: "FK_IdentityServerIdentityClaims_IdentityServerIdentityResources_IdentityResourceId", + column: x => x.IdentityResourceId, + principalTable: "IdentityServerIdentityResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityPropertyChanges", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + EntityChangeId = table.Column(nullable: false), + NewValue = table.Column(maxLength: 512, nullable: true), + OriginalValue = table.Column(maxLength: 512, nullable: true), + PropertyName = table.Column(maxLength: 128, nullable: false), + PropertyTypeFullName = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityPropertyChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityPropertyChanges_AbpEntityChanges_EntityChangeId", + column: x => x.EntityChangeId, + principalTable: "AbpEntityChanges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiScopeClaims", + columns: table => new + { + Type = table.Column(maxLength: 196, nullable: false), + ApiResourceId = table.Column(nullable: false), + Name = table.Column(maxLength: 196, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiScopeClaims", x => new { x.ApiResourceId, x.Name, x.Type }); + table.ForeignKey( + name: "FK_IdentityServerApiScopeClaims_IdentityServerApiScopes_ApiResourceId_Name", + columns: x => new { x.ApiResourceId, x.Name }, + principalTable: "IdentityServerApiScopes", + principalColumns: new[] { "ApiResourceId", "Name" }, + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_AuditLogId", + table: "AbpAuditLogActions", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_TenantId_ServiceName_MethodName_ExecutionTime", + table: "AbpAuditLogActions", + columns: new[] { "TenantId", "ServiceName", "MethodName", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_UserId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "UserId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpBackgroundJobs_IsAbandoned_NextTryTime", + table: "AbpBackgroundJobs", + columns: new[] { "IsAbandoned", "NextTryTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_AuditLogId", + table: "AbpEntityChanges", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_TenantId_EntityTypeFullName_EntityId", + table: "AbpEntityChanges", + columns: new[] { "TenantId", "EntityTypeFullName", "EntityId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityPropertyChanges_EntityChangeId", + table: "AbpEntityPropertyChanges", + column: "EntityChangeId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpFeatureValues_Name_ProviderName_ProviderKey", + table: "AbpFeatureValues", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoleClaims_RoleId", + table: "AbpRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoles_NormalizedName", + table: "AbpRoles", + column: "NormalizedName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpTenants_Name", + table: "AbpTenants", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserClaims_UserId", + table: "AbpUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserLogins_LoginProvider_ProviderKey", + table: "AbpUserLogins", + columns: new[] { "LoginProvider", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserRoles_RoleId_UserId", + table: "AbpUserRoles", + columns: new[] { "RoleId", "UserId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_Email", + table: "AbpUsers", + column: "Email"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedEmail", + table: "AbpUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedUserName", + table: "AbpUsers", + column: "NormalizedUserName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_UserName", + table: "AbpUsers", + column: "UserName"); + + migrationBuilder.CreateIndex( + name: "IX_IdentityServerClients_ClientId", + table: "IdentityServerClients", + column: "ClientId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_IdentityServerPersistedGrants_SubjectId_ClientId_Type", + table: "IdentityServerPersistedGrants", + columns: new[] { "SubjectId", "ClientId", "Type" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpAuditLogActions"); + + migrationBuilder.DropTable( + name: "AbpBackgroundJobs"); + + migrationBuilder.DropTable( + name: "AbpClaimTypes"); + + migrationBuilder.DropTable( + name: "AbpEntityPropertyChanges"); + + migrationBuilder.DropTable( + name: "AbpFeatureValues"); + + migrationBuilder.DropTable( + name: "AbpPermissionGrants"); + + migrationBuilder.DropTable( + name: "AbpRoleClaims"); + + migrationBuilder.DropTable( + name: "AbpSettings"); + + migrationBuilder.DropTable( + name: "AbpTenantConnectionStrings"); + + migrationBuilder.DropTable( + name: "AbpUserClaims"); + + migrationBuilder.DropTable( + name: "AbpUserLogins"); + + migrationBuilder.DropTable( + name: "AbpUserRoles"); + + migrationBuilder.DropTable( + name: "AbpUserTokens"); + + migrationBuilder.DropTable( + name: "IdentityServerApiClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerApiScopeClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerApiSecrets"); + + migrationBuilder.DropTable( + name: "IdentityServerClientClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerClientCorsOrigins"); + + migrationBuilder.DropTable( + name: "IdentityServerClientGrantTypes"); + + migrationBuilder.DropTable( + name: "IdentityServerClientIdPRestrictions"); + + migrationBuilder.DropTable( + name: "IdentityServerClientPostLogoutRedirectUris"); + + migrationBuilder.DropTable( + name: "IdentityServerClientProperties"); + + migrationBuilder.DropTable( + name: "IdentityServerClientRedirectUris"); + + migrationBuilder.DropTable( + name: "IdentityServerClientScopes"); + + migrationBuilder.DropTable( + name: "IdentityServerClientSecrets"); + + migrationBuilder.DropTable( + name: "IdentityServerIdentityClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerPersistedGrants"); + + migrationBuilder.DropTable( + name: "AbpEntityChanges"); + + migrationBuilder.DropTable( + name: "AbpTenants"); + + migrationBuilder.DropTable( + name: "AbpRoles"); + + migrationBuilder.DropTable( + name: "AbpUsers"); + + migrationBuilder.DropTable( + name: "IdentityServerApiScopes"); + + migrationBuilder.DropTable( + name: "IdentityServerClients"); + + migrationBuilder.DropTable( + name: "IdentityServerIdentityResources"); + + migrationBuilder.DropTable( + name: "AbpAuditLogs"); + + migrationBuilder.DropTable( + name: "IdentityServerApiResources"); + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603113122_Created_Book_Entity.Designer.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603113122_Created_Book_Entity.Designer.cs new file mode 100644 index 0000000000..de5a467a66 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603113122_Created_Book_Entity.Designer.cs @@ -0,0 +1,1413 @@ +// +using System; +using Acme.BookStore.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Acme.BookStore.Migrations +{ + [DbContext(typeof(BookStoreMigrationsDbContext))] + [Migration("20190603113122_Created_Book_Entity")] + partial class Created_Book_Entity + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Acme.BookStore.Book", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("Price"); + + b.Property("PublishDate"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("AppBooks"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TenantName"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTenantId"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128); + + b.Property("LastTryTime"); + + b.Property("NextTryTime"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs"); + }); + + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpFeatureValues"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .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") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("DisplayName") + .HasMaxLength(128); + + b.Property("Emphasize"); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("ApiResourceId", "Name"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Name", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AbsoluteRefreshTokenLifetime"); + + b.Property("AccessTokenLifetime"); + + b.Property("AccessTokenType"); + + b.Property("AllowAccessTokensViaBrowser"); + + b.Property("AllowOfflineAccess"); + + b.Property("AllowPlainTextPkce"); + + b.Property("AllowRememberConsent"); + + b.Property("AlwaysIncludeUserClaimsInIdToken"); + + b.Property("AlwaysSendClientClaims"); + + b.Property("AuthorizationCodeLifetime"); + + b.Property("BackChannelLogoutSessionRequired"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(300); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ClientName") + .HasMaxLength(200); + + b.Property("ClientUri") + .HasMaxLength(300); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("EnableLocalLogin"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(300); + + b.Property("IdentityTokenLifetime"); + + b.Property("IncludeJwtId"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(300); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200); + + b.Property("RefreshTokenExpiration"); + + b.Property("RefreshTokenUsage"); + + b.Property("RequireClientSecret"); + + b.Property("RequireConsent"); + + b.Property("RequirePkce"); + + b.Property("SlidingRefreshTokenLifetime"); + + b.Property("UpdateAccessTokenClaimsOnRefresh"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(250); + + b.Property("Value") + .HasMaxLength(250); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId"); + + b.Property("Origin") + .HasMaxLength(150); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId"); + + b.Property("GrantType") + .HasMaxLength(196); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId"); + + b.Property("Provider") + .HasMaxLength(64); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId"); + + b.Property("Key") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("ClientId", "Key"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("RedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId"); + + b.Property("Scope") + .HasMaxLength(196); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime"); + + b.Property("Data") + .IsRequired(); + + b.Property("Expiration"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Id"); + + b.Property("SubjectId") + .HasMaxLength(200); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50); + + b.HasKey("Key"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.Property("IdentityResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Emphasize"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiScope") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId", "Name") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603113122_Created_Book_Entity.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603113122_Created_Book_Entity.cs new file mode 100644 index 0000000000..a1aef5d9da --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/20190603113122_Created_Book_Entity.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Acme.BookStore.Migrations +{ + public partial class Created_Book_Entity : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AppBooks", + 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), + Name = table.Column(maxLength: 128, nullable: false), + Type = table.Column(nullable: false), + PublishDate = table.Column(nullable: false), + Price = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AppBooks", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AppBooks"); + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/BookStoreMigrationsDbContextModelSnapshot.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/BookStoreMigrationsDbContextModelSnapshot.cs index fcd623b85e..80e730e081 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/BookStoreMigrationsDbContextModelSnapshot.cs +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore.DbMigrations/Migrations/BookStoreMigrationsDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace Acme.BookStore.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.3-servicing-35854") + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); @@ -24,18 +24,24 @@ namespace Acme.BookStore.Migrations b.Property("Id") .ValueGeneratedOnAdd(); - b.Property("ConcurrencyStamp"); + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); - b.Property("CreationTime"); + b.Property("CreationTime") + .HasColumnName("CreationTime"); - b.Property("CreatorId"); + b.Property("CreatorId") + .HasColumnName("CreatorId"); b.Property("ExtraProperties") .HasColumnName("ExtraProperties"); - b.Property("LastModificationTime"); + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); - b.Property("LastModifierId"); + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); b.Property("Name") .IsRequired() @@ -45,7 +51,7 @@ namespace Acme.BookStore.Migrations b.Property("PublishDate"); - b.Property("Type"); + b.Property("Type"); b.HasKey("Id"); @@ -296,6 +302,32 @@ namespace Acme.BookStore.Migrations b.ToTable("AbpBackgroundJobs"); }); + modelBuilder.Entity("Volo.Abp.FeatureManagement.FeatureValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpFeatureValues"); + }); + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => { b.Property("Id") @@ -594,6 +626,482 @@ namespace Acme.BookStore.Migrations b.ToTable("AbpUserTokens"); }); + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("DisplayName") + .HasMaxLength(128); + + b.Property("Emphasize"); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("ApiResourceId", "Name"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Name", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AbsoluteRefreshTokenLifetime"); + + b.Property("AccessTokenLifetime"); + + b.Property("AccessTokenType"); + + b.Property("AllowAccessTokensViaBrowser"); + + b.Property("AllowOfflineAccess"); + + b.Property("AllowPlainTextPkce"); + + b.Property("AllowRememberConsent"); + + b.Property("AlwaysIncludeUserClaimsInIdToken"); + + b.Property("AlwaysSendClientClaims"); + + b.Property("AuthorizationCodeLifetime"); + + b.Property("BackChannelLogoutSessionRequired"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(300); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ClientName") + .HasMaxLength(200); + + b.Property("ClientUri") + .HasMaxLength(300); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("EnableLocalLogin"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(300); + + b.Property("IdentityTokenLifetime"); + + b.Property("IncludeJwtId"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(300); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200); + + b.Property("RefreshTokenExpiration"); + + b.Property("RefreshTokenUsage"); + + b.Property("RequireClientSecret"); + + b.Property("RequireConsent"); + + b.Property("RequirePkce"); + + b.Property("SlidingRefreshTokenLifetime"); + + b.Property("UpdateAccessTokenClaimsOnRefresh"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(250); + + b.Property("Value") + .HasMaxLength(250); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId"); + + b.Property("Origin") + .HasMaxLength(150); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId"); + + b.Property("GrantType") + .HasMaxLength(196); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId"); + + b.Property("Provider") + .HasMaxLength(64); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId"); + + b.Property("Key") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("ClientId", "Key"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("RedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId"); + + b.Property("Scope") + .HasMaxLength(196); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime"); + + b.Property("Data") + .IsRequired(); + + b.Property("Expiration"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Id"); + + b.Property("SubjectId") + .HasMaxLength(200); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50); + + b.HasKey("Key"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.Property("IdentityResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Emphasize"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => { b.Property("Id") @@ -778,6 +1286,118 @@ namespace Acme.BookStore.Migrations .OnDelete(DeleteBehavior.Cascade); }); + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiScope") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId", "Name") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => { b.HasOne("Volo.Abp.TenantManagement.Tenant") diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Acme.BookStore.EntityFrameworkCore.csproj b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Acme.BookStore.EntityFrameworkCore.csproj index c42ea44516..967187059a 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Acme.BookStore.EntityFrameworkCore.csproj +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/Acme.BookStore.EntityFrameworkCore.csproj @@ -9,14 +9,15 @@ - - - - - - - - + + + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContext.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContext.cs index 1aebc9b1ae..386eb2623b 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContext.cs +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContext.cs @@ -7,6 +7,15 @@ using Volo.Abp.Users.EntityFrameworkCore; namespace Acme.BookStore.EntityFrameworkCore { + /* This is your actual DbContext used on runtime. + * It includes only your entities. + * It does not include entities of the used modules, because each module has already + * its own DbContext class. If you want to share some database tables with the used modules, + * just create a structure like done for AppUser. + * + * Don't use this DbContext for database migrations since it does not contain tables of the + * used modules (as explained above). See BookStoreMigrationsDbContext for migrations. + */ [ConnectionStringName("Default")] public class BookStoreDbContext : AbpDbContext { @@ -14,6 +23,10 @@ namespace Acme.BookStore.EntityFrameworkCore public DbSet Books { get; set; } + /* Add DbSet properties for your Aggregate Roots / Entities here. + * Also map them inside BookStoreDbContextModelCreatingExtensions.ConfigureBookStore + */ + public BookStoreDbContext(DbContextOptions options) : base(options) { diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContextModelCreatingExtensions.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContextModelCreatingExtensions.cs index b7cdd8705d..d14e33208d 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContextModelCreatingExtensions.cs +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreDbContextModelCreatingExtensions.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Volo.Abp; -using Volo.Abp.Users; using Volo.Abp.EntityFrameworkCore.Modeling; +using Volo.Abp.Users; namespace Acme.BookStore.EntityFrameworkCore { @@ -17,7 +17,12 @@ namespace Acme.BookStore.EntityFrameworkCore builder.Entity(b => { b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema); + + b.ConfigureAudited(); b.ConfigureExtraProperties(); + b.ConfigureConcurrencyStamp(); + + b.Property(x => x.Name).IsRequired().HasMaxLength(128); }); } diff --git a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreEntityFrameworkCoreModule.cs b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreEntityFrameworkCoreModule.cs index a8e4d2de0c..efa5642632 100644 --- a/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreEntityFrameworkCoreModule.cs +++ b/samples/BookStore/src/Acme.BookStore.EntityFrameworkCore/EntityFrameworkCore/BookStoreEntityFrameworkCoreModule.cs @@ -1,9 +1,11 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AuditLogging.EntityFrameworkCore; using Volo.Abp.BackgroundJobs.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.SqlServer; using Volo.Abp.FeatureManagement.EntityFrameworkCore; using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.IdentityServer.EntityFrameworkCore; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.SettingManagement.EntityFrameworkCore; @@ -14,6 +16,7 @@ namespace Acme.BookStore.EntityFrameworkCore [DependsOn( typeof(BookStoreDomainModule), typeof(AbpIdentityEntityFrameworkCoreModule), + typeof(AbpIdentityServerEntityFrameworkCoreModule), typeof(AbpPermissionManagementEntityFrameworkCoreModule), typeof(AbpSettingManagementEntityFrameworkCoreModule), typeof(AbpEntityFrameworkCoreSqlServerModule), @@ -28,9 +31,17 @@ namespace Acme.BookStore.EntityFrameworkCore { context.Services.AddAbpDbContext(options => { - //Remove "includeAllEntities: true" to create default repositories only for aggregate roots + /* Remove "includeAllEntities: true" to create + * default repositories only for aggregate roots */ options.AddDefaultRepositories(includeAllEntities: true); }); + + Configure(options => + { + /* The main point to change your DBMS. + * See also BookStoreMigrationsDbContextFactory for EF Core tooling. */ + options.UseSqlServer(); + }); } } } diff --git a/samples/BookStore/src/Acme.BookStore.HttpApi.Client/Acme.BookStore.HttpApi.Client.csproj b/samples/BookStore/src/Acme.BookStore.HttpApi.Client/Acme.BookStore.HttpApi.Client.csproj new file mode 100644 index 0000000000..fd3ba12fe7 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.HttpApi.Client/Acme.BookStore.HttpApi.Client.csproj @@ -0,0 +1,21 @@ + + + + + + netstandard2.0 + Acme.BookStore + + + + + + + + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.HttpApi.Client/BookStoreHttpApiClientModule.cs b/samples/BookStore/src/Acme.BookStore.HttpApi.Client/BookStoreHttpApiClientModule.cs new file mode 100644 index 0000000000..0139522107 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.HttpApi.Client/BookStoreHttpApiClientModule.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.FeatureManagement; +using Volo.Abp.Identity; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement; +using Volo.Abp.TenantManagement; + +namespace Acme.BookStore +{ + [DependsOn( + typeof(BookStoreApplicationContractsModule), + typeof(AbpIdentityHttpApiClientModule), + typeof(AbpPermissionManagementHttpApiClientModule), + typeof(AbpTenantManagementHttpApiClientModule), + typeof(AbpFeatureManagementHttpApiClientModule) + )] + public class BookStoreHttpApiClientModule : AbpModule + { + public const string RemoteServiceName = "Default"; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddHttpClientProxies( + typeof(BookStoreApplicationContractsModule).Assembly, + RemoteServiceName + ); + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.HttpApi/Acme.BookStore.HttpApi.csproj b/samples/BookStore/src/Acme.BookStore.HttpApi/Acme.BookStore.HttpApi.csproj new file mode 100644 index 0000000000..7f819c039b --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.HttpApi/Acme.BookStore.HttpApi.csproj @@ -0,0 +1,21 @@ + + + + + + netcoreapp2.2 + Acme.BookStore + + + + + + + + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.HttpApi/BookStoreHttpApiModule.cs b/samples/BookStore/src/Acme.BookStore.HttpApi/BookStoreHttpApiModule.cs new file mode 100644 index 0000000000..2f990bfe56 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.HttpApi/BookStoreHttpApiModule.cs @@ -0,0 +1,20 @@ +using Volo.Abp.FeatureManagement; +using Volo.Abp.Identity; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.HttpApi; +using Volo.Abp.TenantManagement; + +namespace Acme.BookStore +{ + [DependsOn( + typeof(BookStoreApplicationContractsModule), + typeof(AbpIdentityHttpApiModule), + typeof(AbpPermissionManagementHttpApiModule), + typeof(AbpTenantManagementHttpApiModule), + typeof(AbpFeatureManagementHttpApiModule) + )] + public class BookStoreHttpApiModule : AbpModule + { + + } +} diff --git a/samples/BookStore/src/Acme.BookStore.HttpApi/Controllers/BookStoreController.cs b/samples/BookStore/src/Acme.BookStore.HttpApi/Controllers/BookStoreController.cs new file mode 100644 index 0000000000..4489c61c3c --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.HttpApi/Controllers/BookStoreController.cs @@ -0,0 +1,13 @@ +using Acme.BookStore.Localization; +using Volo.Abp.AspNetCore.Mvc; + +namespace Acme.BookStore.Controllers +{ + public abstract class BookStoreController : AbpController + { + protected BookStoreController() + { + //LocalizationResource = typeof(BookStoreResource); + } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.HttpApi/Controllers/TestController.cs b/samples/BookStore/src/Acme.BookStore.HttpApi/Controllers/TestController.cs new file mode 100644 index 0000000000..5f06bcfb4f --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.HttpApi/Controllers/TestController.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Mvc; +using Acme.BookStore.Models.Test; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Acme.BookStore.Controllers +{ + [Route("api/test")] + public class TestController : BookStoreController + { + public TestController() + { + + } + + [HttpGet] + [Route("")] + public async Task> GetAsync() + { + return new List + { + new TestModel {Name = "John", BirthDate = new DateTime(1942, 11, 18)}, + new TestModel {Name = "Adams", BirthDate = new DateTime(1997, 05, 24)} + }; + } + } +} diff --git a/samples/BookStore/src/Acme.BookStore.HttpApi/Models/Test/TestModel.cs b/samples/BookStore/src/Acme.BookStore.HttpApi/Models/Test/TestModel.cs new file mode 100644 index 0000000000..05381a8371 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.HttpApi/Models/Test/TestModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace Acme.BookStore.Models.Test +{ + public class TestModel + { + public string Name { get; set; } + + public DateTime BirthDate { get; set; } + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj b/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj index 46616b452a..a6e1fb1b77 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj +++ b/samples/BookStore/src/Acme.BookStore.Web/Acme.BookStore.Web.csproj @@ -4,7 +4,6 @@ netcoreapp2.2 - Acme.BookStore $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; true true @@ -20,28 +19,27 @@ - - - - - - + + + + - - - - - - + + + + + + + diff --git a/samples/BookStore/src/Acme.BookStore.Web/BookStoreBrandingProvider.cs b/samples/BookStore/src/Acme.BookStore.Web/BookStoreBrandingProvider.cs new file mode 100644 index 0000000000..4ccc981e61 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Web/BookStoreBrandingProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; + +namespace Acme.BookStore.Web +{ + [Dependency(ReplaceServices = true)] + public class BookStoreBrandingProvider : DefaultBrandingProvider + { + public override string AppName => "BookStore"; + } +} diff --git a/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebAutoMapperProfile.cs b/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebAutoMapperProfile.cs index 86c57ce794..d2ae542615 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebAutoMapperProfile.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebAutoMapperProfile.cs @@ -1,12 +1,12 @@ using AutoMapper; -namespace Acme.BookStore +namespace Acme.BookStore.Web { public class BookStoreWebAutoMapperProfile : Profile { public BookStoreWebAutoMapperProfile() { - //Configure your AutoMapper mapping configuration here... + CreateMap(); } } } diff --git a/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebModule.cs b/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebModule.cs index 9f42c3d330..bbba8b543c 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebModule.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/BookStoreWebModule.cs @@ -1,50 +1,42 @@ using System.IO; -using System.Linq; +using Acme.BookStore.EntityFrameworkCore; +using Acme.BookStore.Localization; +using Acme.BookStore.MultiTenancy; +using Acme.BookStore.Web.Menus; using Localization.Resources.AbpUi; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Acme.BookStore.EntityFrameworkCore; -using Acme.BookStore.Localization.BookStore; -using Acme.BookStore.Menus; -using Acme.BookStore.Permissions; using Swashbuckle.AspNetCore.Swagger; using Volo.Abp; using Volo.Abp.Account.Web; +using Volo.Abp.AspNetCore.Authentication.JwtBearer; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.Localization; -using Volo.Abp.AspNetCore.Mvc.UI; -using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; -using Volo.Abp.Authorization.Permissions; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; -using Volo.Abp.Data; -using Volo.Abp.Identity; using Volo.Abp.Identity.Web; using Volo.Abp.Localization; -using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Modularity; -using Volo.Abp.PermissionManagement.Web; -using Volo.Abp.Threading; -using Volo.Abp.UI; +using Volo.Abp.TenantManagement.Web; using Volo.Abp.UI.Navigation; +using Volo.Abp.Ui.Navigation.Urls; using Volo.Abp.VirtualFileSystem; -using Volo.Abp.PermissionManagement; -using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.TenantManagement.Web; - -namespace Acme.BookStore +namespace Acme.BookStore.Web { [DependsOn( + typeof(BookStoreHttpApiModule), typeof(BookStoreApplicationModule), - typeof(BookStoreEntityFrameworkCoreModule), + typeof(BookStoreEntityFrameworkCoreDbMigrationsModule), typeof(AbpAutofacModule), typeof(AbpIdentityWebModule), - typeof(AbpAccountWebModule), + typeof(AbpAccountWebIdentityServerModule), typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(AbpAspNetCoreAuthenticationJwtBearerModule), typeof(AbpTenantManagementWebModule) )] public class BookStoreWebModule : AbpModule @@ -56,7 +48,9 @@ namespace Acme.BookStore options.AddAssemblyResource( typeof(BookStoreResource), typeof(BookStoreDomainModule).Assembly, + typeof(BookStoreDomainSharedModule).Assembly, typeof(BookStoreApplicationModule).Assembly, + typeof(BookStoreApplicationContractsModule).Assembly, typeof(BookStoreWebModule).Assembly ); }); @@ -67,7 +61,8 @@ namespace Acme.BookStore var hostingEnvironment = context.Services.GetHostingEnvironment(); var configuration = context.Services.GetConfiguration(); - ConfigureDatabaseServices(); + ConfigureUrls(configuration); + ConfigureAuthentication(context, configuration); ConfigureAutoMapper(); ConfigureVirtualFileSystem(hostingEnvironment); ConfigureLocalizationServices(); @@ -76,18 +71,34 @@ namespace Acme.BookStore ConfigureSwaggerServices(context.Services); } - private void ConfigureDatabaseServices() + private void ConfigureUrls(IConfigurationRoot configuration) { - Configure(options => + Configure(options => { - options.UseSqlServer(); + options.Applications["MVC"].RootUrl = configuration["AppSelfUrl"]; }); } + private void ConfigureAuthentication(ServiceConfigurationContext context, IConfigurationRoot configuration) + { + context.Services.AddAuthentication() + .AddIdentityServerAuthentication(options => + { + options.Authority = configuration["AuthServer:Authority"]; + options.RequireHttpsMetadata = false; + options.ApiName = "BookStore"; + }); + } + private void ConfigureAutoMapper() { Configure(options => { + /* use `true` for the `validate` parameter if you want to + * validate the profile on application startup. + * See http://docs.automapper.org/en/stable/Configuration-validation.html for more + * about configuration validation. + */ options.AddProfile(); }); } @@ -98,7 +109,11 @@ namespace Acme.BookStore { Configure(options => { + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}Acme.BookStore.Domain.Shared", Path.DirectorySeparatorChar))); options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}Acme.BookStore.Domain", Path.DirectorySeparatorChar))); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}Acme.BookStore.Application.Contracts", Path.DirectorySeparatorChar))); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}Acme.BookStore.Application", Path.DirectorySeparatorChar))); + options.FileSets.ReplaceEmbeddedByPhysical(hostingEnvironment.ContentRootPath); }); } } @@ -110,10 +125,10 @@ namespace Acme.BookStore options.Resources .Get() .AddBaseTypes( - typeof(AbpValidationResource), typeof(AbpUiResource) ); + options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština")); options.Languages.Add(new LanguageInfo("en", "en", "English")); options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português")); options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe")); @@ -145,7 +160,8 @@ namespace Acme.BookStore options.SwaggerDoc("v1", new Info { Title = "BookStore API", Version = "v1" }); options.DocInclusionPredicate((docName, description) => true); options.CustomSchemaIds(type => type.FullName); - }); + } + ); } public override void OnApplicationInitialization(ApplicationInitializationContext context) @@ -164,38 +180,22 @@ namespace Acme.BookStore app.UseVirtualFiles(); app.UseAuthentication(); + app.UseJwtTokenMiddleware(); - if (BookStoreConsts.IsMultiTenancyEnabled) + if (MultiTenancyConsts.IsEnabled) { app.UseMultiTenancy(); } + app.UseIdentityServer(); app.UseAbpRequestLocalization(); - app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "BookStore API"); }); - app.UseAuditing(); - app.UseMvcWithDefaultRouteAndArea(); - - SeedDatabase(context); - } - - private static void SeedDatabase(ApplicationInitializationContext context) - { - using (var scope = context.ServiceProvider.CreateScope()) - { - AsyncHelper.RunSync(async () => - { - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync(); - }); - } } } } diff --git a/samples/BookStore/src/Acme.BookStore.Web/Branding/BookStoreBrandingProvider.cs b/samples/BookStore/src/Acme.BookStore.Web/Branding/BookStoreBrandingProvider.cs deleted file mode 100644 index e73cccf6ab..0000000000 --- a/samples/BookStore/src/Acme.BookStore.Web/Branding/BookStoreBrandingProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; -using Volo.Abp.DependencyInjection; - -namespace Acme.BookStore.Branding -{ - [Dependency(ReplaceServices = true)] - public class BookStoreBrandingProvider : DefaultBrandingProvider - { - public override string AppName => "BookStore"; - } -} diff --git a/samples/BookStore/src/Acme.BookStore.Web/Menus/BookStoreMenuContributor.cs b/samples/BookStore/src/Acme.BookStore.Web/Menus/BookStoreMenuContributor.cs index 4b6aa2821a..78459ebb41 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Menus/BookStoreMenuContributor.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Menus/BookStoreMenuContributor.cs @@ -1,11 +1,12 @@ using System.Threading.Tasks; +using Acme.BookStore.Localization; +using Acme.BookStore.MultiTenancy; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; -using Acme.BookStore.Localization.BookStore; using Volo.Abp.TenantManagement.Web.Navigation; using Volo.Abp.UI.Navigation; -namespace Acme.BookStore.Menus +namespace Acme.BookStore.Web.Menus { public class BookStoreMenuContributor : IMenuContributor { @@ -19,19 +20,20 @@ namespace Acme.BookStore.Menus private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) { - if (!BookStoreConsts.IsMultiTenancyEnabled) + if (!MultiTenancyConsts.IsEnabled) { - ApplicationMenuItem administration = context.Menu.GetAdministration(); + var administration = context.Menu.GetAdministration(); administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName); } var l = context.ServiceProvider.GetRequiredService>(); context.Menu.Items.Insert(0, new ApplicationMenuItem("BookStore.Home", l["Menu:Home"], "/")); + context.Menu.AddItem( new ApplicationMenuItem("BooksStore", l["Menu:BookStore"]) .AddItem(new ApplicationMenuItem("BooksStore.Books", l["Menu:Books"], url: "/Books")) - ); + ); } } } diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageBase.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageBase.cs index 16aad9bfa4..57c9721951 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageBase.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageBase.cs @@ -1,9 +1,9 @@ -using Microsoft.AspNetCore.Mvc.Localization; +using Acme.BookStore.Localization; +using Microsoft.AspNetCore.Mvc.Localization; using Microsoft.AspNetCore.Mvc.Razor.Internal; -using Acme.BookStore.Localization.BookStore; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; -namespace Acme.BookStore.Pages +namespace Acme.BookStore.Web.Pages { public abstract class BookStorePageBase : AbpPage { diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageModelBase.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageModelBase.cs index 5dd1fc918c..0a213892e8 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageModelBase.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/BookStorePageModelBase.cs @@ -1,7 +1,7 @@ -using Acme.BookStore.Localization.BookStore; +using Acme.BookStore.Localization; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; -namespace Acme.BookStore.Pages +namespace Acme.BookStore.Web.Pages { public abstract class BookStorePageModelBase : AbpPageModel { diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml index 5766199fbc..04a6eee412 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml @@ -1,7 +1,7 @@ @page -@inherits Acme.BookStore.Pages.BookStorePageBase +@inherits Acme.BookStore.Web.Pages.BookStorePageBase @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal -@model Acme.BookStore.Pages.Books.CreateModalModel +@model Acme.BookStore.Web.Pages.Books.CreateModalModel @{ Layout = null; } diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs index 1dc5f368fe..cca77771bd 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/CreateModal.cshtml.cs @@ -1,7 +1,7 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -namespace Acme.BookStore.Pages.Books +namespace Acme.BookStore.Web.Pages.Books { public class CreateModalModel : BookStorePageModelBase { diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml index dc03c197d6..6f1bb03494 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml @@ -1,6 +1,6 @@ @page -@inherits Acme.BookStore.Pages.BookStorePageBase -@using Acme.BookStore.Pages.Books +@inherits Acme.BookStore.Web.Pages.BookStorePageBase +@using Acme.BookStore.Web.Pages.Books @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @model EditModalModel @{ diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs index 89a3e3bb13..21aef6d072 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/EditModal.cshtml.cs @@ -2,7 +2,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -namespace Acme.BookStore.Pages.Books +namespace Acme.BookStore.Web.Pages.Books { public class EditModalModel : BookStorePageModelBase { diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml index 641652edf2..abaed993ca 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml @@ -1,9 +1,8 @@ @page -@using Acme.BookStore.Pages.Books -@inherits Acme.BookStore.Pages.BookStorePageBase -@model IndexModel +@inherits Acme.BookStore.Web.Pages.BookStorePageBase +@model Acme.BookStore.Web.Pages.Books.IndexModel @section scripts - { +{ } @@ -23,14 +22,14 @@ - - @L["Actions"] - @L["Name"] - @L["Type"] - @L["PublishDate"] - @L["Price"] - @L["CreationTime"] - + + @L["Actions"] + @L["Name"] + @L["Type"] + @L["PublishDate"] + @L["Price"] + @L["CreationTime"] + diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml.cs index cc2b70329d..4d182bf266 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/Index.cshtml.cs @@ -1,16 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.RazorPages; -namespace Acme.BookStore.Pages.Books +namespace Acme.BookStore.Web.Pages.Books { public class IndexModel : PageModel { public void OnGet() { + } } } \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/index.js b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/index.js index e1f8e44804..b5c4da072d 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/index.js +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Books/index.js @@ -18,28 +18,28 @@ { rowAction: { items: - [ - { - text: l('Edit'), - action: function (data) { - editModal.open({ id: data.record.id }); - } - }, - { - text: l('Delete'), - confirmMessage: function (data) { - return l('BookDeletionConfirmationMessage', data.record.name); + [ + { + text: l('Edit'), + action: function (data) { + editModal.open({ id: data.record.id }); + } }, - action: function (data) { - acme.bookStore.book - .delete(data.record.id) - .then(function() { - abp.notify.info(l('SuccessfullyDeleted')); - dataTable.ajax.reload(); - }); + { + text: l('Delete'), + confirmMessage: function (data) { + return l('BookDeletionConfirmationMessage', data.record.name); + }, + action: function (data) { + acme.bookStore.book + .delete(data.record.id) + .then(function () { + abp.notify.info(l('SuccessfullyDeleted')); + dataTable.ajax.reload(); + }); + } } - } - ] + ] } }, { data: "name" }, diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml index e6e24beeae..0107052885 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml @@ -1,9 +1,15 @@ @page -@using Acme.BookStore.Pages -@inherits BookStorePageBase -@model IndexModel +@inherits Acme.BookStore.Web.Pages.BookStorePageBase +@model Acme.BookStore.Web.Pages.IndexModel @section styles { - + + + +} +@section scripts { + + + } @L["Welcome"] @@ -13,7 +19,7 @@ {

@L["Login"]

} -
-

abp.io

+
+

abp.io

\ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml.cs b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml.cs index 9425a3fbbd..89e1e770e8 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.cshtml.cs @@ -1,4 +1,4 @@ -namespace Acme.BookStore.Pages +namespace Acme.BookStore.Web.Pages { public class IndexModel : BookStorePageModelBase { diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.css b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.css new file mode 100644 index 0000000000..3f73b78b9f --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.css @@ -0,0 +1,3 @@ +body { + +} diff --git a/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.js b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.js new file mode 100644 index 0000000000..46f089b9c7 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Web/Pages/Index.js @@ -0,0 +1,3 @@ +$(function () { + abp.log.debug('Index.js initialized!'); +}); \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/Program.cs b/samples/BookStore/src/Acme.BookStore.Web/Program.cs index 40b58ff6b1..0ed9fd6438 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Program.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Program.cs @@ -4,14 +4,18 @@ using Microsoft.AspNetCore.Hosting; using Serilog; using Serilog.Events; -namespace Acme.BookStore +namespace Acme.BookStore.Web { public class Program { public static int Main(string[] args) { Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() //TODO: Should be configurable! +#if DEBUG + .MinimumLevel.Debug() +#else + .MinimumLevel.Information() +#endif .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.File("Logs/logs.txt") diff --git a/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json b/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json index 6913413ef0..2503896399 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json +++ b/samples/BookStore/src/Acme.BookStore.Web/Properties/launchSettings.json @@ -4,7 +4,7 @@ "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:53929/", - "sslPort": 0 + "sslPort": 44361 } }, "profiles": { diff --git a/samples/BookStore/src/Acme.BookStore.Web/Startup.cs b/samples/BookStore/src/Acme.BookStore.Web/Startup.cs index 0cd4c700d7..13db155180 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Startup.cs +++ b/samples/BookStore/src/Acme.BookStore.Web/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Volo.Abp; -namespace Acme.BookStore +namespace Acme.BookStore.Web { public class Startup { diff --git a/samples/BookStore/src/Acme.BookStore.Web/Views/_ViewImports.cshtml b/samples/BookStore/src/Acme.BookStore.Web/Views/_ViewImports.cshtml index e4d518b1b8..c1da1f5f10 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/Views/_ViewImports.cshtml +++ b/samples/BookStore/src/Acme.BookStore.Web/Views/_ViewImports.cshtml @@ -1,3 +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 \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/abp.resourcemapping.js b/samples/BookStore/src/Acme.BookStore.Web/abp.resourcemapping.js index 96f7c92778..122e8e926a 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/abp.resourcemapping.js +++ b/samples/BookStore/src/Acme.BookStore.Web/abp.resourcemapping.js @@ -5,4 +5,4 @@ mappings: { } -} \ No newline at end of file +}; diff --git a/samples/BookStore/src/Acme.BookStore.Web/appsettings.Development.json b/samples/BookStore/src/Acme.BookStore.Web/appsettings.Development.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Web/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} diff --git a/samples/BookStore/src/Acme.BookStore.Web/appsettings.json b/samples/BookStore/src/Acme.BookStore.Web/appsettings.json index 2e5ae70aaa..c4f8c54b9c 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/appsettings.json +++ b/samples/BookStore/src/Acme.BookStore.Web/appsettings.json @@ -1,5 +1,16 @@ { + "AppSelfUrl": "https://localhost:44361", "ConnectionStrings": { "Default": "Server=localhost;Database=BookStore;Trusted_Connection=True;MultipleActiveResultSets=true" + }, + "AuthServer": { + "Authority": "https://localhost:44361" + }, + "IdentityServer": { + "Clients": { + "BookStore_ConsoleTestApp": { + "ClientId": "BookStore_ConsoleTestApp" + } + } } } \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/package.json b/samples/BookStore/src/Acme.BookStore.Web/package.json index 6d3170da8c..ef4fcf2350 100644 --- a/samples/BookStore/src/Acme.BookStore.Web/package.json +++ b/samples/BookStore/src/Acme.BookStore.Web/package.json @@ -1,6 +1,6 @@ { - "version": "0.1.0", - "name": "Acme.BookStore", + "version": "1.0.0", + "name": "my-app", "private": true, "dependencies": { "@abp/aspnetcore.mvc.ui.theme.basic": "^0.4.9" diff --git a/samples/BookStore/src/Acme.BookStore.Web/tempkey.rsa b/samples/BookStore/src/Acme.BookStore.Web/tempkey.rsa new file mode 100644 index 0000000000..f3b32de233 --- /dev/null +++ b/samples/BookStore/src/Acme.BookStore.Web/tempkey.rsa @@ -0,0 +1 @@ +{"KeyId":"ac0d2edf3c04d13cd95cc41b9b33ab03","Parameters":{"D":"FFymEWbX3XBT5YnTngOKwtz3XKsyaQbyDvvw9jbmkdvLKUmrb1hkmylqVYwU/3G1u79rrD8AZXJDoYVLegyZymK/iNeePMLI4i5vScF7PdQXn/g6CvQ8j3J60YYyfDWuxjBH8nCEUIfQIv14BpcH6CWi1BVZUbnbWcpe2ji8D5I3FHOZpvksQztst5eE836ODdN9jgk35kgrdUQ14MEFHA6m6fAzvFI1csFxD3Kwej4rI8RYZ0WYLkndz9ID88+v9VxqI8+wN6wR6tIOR510uT1FI5wONBVBUzgeZ6cCEpg0jC9tgqcP03gMDpmE7vKp34ExxH/iOWyhQ0gAxHOVlQ==","DP":"qSQfNZvEkepgY12d+uwTbRoOp+0g1CBiMU9kqyoIB56hAUnzENSCDzunjLRrvDLFPgXiUcoccskyVRnKKOGPOqPE6VjgzBrdLgA/hBf8hPg0GnTVoPyLxR9G6GsGUKsAYVn8G7cnK5wXzK4jCjsflkfxCzFfdASJ/+sa5QPWZMM=","DQ":"u+56T1+rhMvlurid9kBS2Ypfm4vOiNAXLD9kGz2wx8Ob0yYlWo55kn11qPs6Ej9bnQIY3N+TY2tXMamfhAhntoHaKrFjOpCmlHZ0GAeQOJVuWSlFvu/NBoxfjZzcUCNua22oJjy++wSdkkLLGEqau62byaQoSSqUxUzwL36RexM=","Exponent":"AQAB","InverseQ":"E+uq9g6D5LjUk+M7gtt0srnT8duwu8P83AgFqjtGOnSCy71omSboxb4zC9bGq/WaEFauFBwqxbkXwFyXuYNfIelfmSERulU9jgf0+KH6QmFdtjrJ5UO7VArqET1WUquwiDvOyO8udCxi8RRAiM5G3dTzIs5JTalGhlKEZSAgPtQ=","Modulus":"rgHACxzqvuE72RF/NdDTLsIEy3F/n6P4lkgrER60FU8uRNwSmAMRxvxYOaE3Ot/krRYcw2+MP8ewR1VBXywXVT1zuACA3SacMHJYmZQ2UkuwsD9bmpjvqoMhR/hjsI74jTzKpclHtEu7D7WYDZaIPAEIs/8+5H/z1mXVfgACaeaQt3C0OhwXSOPPDP71VKGfoFucXYED1keZ0PKxYAlhiHOe7cnSlfJseujwD2Rhyq8mUhe6aEMTYBjuruWgpfvnSOARAqu5vwzK35KpAirHwa1DJSZyanNMFdlLkVNKWUKEqd6PwRneiHacmaaDF6oQQstuSbf9cuJeSMbKh7WVQQ==","P":"0CJDit2NHk0Z1bN5ZVIYZRUYbrAI2bEOafAKqfffcA/Os7yXsY14Ye4pSpDxfdZGcPGRL04HPkJsogFtyI7k4ujrvsC66I5cg45+BhBMM0zyLJ7LZkD2HGwX2+a/xrXhhIIOnGWWy2zzW3dkayRhi1bR9krfTA5uBw1LX9qZ3Yc=","Q":"1gZITAaB+r0+PgOfyDCeVzSnTyQuKSkgbUkIgQP9jQZa6edrbAXogdJipxmUTrW7JaxifG1z9ubK+TOqhjZHqT4gd8U8Bh7jCBizZNVe60pez1OtNGpEOW2N+ZrXBSMNcV8PFaMg/B+fcaX+i7NWpTqmztR/V0DGXmD+XosuaPc="}} \ No newline at end of file diff --git a/samples/BookStore/test/Acme.BookStore.Application.Tests/Acme.BookStore.Application.Tests.csproj b/samples/BookStore/test/Acme.BookStore.Application.Tests/Acme.BookStore.Application.Tests.csproj index 6813e17ee6..2ffa2d87a1 100644 --- a/samples/BookStore/test/Acme.BookStore.Application.Tests/Acme.BookStore.Application.Tests.csproj +++ b/samples/BookStore/test/Acme.BookStore.Application.Tests/Acme.BookStore.Application.Tests.csproj @@ -9,20 +9,11 @@ - - - + - - - - - - - - + diff --git a/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestBase.cs b/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestBase.cs index 24f530b735..5263dc5ea4 100644 --- a/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestBase.cs +++ b/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestBase.cs @@ -1,12 +1,7 @@ -using Volo.Abp; - -namespace Acme.BookStore +namespace Acme.BookStore { - public abstract class BookStoreApplicationTestBase : AbpIntegratedTest + public abstract class BookStoreApplicationTestBase : BookStoreTestBase { - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } + } } diff --git a/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestModule.cs b/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestModule.cs index 102a983013..40fee21794 100644 --- a/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestModule.cs +++ b/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreApplicationTestModule.cs @@ -1,78 +1,13 @@ -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; -using Acme.BookStore.EntityFrameworkCore; -using Volo.Abp; -using Volo.Abp.Autofac; -using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.Modularity; +using Volo.Abp.Modularity; namespace Acme.BookStore { [DependsOn( typeof(BookStoreApplicationModule), - typeof(BookStoreEntityFrameworkCoreModule), - typeof(AbpAutofacModule), - typeof(AbpTestBaseModule) + typeof(BookStoreDomainTestModule) )] public class BookStoreApplicationTestModule : AbpModule { - private SqliteConnection _sqliteConnection; - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services.AddAlwaysAllowAuthorization(); - - ConfigureInMemorySqlite(context.Services); - } - - private void ConfigureInMemorySqlite(IServiceCollection services) - { - _sqliteConnection = CreateDatabaseAndGetConnection(); - - services.Configure(options => - { - options.Configure(context => { context.DbContextOptions.UseSqlite(_sqliteConnection); }); - }); - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - SeedTestData(context); - } - - public override void OnApplicationShutdown(ApplicationShutdownContext context) - { - _sqliteConnection.Dispose(); - } - - private static SqliteConnection CreateDatabaseAndGetConnection() - { - var connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); - - var options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - using (var context = new BookStoreMigrationsDbContext(options)) - { - context.GetService().CreateTables(); - } - - return connection; - } - - private static void SeedTestData(ApplicationInitializationContext context) - { - using (var scope = context.ServiceProvider.CreateScope()) - { - scope.ServiceProvider - .GetRequiredService() - .Build(); - } - } } } \ No newline at end of file diff --git a/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreTestDataBuilder.cs b/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreTestDataBuilder.cs deleted file mode 100644 index 4d31c1d4cb..0000000000 --- a/samples/BookStore/test/Acme.BookStore.Application.Tests/BookStoreTestDataBuilder.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Threading.Tasks; -using Volo.Abp.Data; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.Identity; -using Volo.Abp.Threading; - -namespace Acme.BookStore -{ - public class BookStoreTestDataBuilder : ITransientDependency - { - private readonly IDataSeeder _dataSeeder; - private readonly IRepository _bookRepository; - - public BookStoreTestDataBuilder(IDataSeeder dataSeeder, IRepository bookRepository) - { - _dataSeeder = dataSeeder; - _bookRepository = bookRepository; - } - - public void Build() - { - AsyncHelper.RunSync(BuildInternalAsync); - } - - public async Task BuildInternalAsync() - { - await _dataSeeder.SeedAsync(); - await _bookRepository.InsertAsync( - new Book - { - Id = Guid.NewGuid(), - Name = "Test book 1", - Type = BookType.Fantastic, - PublishDate = new DateTime(2015, 05, 24), - Price = 21 - } - ); - - await _bookRepository.InsertAsync( - new Book - { - Id = Guid.NewGuid(), - Name = "Test book 2", - Type = BookType.Science, - PublishDate = new DateTime(2014, 02, 11), - Price = 15 - } - ); - } - } -} \ No newline at end of file diff --git a/samples/BookStore/test/Acme.BookStore.Application.Tests/Samples/SampleAppServiceTests.cs b/samples/BookStore/test/Acme.BookStore.Application.Tests/Samples/SampleAppServiceTests.cs new file mode 100644 index 0000000000..cd059e5639 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.Application.Tests/Samples/SampleAppServiceTests.cs @@ -0,0 +1,33 @@ +using Shouldly; +using System.Threading.Tasks; +using Volo.Abp.Identity; +using Xunit; + +namespace Acme.BookStore.Samples +{ + /* This is just an example test class. + * Normally, you don't test code of the modules you are using + * (like IIdentityUserAppService here). + * Only test your own application services. + */ + public class SampleAppServiceTests : BookStoreApplicationTestBase + { + private readonly IIdentityUserAppService _userAppService; + + public SampleAppServiceTests() + { + _userAppService = GetRequiredService(); + } + + [Fact] + public async Task Initial_Data_Should_Contain_Admin_User() + { + //Act + var result = await _userAppService.GetListAsync(new GetIdentityUsersInput()); + + //Assert + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldContain(u => u.UserName == "admin"); + } + } +} diff --git a/samples/BookStore/test/Acme.BookStore.Application.Tests/Samples/SampleTests.cs b/samples/BookStore/test/Acme.BookStore.Application.Tests/Samples/SampleTests.cs deleted file mode 100644 index 0470eaec1e..0000000000 --- a/samples/BookStore/test/Acme.BookStore.Application.Tests/Samples/SampleTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Acme.BookStore.Users; -using Shouldly; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.Identity; -using Volo.Abp.Uow; -using Xunit; - -namespace Acme.BookStore.Samples -{ - public class SampleTests : BookStoreApplicationTestBase - { - private readonly IIdentityUserAppService _userAppService; - private readonly IRepository _appUserRepository; - private readonly IUnitOfWorkManager _unitOfWorkManager; - - public SampleTests() - { - _userAppService = ServiceProvider.GetRequiredService(); - _appUserRepository = ServiceProvider.GetRequiredService>(); - _unitOfWorkManager = ServiceProvider.GetRequiredService(); - } - - [Fact] - public async Task Initial_Data_Should_Contain_Admin_User() - { - //Act - var result = await _userAppService.GetListAsync(new GetIdentityUsersInput()); - - //Assert - result.TotalCount.ShouldBeGreaterThan(0); - result.Items.ShouldContain(u => u.UserName == "admin"); - } - - [Fact] - public async Task Should_Query_AppUser() - { - /* Need to manually start Unit Of Work because - * FirstOrDefaultAsync should be executed while db connection / context is available. - */ - using (var uow = _unitOfWorkManager.Begin()) - { - //Act - var adminUser = await _appUserRepository - .Where(u => u.UserName == "admin") - .FirstOrDefaultAsync(); - - //Assert - adminUser.ShouldNotBeNull(); - - await uow.CompleteAsync(); - } - } - } -} diff --git a/samples/BookStore/test/Acme.BookStore.Domain.Tests/Acme.BookStore.Domain.Tests.csproj b/samples/BookStore/test/Acme.BookStore.Domain.Tests/Acme.BookStore.Domain.Tests.csproj new file mode 100644 index 0000000000..610d67b5bc --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.Domain.Tests/Acme.BookStore.Domain.Tests.csproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp2.2 + Acme.BookStore + + + + + + + + + + + diff --git a/samples/BookStore/test/Acme.BookStore.Domain.Tests/BookStoreDomainTestBase.cs b/samples/BookStore/test/Acme.BookStore.Domain.Tests/BookStoreDomainTestBase.cs new file mode 100644 index 0000000000..a8d86b009d --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.Domain.Tests/BookStoreDomainTestBase.cs @@ -0,0 +1,7 @@ +namespace Acme.BookStore +{ + public abstract class BookStoreDomainTestBase : BookStoreTestBase + { + + } +} diff --git a/samples/BookStore/test/Acme.BookStore.Domain.Tests/BookStoreDomainTestModule.cs b/samples/BookStore/test/Acme.BookStore.Domain.Tests/BookStoreDomainTestModule.cs new file mode 100644 index 0000000000..7b3fb6fc79 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.Domain.Tests/BookStoreDomainTestModule.cs @@ -0,0 +1,13 @@ +using Acme.BookStore.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Acme.BookStore +{ + [DependsOn( + typeof(BookStoreEntityFrameworkCoreTestModule) + )] + public class BookStoreDomainTestModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/samples/BookStore/test/Acme.BookStore.Domain.Tests/Samples/SampleDomainTests.cs b/samples/BookStore/test/Acme.BookStore.Domain.Tests/Samples/SampleDomainTests.cs new file mode 100644 index 0000000000..0d8978e7f5 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.Domain.Tests/Samples/SampleDomainTests.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Identity; +using Xunit; + +namespace Acme.BookStore.Samples +{ + /* This is just an example test class. + * Normally, you don't test code of the modules you are using + * (like IdentityUserManager here). + * Only test your own domain services. + */ + public class SampleDomainTests : BookStoreDomainTestBase + { + private readonly IIdentityUserRepository _identityUserRepository; + private readonly IdentityUserManager _identityUserManager; + + public SampleDomainTests() + { + _identityUserRepository = GetRequiredService(); + _identityUserManager = GetRequiredService(); + } + + [Fact] + public async Task Should_Set_Email_Of_A_User() + { + IdentityUser adminUser; + + /* Need to manually start Unit Of Work because + * FirstOrDefaultAsync should be executed while db connection / context is available. + */ + await WithUnitOfWorkAsync(async () => + { + adminUser = await _identityUserRepository + .FindByNormalizedUserNameAsync("ADMIN"); + + await _identityUserManager.SetEmailAsync(adminUser, "newemail@abp.io"); + await _identityUserRepository.UpdateAsync(adminUser); + }); + + adminUser = await _identityUserRepository.FindByNormalizedUserNameAsync("ADMIN"); + adminUser.Email.ShouldBe("newemail@abp.io"); + } + } +} diff --git a/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/Acme.BookStore.EntityFrameworkCore.Tests.csproj b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/Acme.BookStore.EntityFrameworkCore.Tests.csproj new file mode 100644 index 0000000000..7c0705012f --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/Acme.BookStore.EntityFrameworkCore.Tests.csproj @@ -0,0 +1,21 @@ + + + + + + netcoreapp2.2 + Acme.BookStore + + + + + + + + + + + + + + diff --git a/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/BookStoreEntityFrameworkCoreTestBase.cs b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/BookStoreEntityFrameworkCoreTestBase.cs new file mode 100644 index 0000000000..792c2c3a12 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/BookStoreEntityFrameworkCoreTestBase.cs @@ -0,0 +1,9 @@ +using Volo.Abp; + +namespace Acme.BookStore.EntityFrameworkCore +{ + public abstract class BookStoreEntityFrameworkCoreTestBase : BookStoreTestBase + { + + } +} diff --git a/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/BookStoreEntityFrameworkCoreTestModule.cs b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/BookStoreEntityFrameworkCoreTestModule.cs new file mode 100644 index 0000000000..e1171d6d90 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/BookStoreEntityFrameworkCoreTestModule.cs @@ -0,0 +1,57 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace Acme.BookStore.EntityFrameworkCore +{ + [DependsOn( + typeof(BookStoreEntityFrameworkCoreDbMigrationsModule), + typeof(BookStoreTestBaseModule) + )] + public class BookStoreEntityFrameworkCoreTestModule : AbpModule + { + private SqliteConnection _sqliteConnection; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + ConfigureInMemorySqlite(context.Services); + } + + private void ConfigureInMemorySqlite(IServiceCollection services) + { + _sqliteConnection = CreateDatabaseAndGetConnection(); + + services.Configure(options => + { + options.Configure(context => { context.DbContextOptions.UseSqlite(_sqliteConnection); }); + }); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + _sqliteConnection.Dispose(); + } + + private static SqliteConnection CreateDatabaseAndGetConnection() + { + var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + + using (var context = new BookStoreMigrationsDbContext(options)) + { + context.GetService().CreateTables(); + } + + return connection; + } + } +} diff --git a/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/Samples/SampleRepositoryTests.cs b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/Samples/SampleRepositoryTests.cs new file mode 100644 index 0000000000..3cda13f3ea --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.EntityFrameworkCore.Tests/EntityFrameworkCore/Samples/SampleRepositoryTests.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore; +using Acme.BookStore.Users; +using Shouldly; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Xunit; + +namespace Acme.BookStore.EntityFrameworkCore.Samples +{ + /* This is just an example test class. + * Normally, you don't test ABP framework code + * (like default AppUser repository IRepository here). + * Only test your custom repository methods. + */ + public class SampleRepositoryTests : BookStoreEntityFrameworkCoreTestBase + { + private readonly IRepository _appUserRepository; + + public SampleRepositoryTests() + { + _appUserRepository = GetRequiredService>(); + } + + [Fact] + public async Task Should_Query_AppUser() + { + /* Need to manually start Unit Of Work because + * FirstOrDefaultAsync should be executed while db connection / context is available. + */ + await WithUnitOfWorkAsync(async () => + { + //Act + var adminUser = await _appUserRepository + .Where(u => u.UserName == "admin") + .FirstOrDefaultAsync(); + + //Assert + adminUser.ShouldNotBeNull(); + }); + } + } +} diff --git a/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/Acme.BookStore.HttpApi.Client.ConsoleTestApp.csproj b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/Acme.BookStore.HttpApi.Client.ConsoleTestApp.csproj new file mode 100644 index 0000000000..325c7af448 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/Acme.BookStore.HttpApi.Client.ConsoleTestApp.csproj @@ -0,0 +1,24 @@ + + + + Exe + netcoreapp2.2 + + + + + + + + + PreserveNewest + Always + + + + + + + + + diff --git a/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/BookStoreConsoleApiClientModule.cs b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/BookStoreConsoleApiClientModule.cs new file mode 100644 index 0000000000..4115bcd2ca --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/BookStoreConsoleApiClientModule.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Http.Client.IdentityModel; +using Volo.Abp.Modularity; + +namespace Acme.BookStore.HttpApi.Client.ConsoleTestApp +{ + [DependsOn( + typeof(BookStoreHttpApiClientModule), + typeof(AbpHttpClientIdentityModelModule) + )] + public class BookStoreConsoleApiClientModule : AbpModule + { + + } +} diff --git a/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/ClientDemoService.cs b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/ClientDemoService.cs new file mode 100644 index 0000000000..edfde1aae6 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/ClientDemoService.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Identity; + +namespace Acme.BookStore.HttpApi.Client.ConsoleTestApp +{ + public class ClientDemoService : ITransientDependency + { + private readonly IProfileAppService _profileAppService; + + public ClientDemoService(IProfileAppService profileAppService) + { + _profileAppService = profileAppService; + } + + public async Task RunAsync() + { + var output = await _profileAppService.GetAsync(); + Console.WriteLine($"UserName : {output.UserName}"); + Console.WriteLine($"Email : {output.Email}"); + Console.WriteLine($"Name : {output.Name}"); + Console.WriteLine($"Surname : {output.Surname}"); + } + } +} \ No newline at end of file diff --git a/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/Program.cs b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/Program.cs new file mode 100644 index 0000000000..ecef29d963 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/Program.cs @@ -0,0 +1,24 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Threading; + +namespace Acme.BookStore.HttpApi.Client.ConsoleTestApp +{ + class Program + { + static void Main(string[] args) + { + using (var application = AbpApplicationFactory.Create()) + { + application.Initialize(); + + var demo = application.ServiceProvider.GetRequiredService(); + AsyncHelper.RunSync(() => demo.RunAsync()); + + Console.WriteLine("Press ENTER to stop application..."); + Console.ReadLine(); + } + } + } +} diff --git a/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/appsettings.json b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/appsettings.json new file mode 100644 index 0000000000..c220f47daf --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.HttpApi.Client.ConsoleTestApp/appsettings.json @@ -0,0 +1,18 @@ +{ + "RemoteServices": { + "Default": { + "BaseUrl": "https://localhost:44361/" + } + }, + "IdentityClients": { + "Default": { + "GrantType": "password", + "ClientId": "BookStore_ConsoleTestApp", + "ClientSecret": "1q2w3e*", + "UserName": "admin", + "UserPassword": "1q2w3E*", + "Authority": "https://localhost:44361", + "Scope": "BookStore" + } + } +} \ No newline at end of file diff --git a/samples/BookStore/test/Acme.BookStore.TestBase/Acme.BookStore.TestBase.csproj b/samples/BookStore/test/Acme.BookStore.TestBase/Acme.BookStore.TestBase.csproj new file mode 100644 index 0000000000..882396615e --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.TestBase/Acme.BookStore.TestBase.csproj @@ -0,0 +1,26 @@ + + + + + + netcoreapp2.2 + Acme.BookStore + + + + + + + + + + + + + + + + + + + diff --git a/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestBase.cs b/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestBase.cs new file mode 100644 index 0000000000..ba9d7c6b57 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestBase.cs @@ -0,0 +1,100 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Modularity; +using Volo.Abp.Uow; + +namespace Acme.BookStore +{ + /* All test classes are derived from this class, directly or indirectly. + */ + public abstract class BookStoreTestBase : AbpIntegratedTest + where TStartupModule : IAbpModule + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + + protected virtual void WithUnitOfWork(Action action) + { + WithUnitOfWork(new UnitOfWorkOptions(), action); + } + + protected virtual void WithUnitOfWork(UnitOfWorkOptions options, Action action) + { + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (var uow = uowManager.Begin(options)) + { + action(); + + uow.Complete(); + } + } + } + + protected virtual Task WithUnitOfWorkAsync(Func func) + { + return WithUnitOfWorkAsync(new UnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(UnitOfWorkOptions 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 TResult WithUnitOfWork(Func func) + { + return WithUnitOfWork(new UnitOfWorkOptions(), func); + } + + protected virtual TResult WithUnitOfWork(UnitOfWorkOptions options, Func func) + { + using (var scope = ServiceProvider.CreateScope()) + { + var uowManager = scope.ServiceProvider.GetRequiredService(); + + using (var uow = uowManager.Begin(options)) + { + var result = func(); + uow.Complete(); + return result; + } + } + } + + protected virtual Task WithUnitOfWorkAsync(Func> func) + { + return WithUnitOfWorkAsync(new UnitOfWorkOptions(), func); + } + + protected virtual async Task WithUnitOfWorkAsync(UnitOfWorkOptions 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/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestBaseModule.cs b/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestBaseModule.cs new file mode 100644 index 0000000000..3674dd6503 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestBaseModule.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp; +using Volo.Abp.Authorization; +using Volo.Abp.Autofac; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.Data; +using Volo.Abp.Modularity; +using Volo.Abp.Threading; + +namespace Acme.BookStore +{ + [DependsOn( + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule), + typeof(AbpAuthorizationModule), + typeof(BookStoreDomainModule) + )] + public class BookStoreTestBaseModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.IsJobExecutionEnabled = false; + }); + + context.Services.AddAlwaysAllowAuthorization(); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + AsyncHelper.RunSync(async () => + { + using (var scope = context.ServiceProvider.CreateScope()) + { + await scope.ServiceProvider + .GetRequiredService() + .SeedAsync(); + } + }); + } + } +} diff --git a/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestDataSeedContributor.cs b/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestDataSeedContributor.cs new file mode 100644 index 0000000000..eb11db2b11 --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.TestBase/BookStoreTestDataSeedContributor.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; + +namespace Acme.BookStore +{ + public class BookStoreTestDataSeedContributor : IDataSeedContributor, ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IGuidGenerator _guidGenerator; + + public BookStoreTestDataSeedContributor( + IRepository bookRepository, + IGuidGenerator guidGenerator) + { + _bookRepository = bookRepository; + _guidGenerator = guidGenerator; + } + + public async Task SeedAsync(DataSeedContext context) + { + await _bookRepository.InsertAsync( + new Book + { + Id = _guidGenerator.Create(), + Name = "Test book 1", + Type = BookType.Fantastic, + PublishDate = new DateTime(2015, 05, 24), + Price = 21 + } + ); + + await _bookRepository.InsertAsync( + new Book + { + Id = _guidGenerator.Create(), + Name = "Test book 2", + Type = BookType.Science, + PublishDate = new DateTime(2014, 02, 11), + Price = 15 + } + ); + } + } +} \ No newline at end of file diff --git a/samples/BookStore/test/Acme.BookStore.Web.Tests/Acme.BookStore.Web.Tests.csproj b/samples/BookStore/test/Acme.BookStore.Web.Tests/Acme.BookStore.Web.Tests.csproj index 263836714f..751672baee 100644 --- a/samples/BookStore/test/Acme.BookStore.Web.Tests/Acme.BookStore.Web.Tests.csproj +++ b/samples/BookStore/test/Acme.BookStore.Web.Tests/Acme.BookStore.Web.Tests.csproj @@ -14,10 +14,10 @@
- + - + diff --git a/samples/BookStore/test/Acme.BookStore.Web.Tests/BookStoreWebTestModule.cs b/samples/BookStore/test/Acme.BookStore.Web.Tests/BookStoreWebTestModule.cs index efae3e3755..c544d97116 100644 --- a/samples/BookStore/test/Acme.BookStore.Web.Tests/BookStoreWebTestModule.cs +++ b/samples/BookStore/test/Acme.BookStore.Web.Tests/BookStoreWebTestModule.cs @@ -4,17 +4,13 @@ using Localization.Resources.AbpUi; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Acme.BookStore.Localization.BookStore; -using Acme.BookStore.Menus; +using Acme.BookStore.Localization; +using Acme.BookStore.Web; +using Acme.BookStore.Web.Menus; using Volo.Abp; -using Volo.Abp.Account.Web; -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.TestBase; -using Volo.Abp.Identity.Web; using Volo.Abp.Localization; using Volo.Abp.Localization.Resources.AbpValidation; using Volo.Abp.Modularity; @@ -24,10 +20,8 @@ namespace Acme.BookStore { [DependsOn( typeof(AbpAspNetCoreTestBaseModule), - typeof(BookStoreApplicationTestModule), - typeof(AbpIdentityWebModule), - typeof(AbpAccountWebModule), - typeof(AbpAspNetCoreMvcUiBasicThemeModule) + typeof(BookStoreWebModule), + typeof(BookStoreApplicationTestModule) )] public class BookStoreWebTestModule : AbpModule { diff --git a/samples/BookStore/test/Acme.BookStore.Web.Tests/Pages/Index_Tests.cs b/samples/BookStore/test/Acme.BookStore.Web.Tests/Pages/Index_Tests.cs new file mode 100644 index 0000000000..1964a8946e --- /dev/null +++ b/samples/BookStore/test/Acme.BookStore.Web.Tests/Pages/Index_Tests.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Acme.BookStore.Pages +{ + public class Index_Tests : BookStoreWebTestBase + { + [Fact] + public async Task Welcome_Page() + { + var response = await GetResponseAsStringAsync("/Books"); + response.ShouldNotBeNull(); + } + } +} diff --git a/samples/BookStore/test/Acme.BookStore.Web.Tests/Samples/SampleWebTest.cs b/samples/BookStore/test/Acme.BookStore.Web.Tests/Samples/SampleWebTest.cs deleted file mode 100644 index 2192ff8559..0000000000 --- a/samples/BookStore/test/Acme.BookStore.Web.Tests/Samples/SampleWebTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using Shouldly; -using Xunit; - -namespace Acme.BookStore.Samples -{ - public class SampleWebTest : BookStoreWebTestBase - { - [Fact(Skip = "This is disabled since not working")] - public async Task Welcome_Page() - { - var response = await GetResponseAsStringAsync("/"); - response.ShouldNotBeNull(); - } - - [Fact(Skip = "This is disabled since not working")] - public async Task Login_Page() - { - var response = await GetResponseAsStringAsync("/Account/Login/"); - response.ShouldNotBeNull(); - } - } -} diff --git a/templates/module/.gitattributes b/samples/DashboardDemo/.gitattributes similarity index 100% rename from templates/module/.gitattributes rename to samples/DashboardDemo/.gitattributes diff --git a/samples/DashboardDemo/.gitignore b/samples/DashboardDemo/.gitignore new file mode 100644 index 0000000000..3c7ee2487b --- /dev/null +++ b/samples/DashboardDemo/.gitignore @@ -0,0 +1,255 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# DashboardDemo +src/DashboardDemo.Web/Logs/logs.txt diff --git a/samples/DashboardDemo/DashboardDemo.sln b/samples/DashboardDemo/DashboardDemo.sln new file mode 100644 index 0000000000..c6b0948d70 --- /dev/null +++ b/samples/DashboardDemo/DashboardDemo.sln @@ -0,0 +1,74 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardDemo.Domain", "src\DashboardDemo.Domain\DashboardDemo.Domain.csproj", "{554AD327-6DBA-4F8F-96F8-81CE7A0C863F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardDemo.Application", "src\DashboardDemo.Application\DashboardDemo.Application.csproj", "{1A94A50E-06DC-43C1-80B5-B662820EC3EB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardDemo.EntityFrameworkCore", "src\DashboardDemo.EntityFrameworkCore\DashboardDemo.EntityFrameworkCore.csproj", "{C956DD76-69C8-4A9C-83EA-D17DF83340FD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardDemo.Web", "src\DashboardDemo.Web\DashboardDemo.Web.csproj", "{068855E8-9240-4F1A-910B-CF825794513B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CA9AC87F-097E-4F15-8393-4BC07735A5B0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{04DBDB01-70F4-4E06-B468-8F87850B22BE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardDemo.Application.Tests", "test\DashboardDemo.Application.Tests\DashboardDemo.Application.Tests.csproj", "{50B2631D-129C-47B3-A587-029CCD6099BC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardDemo.Web.Tests", "test\DashboardDemo.Web.Tests\DashboardDemo.Web.Tests.csproj", "{5F1B28C6-8D0C-4155-92D0-252F7EA5F674}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DashboardDemo.EntityFrameworkCore.DbMigrations", "src\DashboardDemo.EntityFrameworkCore.DbMigrations\DashboardDemo.EntityFrameworkCore.DbMigrations.csproj", "{0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {554AD327-6DBA-4F8F-96F8-81CE7A0C863F}.Release|Any CPU.Build.0 = Release|Any CPU + {1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A94A50E-06DC-43C1-80B5-B662820EC3EB}.Release|Any CPU.Build.0 = Release|Any CPU + {C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C956DD76-69C8-4A9C-83EA-D17DF83340FD}.Release|Any CPU.Build.0 = Release|Any CPU + {068855E8-9240-4F1A-910B-CF825794513B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {068855E8-9240-4F1A-910B-CF825794513B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {068855E8-9240-4F1A-910B-CF825794513B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {068855E8-9240-4F1A-910B-CF825794513B}.Release|Any CPU.Build.0 = Release|Any CPU + {50B2631D-129C-47B3-A587-029CCD6099BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50B2631D-129C-47B3-A587-029CCD6099BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50B2631D-129C-47B3-A587-029CCD6099BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50B2631D-129C-47B3-A587-029CCD6099BC}.Release|Any CPU.Build.0 = Release|Any CPU + {5F1B28C6-8D0C-4155-92D0-252F7EA5F674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F1B28C6-8D0C-4155-92D0-252F7EA5F674}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F1B28C6-8D0C-4155-92D0-252F7EA5F674}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F1B28C6-8D0C-4155-92D0-252F7EA5F674}.Release|Any CPU.Build.0 = Release|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {554AD327-6DBA-4F8F-96F8-81CE7A0C863F} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {1A94A50E-06DC-43C1-80B5-B662820EC3EB} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {C956DD76-69C8-4A9C-83EA-D17DF83340FD} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {068855E8-9240-4F1A-910B-CF825794513B} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + {50B2631D-129C-47B3-A587-029CCD6099BC} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} + {5F1B28C6-8D0C-4155-92D0-252F7EA5F674} = {04DBDB01-70F4-4E06-B468-8F87850B22BE} + {0372FA84-C517-4EB3-9A9F-B9ACAC0CA5E0} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {28315BFD-90E7-4E14-A2EA-F3D23AF4126F} + EndGlobalSection +EndGlobal diff --git a/samples/DashboardDemo/DashboardDemo.sln.DotSettings b/samples/DashboardDemo/DashboardDemo.sln.DotSettings new file mode 100644 index 0000000000..b89df7b544 --- /dev/null +++ b/samples/DashboardDemo/DashboardDemo.sln.DotSettings @@ -0,0 +1,7 @@ + + True + D:\github\abp\common.DotSettings + ..\..\..\common.DotSettings + True + 1 + \ No newline at end of file diff --git a/samples/DashboardDemo/common.props b/samples/DashboardDemo/common.props new file mode 100644 index 0000000000..7585f303ba --- /dev/null +++ b/samples/DashboardDemo/common.props @@ -0,0 +1,7 @@ + + + latest + 1.0.0 + $(NoWarn);CS1591 + + \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemo.Application.csproj b/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemo.Application.csproj new file mode 100644 index 0000000000..9bbd573401 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemo.Application.csproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp2.2 + DashboardDemo + + + + + + + + + + + diff --git a/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemoApplicationAutoMapperProfile.cs b/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemoApplicationAutoMapperProfile.cs new file mode 100644 index 0000000000..4fcd9fbab3 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemoApplicationAutoMapperProfile.cs @@ -0,0 +1,12 @@ +using AutoMapper; + +namespace DashboardDemo +{ + public class DashboardDemoApplicationAutoMapperProfile : Profile + { + public DashboardDemoApplicationAutoMapperProfile() + { + //Configure your AutoMapper mapping configuration here... + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemoApplicationModule.cs b/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemoApplicationModule.cs new file mode 100644 index 0000000000..afb761cce4 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Application/DashboardDemoApplicationModule.cs @@ -0,0 +1,27 @@ +using Volo.Abp.AutoMapper; +using Volo.Abp.FeatureManagement; +using Volo.Abp.Identity; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement; +using Volo.Abp.TenantManagement; + +namespace DashboardDemo +{ + [DependsOn( + typeof(DashboardDemoDomainModule), + typeof(AbpIdentityApplicationModule), + typeof(AbpPermissionManagementApplicationModule), + typeof(AbpTenantManagementApplicationModule), + typeof(AbpFeatureManagementApplicationModule) + )] + public class DashboardDemoApplicationModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.AddProfile(); + }); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Application/Permissions/DashboardDemoPermissionDefinitionProvider.cs b/samples/DashboardDemo/src/DashboardDemo.Application/Permissions/DashboardDemoPermissionDefinitionProvider.cs new file mode 100644 index 0000000000..07cb79194a --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Application/Permissions/DashboardDemoPermissionDefinitionProvider.cs @@ -0,0 +1,22 @@ +using DashboardDemo.Localization.DashboardDemo; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace DashboardDemo.Permissions +{ + public class DashboardDemoPermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var myGroup = context.AddGroup(DashboardDemoPermissions.GroupName); + + //Define your own permissions here. Examaple: + //myGroup.AddPermission(DashboardDemoPermissions.MyPermission1, L("Permission:MyPermission1")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Application/Permissions/DashboardDemoPermissions.cs b/samples/DashboardDemo/src/DashboardDemo.Application/Permissions/DashboardDemoPermissions.cs new file mode 100644 index 0000000000..3a09652db2 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Application/Permissions/DashboardDemoPermissions.cs @@ -0,0 +1,19 @@ +using System; +using Volo.Abp.Reflection; + +namespace DashboardDemo.Permissions +{ + public static class DashboardDemoPermissions + { + public const string GroupName = "DashboardDemo"; + + //Add your own permission names. Example: + //public const string MyPermission1 = GroupName + ".MyPermission1"; + + public static string[] GetAll() + { + //Return an array of all permissions + return ReflectionHelper.GetPublicConstantsRecursively(typeof(DashboardDemoPermissions)); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemo.Domain.csproj b/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemo.Domain.csproj new file mode 100644 index 0000000000..d036f48de2 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemo.Domain.csproj @@ -0,0 +1,25 @@ + + + + + + netcoreapp2.2 + DashboardDemo + + + + + + + + + + + + + + + + + + diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemoConsts.cs b/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemoConsts.cs new file mode 100644 index 0000000000..25e571159d --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemoConsts.cs @@ -0,0 +1,11 @@ +namespace DashboardDemo +{ + public static class DashboardDemoConsts + { + public const string DbTablePrefix = "App"; + + public const string DbSchema = null; + + public const bool IsMultiTenancyEnabled = true; + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemoDomainModule.cs b/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemoDomainModule.cs new file mode 100644 index 0000000000..f101211a9a --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/DashboardDemoDomainModule.cs @@ -0,0 +1,49 @@ +using DashboardDemo.Localization.DashboardDemo; +using Volo.Abp.Auditing; +using Volo.Abp.AuditLogging; +using Volo.Abp.BackgroundJobs; +using Volo.Abp.FeatureManagement; +using Volo.Abp.Identity; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpValidation; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.PermissionManagement.Identity; +using Volo.Abp.TenantManagement; +using Volo.Abp.VirtualFileSystem; + +namespace DashboardDemo +{ + [DependsOn( + typeof(AbpIdentityDomainModule), + typeof(AbpPermissionManagementDomainIdentityModule), + typeof(AbpAuditingModule), + typeof(BackgroundJobsDomainModule), + typeof(AbpAuditLoggingDomainModule), + typeof(AbpTenantManagementDomainModule), + typeof(AbpFeatureManagementDomainModule) + )] + public class DashboardDemoDomainModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.FileSets.AddEmbedded("DashboardDemo"); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddBaseTypes(typeof(AbpValidationResource)) + .AddVirtualJson("/Localization/DashboardDemo"); + }); + + Configure(options => + { + options.IsEnabled = DashboardDemoConsts.IsMultiTenancyEnabled; + }); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/DashboardDemoResource.cs b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/DashboardDemoResource.cs new file mode 100644 index 0000000000..52f9f3cc47 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/DashboardDemoResource.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Localization; + +namespace DashboardDemo.Localization.DashboardDemo +{ + [LocalizationResourceName("DashboardDemo")] + public class DashboardDemoResource + { + + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/cs.json b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/cs.json new file mode 100644 index 0000000000..01438ba1e8 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/cs.json @@ -0,0 +1,10 @@ +{ + "culture": "cs", + "texts": { + "Menu:Home": "Úvod", + "Welcome": "Vítejte", + "RolesInApplication": "Role v aplikaci", + "UserCount": "Počet uživatelů", + "LongWelcomeMessage": "Vítejte v aplikaci. Toto je startovací projekt založený na ABP frameworku. Pro více informací, navštivte abp.io." + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/en.json b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/en.json new file mode 100644 index 0000000000..aa3df71bf2 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/en.json @@ -0,0 +1,10 @@ +{ + "culture": "en", + "texts": { + "Menu:Home": "Home", + "Welcome": "Welcome", + "RolesInApplication": "Roles in application", + "UserCount": "User count", + "LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io." + } +} \ No newline at end of file diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/Localization/MyProjectName/pt-BR.json b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/pt-BR.json similarity index 100% rename from templates/mvc/src/MyCompanyName.MyProjectName.Domain/Localization/MyProjectName/pt-BR.json rename to samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/pt-BR.json diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/Localization/MyProjectName/tr.json b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/tr.json similarity index 100% rename from templates/mvc/src/MyCompanyName.MyProjectName.Domain/Localization/MyProjectName/tr.json rename to samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/tr.json diff --git a/templates/mvc/src/MyCompanyName.MyProjectName.Domain/Localization/MyProjectName/zh-Hans.json b/samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/zh-Hans.json similarity index 100% rename from templates/mvc/src/MyCompanyName.MyProjectName.Domain/Localization/MyProjectName/zh-Hans.json rename to samples/DashboardDemo/src/DashboardDemo.Domain/Localization/DashboardDemo/zh-Hans.json diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/Settings/DashboardDemoSettingDefinitionProvider.cs b/samples/DashboardDemo/src/DashboardDemo.Domain/Settings/DashboardDemoSettingDefinitionProvider.cs new file mode 100644 index 0000000000..8ba9abccaa --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/Settings/DashboardDemoSettingDefinitionProvider.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Settings; + +namespace DashboardDemo.Settings +{ + public class DashboardDemoSettingDefinitionProvider : SettingDefinitionProvider + { + public override void Define(ISettingDefinitionContext context) + { + //Define your own settings here. Example: + //context.Add(new SettingDefinition(DashboardDemoSettings.MySetting1)); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/Settings/DashboardDemoSettings.cs b/samples/DashboardDemo/src/DashboardDemo.Domain/Settings/DashboardDemoSettings.cs new file mode 100644 index 0000000000..eaffcc35c6 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/Settings/DashboardDemoSettings.cs @@ -0,0 +1,10 @@ +namespace DashboardDemo.Settings +{ + public static class DashboardDemoSettings + { + private const string Prefix = "DashboardDemo"; + + //Add your own setting names here. Example: + //public const string MySetting1 = Prefix + ".MySetting1"; + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Domain/Users/AppUser.cs b/samples/DashboardDemo/src/DashboardDemo.Domain/Users/AppUser.cs new file mode 100644 index 0000000000..4706cd08e9 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Domain/Users/AppUser.cs @@ -0,0 +1,53 @@ +using System; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.Users; + +namespace DashboardDemo.Users +{ + /* This entity shares the same table/collection ("AbpUsers" by default) with the + * IdentityUser entity of the Identity module. + * + * - You can define your custom properties into this class. + * - You never create or delete this entity, becase it is Identity module's job. + * - You can query users from database with this entity. + * - You can update values of your custom properties. + */ + public class AppUser : FullAuditedAggregateRoot, IUser + { + #region Base properties + + /* These properties are shared with the IdentityUser entity of the Identity module. + * Do not change these properties through this class. Instead, use Identity module + * services (like IdentityUserManager) to change them. + * So, this properties are designed as read only! + */ + + public virtual Guid? TenantId { get; private set; } + + public virtual string UserName { get; private set; } + + public virtual string Name { get; private set; } + + public virtual string Surname { get; private set; } + + public virtual string Email { get; private set; } + + public virtual bool EmailConfirmed { get; private set; } + + public virtual string PhoneNumber { get; private set; } + + public virtual bool PhoneNumberConfirmed { get; private set; } + + #endregion + + /* Add your own properties here. Example: + * + * public virtual string MyProperty { get; set; } + */ + + private AppUser() + { + + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/DashboardDemo.EntityFrameworkCore.DbMigrations.csproj b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/DashboardDemo.EntityFrameworkCore.DbMigrations.csproj new file mode 100644 index 0000000000..8b1fe3f5ee --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/DashboardDemo.EntityFrameworkCore.DbMigrations.csproj @@ -0,0 +1,18 @@ + + + + + + netcoreapp2.2 + DashboardDemo + + + + + + + + + + + diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/DashboardDemoMigrationsDbContext.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/DashboardDemoMigrationsDbContext.cs new file mode 100644 index 0000000000..03ed1a2d3d --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/DashboardDemoMigrationsDbContext.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.BackgroundJobs.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Identity; +using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace DashboardDemo.EntityFrameworkCore +{ + public class DashboardDemoMigrationsDbContext : AbpDbContext + { + public DashboardDemoMigrationsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + /* Include modules to your migration db context */ + + builder.ConfigurePermissionManagement(); + builder.ConfigureSettingManagement(); + builder.ConfigureBackgroundJobs(); + builder.ConfigureAuditLogging(); + builder.ConfigureIdentity(); + builder.ConfigureTenantManagement(); + + /* Configure customizations for entities from the modules included */ + + builder.Entity(b => + { + b.ConfigureCustomUserProperties(); + }); + + /* Configure your own tables/entities inside the ConfigureDashboardDemo method */ + + builder.ConfigureDashboardDemo(); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/DashboardDemoMigrationsDbContextFactory.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/DashboardDemoMigrationsDbContextFactory.cs new file mode 100644 index 0000000000..b894e405bc --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/EntityFrameworkCore/DashboardDemoMigrationsDbContextFactory.cs @@ -0,0 +1,29 @@ +using System.IO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace DashboardDemo.EntityFrameworkCore +{ + public class DashboardDemoMigrationsDbContextFactory : IDesignTimeDbContextFactory + { + public DashboardDemoMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); + + return new DashboardDemoMigrationsDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs new file mode 100644 index 0000000000..8c4848b563 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.Designer.cs @@ -0,0 +1,760 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using DashboardDemo.EntityFrameworkCore; + +namespace DashboardDemo.Migrations +{ + [DbContext(typeof(DashboardDemoMigrationsDbContext))] + [Migration("20190402131334_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TenantName"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTenantId"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128); + + b.Property("LastTryTime"); + + b.Property("NextTryTime"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .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") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs new file mode 100644 index 0000000000..f6b5519820 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/20190402131334_Initial.cs @@ -0,0 +1,546 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace DashboardDemo.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpAuditLogs", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + ApplicationName = table.Column(maxLength: 96, nullable: true), + UserId = table.Column(nullable: true), + UserName = table.Column(maxLength: 256, nullable: true), + TenantId = table.Column(nullable: true), + TenantName = table.Column(nullable: true), + ImpersonatorUserId = table.Column(nullable: true), + ImpersonatorTenantId = table.Column(nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ClientIpAddress = table.Column(maxLength: 64, nullable: true), + ClientName = table.Column(maxLength: 128, nullable: true), + ClientId = table.Column(maxLength: 64, nullable: true), + CorrelationId = table.Column(maxLength: 64, nullable: true), + BrowserInfo = table.Column(maxLength: 512, nullable: true), + HttpMethod = table.Column(maxLength: 16, nullable: true), + Url = table.Column(maxLength: 256, nullable: true), + Exceptions = table.Column(maxLength: 4000, nullable: true), + Comments = table.Column(maxLength: 256, nullable: true), + HttpStatusCode = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpBackgroundJobs", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + JobName = table.Column(maxLength: 128, nullable: false), + JobArgs = table.Column(maxLength: 1048576, nullable: false), + TryCount = table.Column(nullable: false, defaultValue: (short)0), + CreationTime = table.Column(nullable: false), + NextTryTime = table.Column(nullable: false), + LastTryTime = table.Column(nullable: true), + IsAbandoned = table.Column(nullable: false, defaultValue: false), + Priority = table.Column(nullable: false, defaultValue: (byte)15) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpBackgroundJobs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpClaimTypes", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + Name = table.Column(maxLength: 256, nullable: false), + Required = table.Column(nullable: false), + IsStatic = table.Column(nullable: false), + Regex = table.Column(maxLength: 512, nullable: true), + RegexDescription = table.Column(maxLength: 128, nullable: true), + Description = table.Column(maxLength: 256, nullable: true), + ValueType = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpPermissionGrants", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 128, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: false), + ProviderKey = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpRoles", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 256, nullable: false), + NormalizedName = table.Column(maxLength: 256, nullable: false), + IsDefault = table.Column(nullable: false), + IsStatic = table.Column(nullable: false), + IsPublic = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpSettings", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(maxLength: 2048, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: true), + ProviderKey = table.Column(maxLength: 64, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpSettings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpTenants", + 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), + Name = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpUsers", + 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), + UserName = table.Column(maxLength: 256, nullable: false), + NormalizedUserName = table.Column(maxLength: 256, nullable: false), + Name = table.Column(maxLength: 64, nullable: true), + Surname = table.Column(maxLength: 64, nullable: true), + Email = table.Column(maxLength: 256, nullable: true), + NormalizedEmail = table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false, defaultValue: false), + PasswordHash = table.Column(maxLength: 256, nullable: true), + SecurityStamp = table.Column(maxLength: 256, nullable: false), + PhoneNumber = table.Column(maxLength: 16, nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false, defaultValue: false), + TwoFactorEnabled = table.Column(nullable: false, defaultValue: false), + LockoutEnd = table.Column(nullable: true), + LockoutEnabled = table.Column(nullable: false, defaultValue: false), + AccessFailedCount = table.Column(nullable: false, defaultValue: 0) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpAuditLogActions", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + AuditLogId = table.Column(nullable: false), + ServiceName = table.Column(maxLength: 256, nullable: true), + MethodName = table.Column(maxLength: 128, nullable: true), + Parameters = table.Column(maxLength: 2000, nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogActions", x => x.Id); + table.ForeignKey( + name: "FK_AbpAuditLogActions_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityChanges", + columns: table => new + { + Id = table.Column(nullable: false), + AuditLogId = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ChangeTime = table.Column(nullable: false), + ChangeType = table.Column(nullable: false), + EntityTenantId = table.Column(nullable: true), + EntityId = table.Column(maxLength: 128, nullable: false), + EntityTypeFullName = table.Column(maxLength: 128, nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityChanges_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ClaimType = table.Column(maxLength: 256, nullable: false), + ClaimValue = table.Column(maxLength: 1024, nullable: true), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpRoleClaims_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpTenantConnectionStrings", + columns: table => new + { + TenantId = table.Column(nullable: false), + Name = table.Column(maxLength: 64, nullable: false), + Value = table.Column(maxLength: 1024, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name }); + table.ForeignKey( + name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId", + column: x => x.TenantId, + principalTable: "AbpTenants", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserClaims", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ClaimType = table.Column(maxLength: 256, nullable: false), + ClaimValue = table.Column(maxLength: 1024, nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpUserClaims_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserLogins", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 64, nullable: false), + TenantId = table.Column(nullable: true), + ProviderKey = table.Column(maxLength: 196, nullable: false), + ProviderDisplayName = table.Column(maxLength: 128, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserLogins", x => new { x.UserId, x.LoginProvider }); + table.ForeignKey( + name: "FK_AbpUserLogins_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false), + TenantId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 64, nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + TenantId = table.Column(nullable: true), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AbpUserTokens_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityPropertyChanges", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + EntityChangeId = table.Column(nullable: false), + NewValue = table.Column(maxLength: 512, nullable: true), + OriginalValue = table.Column(maxLength: 512, nullable: true), + PropertyName = table.Column(maxLength: 128, nullable: false), + PropertyTypeFullName = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityPropertyChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityPropertyChanges_AbpEntityChanges_EntityChangeId", + column: x => x.EntityChangeId, + principalTable: "AbpEntityChanges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_AuditLogId", + table: "AbpAuditLogActions", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_TenantId_ServiceName_MethodName_ExecutionTime", + table: "AbpAuditLogActions", + columns: new[] { "TenantId", "ServiceName", "MethodName", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_UserId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "UserId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpBackgroundJobs_IsAbandoned_NextTryTime", + table: "AbpBackgroundJobs", + columns: new[] { "IsAbandoned", "NextTryTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_AuditLogId", + table: "AbpEntityChanges", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_TenantId_EntityTypeFullName_EntityId", + table: "AbpEntityChanges", + columns: new[] { "TenantId", "EntityTypeFullName", "EntityId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityPropertyChanges_EntityChangeId", + table: "AbpEntityPropertyChanges", + column: "EntityChangeId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoleClaims_RoleId", + table: "AbpRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoles_NormalizedName", + table: "AbpRoles", + column: "NormalizedName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpTenants_Name", + table: "AbpTenants", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserClaims_UserId", + table: "AbpUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserLogins_LoginProvider_ProviderKey", + table: "AbpUserLogins", + columns: new[] { "LoginProvider", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserRoles_RoleId_UserId", + table: "AbpUserRoles", + columns: new[] { "RoleId", "UserId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_Email", + table: "AbpUsers", + column: "Email"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedEmail", + table: "AbpUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedUserName", + table: "AbpUsers", + column: "NormalizedUserName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_UserName", + table: "AbpUsers", + column: "UserName"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpAuditLogActions"); + + migrationBuilder.DropTable( + name: "AbpBackgroundJobs"); + + migrationBuilder.DropTable( + name: "AbpClaimTypes"); + + migrationBuilder.DropTable( + name: "AbpEntityPropertyChanges"); + + migrationBuilder.DropTable( + name: "AbpPermissionGrants"); + + migrationBuilder.DropTable( + name: "AbpRoleClaims"); + + migrationBuilder.DropTable( + name: "AbpSettings"); + + migrationBuilder.DropTable( + name: "AbpTenantConnectionStrings"); + + migrationBuilder.DropTable( + name: "AbpUserClaims"); + + migrationBuilder.DropTable( + name: "AbpUserLogins"); + + migrationBuilder.DropTable( + name: "AbpUserRoles"); + + migrationBuilder.DropTable( + name: "AbpUserTokens"); + + migrationBuilder.DropTable( + name: "AbpEntityChanges"); + + migrationBuilder.DropTable( + name: "AbpTenants"); + + migrationBuilder.DropTable( + name: "AbpRoles"); + + migrationBuilder.DropTable( + name: "AbpUsers"); + + migrationBuilder.DropTable( + name: "AbpAuditLogs"); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/DashboardDemoMigrationsDbContextModelSnapshot.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/DashboardDemoMigrationsDbContextModelSnapshot.cs new file mode 100644 index 0000000000..a178562944 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore.DbMigrations/Migrations/DashboardDemoMigrationsDbContextModelSnapshot.cs @@ -0,0 +1,758 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using DashboardDemo.EntityFrameworkCore; + +namespace DashboardDemo.Migrations +{ + [DbContext(typeof(DashboardDemoMigrationsDbContext))] + partial class DashboardDemoMigrationsDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TenantName"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTenantId"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128); + + b.Property("LastTryTime"); + + b.Property("NextTryTime"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .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") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/DashboardDemo.EntityFrameworkCore.csproj b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/DashboardDemo.EntityFrameworkCore.csproj new file mode 100644 index 0000000000..8d992ddba2 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/DashboardDemo.EntityFrameworkCore.csproj @@ -0,0 +1,22 @@ + + + + + + netcoreapp2.2 + DashboardDemo + + + + + + + + + + + + + + + diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoDbContext.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoDbContext.cs new file mode 100644 index 0000000000..ad4ef4af33 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoDbContext.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; +using DashboardDemo.Users; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.Modeling; +using Volo.Abp.Users.EntityFrameworkCore; + +namespace DashboardDemo.EntityFrameworkCore +{ + [ConnectionStringName("Default")] + public class DashboardDemoDbContext : AbpDbContext + { + public DbSet Users { get; set; } + + public DashboardDemoDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + /* Configure the shared tables (with included modules) here */ + + builder.Entity(b => + { + b.ToTable("AbpUsers"); //Sharing the same table "AbpUsers" with the IdentityUser + + b.ConfigureFullAudited(); + b.ConfigureExtraProperties(); + b.ConfigureConcurrencyStamp(); + b.ConfigureAbpUser(); + + //Moved customization to a method so we can share it with the DashboardDemoMigrationsDbContext class + b.ConfigureCustomUserProperties(); + }); + + /* Configure your own tables/entities inside the ConfigureDashboardDemo method */ + + builder.ConfigureDashboardDemo(); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoDbContextModelCreatingExtensions.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoDbContextModelCreatingExtensions.cs new file mode 100644 index 0000000000..60dae7c4cf --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoDbContextModelCreatingExtensions.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Volo.Abp; +using Volo.Abp.Users; + +namespace DashboardDemo.EntityFrameworkCore +{ + public static class DashboardDemoDbContextModelCreatingExtensions + { + public static void ConfigureDashboardDemo(this ModelBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + /* Configure your own tables/entities inside here */ + + //builder.Entity(b => + //{ + // b.ToTable(DashboardDemoConsts.DbTablePrefix + "YourEntities", DashboardDemoConsts.DbSchema); + + // //... + //}); + } + + public static void ConfigureCustomUserProperties(this EntityTypeBuilder b) + where TUser: class, IUser + { + //b.Property(nameof(AppUser.MyProperty))... + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoEntityFrameworkCoreModule.cs b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoEntityFrameworkCoreModule.cs new file mode 100644 index 0000000000..cd7e6f4af8 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.EntityFrameworkCore/EntityFrameworkCore/DashboardDemoEntityFrameworkCoreModule.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.BackgroundJobs.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.SqlServer; +using Volo.Abp.FeatureManagement.EntityFrameworkCore; +using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace DashboardDemo.EntityFrameworkCore +{ + [DependsOn( + typeof(DashboardDemoDomainModule), + typeof(AbpIdentityEntityFrameworkCoreModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpEntityFrameworkCoreSqlServerModule), + typeof(BackgroundJobsEntityFrameworkCoreModule), + typeof(AbpAuditLoggingEntityFrameworkCoreModule), + typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpFeatureManagementEntityFrameworkCoreModule) + )] + public class DashboardDemoEntityFrameworkCoreModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAbpDbContext(options => + { + //Remove "includeAllEntities: true" to create default repositories only for aggregate roots + options.AddDefaultRepositories(includeAllEntities: true); + }); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Branding/DashboardDemoBrandingProvider.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Branding/DashboardDemoBrandingProvider.cs new file mode 100644 index 0000000000..78b9c57452 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Branding/DashboardDemoBrandingProvider.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Components; +using Volo.Abp.DependencyInjection; + +namespace DashboardDemo.Branding +{ + [Dependency(ReplaceServices = true)] + public class DashboardDemoBrandingProvider : DefaultBrandingProvider + { + public override string AppName => "DashboardDemo"; + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemo.Web.csproj b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemo.Web.csproj new file mode 100644 index 0000000000..550630fe7e --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemo.Web.csproj @@ -0,0 +1,42 @@ + + + + + + netcoreapp2.2 + DashboardDemo + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + true + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemoWebAutoMapperProfile.cs b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemoWebAutoMapperProfile.cs new file mode 100644 index 0000000000..1d8ed11487 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemoWebAutoMapperProfile.cs @@ -0,0 +1,12 @@ +using AutoMapper; + +namespace DashboardDemo +{ + public class DashboardDemoWebAutoMapperProfile : Profile + { + public DashboardDemoWebAutoMapperProfile() + { + //Configure your AutoMapper mapping configuration here... + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemoWebModule.cs b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemoWebModule.cs new file mode 100644 index 0000000000..c8d7bcc18b --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/DashboardDemoWebModule.cs @@ -0,0 +1,247 @@ +using System.IO; +using System.Linq; +using DashboardDemo.Dashboards; +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using DashboardDemo.EntityFrameworkCore; +using DashboardDemo.Localization.DashboardDemo; +using DashboardDemo.Menus; +using DashboardDemo.Pages; +using DashboardDemo.Pages.widgets; +using DashboardDemo.Permissions; +using DashboardDemo.Widgets; +using Swashbuckle.AspNetCore.Swagger; +using Volo.Abp; +using Volo.Abp.Account.Web; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.Localization; +using Volo.Abp.AspNetCore.Mvc.UI; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; +using Volo.Abp.AspNetCore.Mvc.UI.Dashboards; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Autofac; +using Volo.Abp.AutoMapper; +using Volo.Abp.Data; +using Volo.Abp.Identity; +using Volo.Abp.Identity.Web; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpValidation; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement.Web; +using Volo.Abp.Threading; +using Volo.Abp.UI; +using Volo.Abp.UI.Navigation; +using Volo.Abp.VirtualFileSystem; +using Volo.Abp.PermissionManagement; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.TenantManagement.Web; + +namespace DashboardDemo +{ + [DependsOn( + typeof(DashboardDemoApplicationModule), + typeof(DashboardDemoEntityFrameworkCoreModule), + typeof(AbpAutofacModule), + typeof(AbpIdentityWebModule), + typeof(AbpAccountWebModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(AbpAspNetCoreMvcUiDashboardsModule), + typeof(AbpTenantManagementWebModule) + )] + public class DashboardDemoWebModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.PreConfigure(options => + { + options.AddAssemblyResource( + typeof(DashboardDemoResource), + typeof(DashboardDemoDomainModule).Assembly, + typeof(DashboardDemoApplicationModule).Assembly, + typeof(DashboardDemoWebModule).Assembly + ); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.GetConfiguration(); + + ConfigureWidgets(); + ConfigureDashboards(); + ConfigureDatabaseServices(); + ConfigureAutoMapper(); + ConfigureVirtualFileSystem(hostingEnvironment); + ConfigureLocalizationServices(); + ConfigureNavigationServices(); + ConfigureAutoApiControllers(); + ConfigureSwaggerServices(context.Services); + } + + private void ConfigureWidgets() + { + Configure(options => + { + options.Widgets.AddRange(WidgetDefinitionProvider.GetDefinitions()); + }); + } + + private void ConfigureDashboards() + { + Configure(options => + { + options.Dashboards.AddRange(DashboardDefinitionProvider.GetDefinitions()); + }); + + Configure(options => + { + options.ScriptBundles.Add(DashboardNames.MyDashboard, configuration => + { + configuration.AddContributors(typeof(MyDashboardScriptBundleContributor)); + }); + options.ScriptBundles.Add(UserCountWidgetViewComponent.WidgetName, configuration => + { + configuration.AddContributors(typeof(UserCountWidgetScriptBundleContributor)); + }); + options.StyleBundles.Add(DashboardNames.MyDashboard, configuration => + { + configuration.AddContributors(typeof(MyDashboardStyleBundleContributor)); + }); + options.StyleBundles.Add(UserCountWidgetViewComponent.WidgetName, configuration => + { + configuration.AddContributors(typeof(UserCountWidgetStyleBundleContributor)); + }); + }); + } + + private void ConfigureDatabaseServices() + { + Configure(options => + { + options.UseSqlServer(); + }); + } + + private void ConfigureAutoMapper() + { + Configure(options => + { + options.AddProfile(); + }); + } + + private void ConfigureVirtualFileSystem(IHostingEnvironment hostingEnvironment) + { + if (hostingEnvironment.IsDevelopment()) + { + Configure(options => + { + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}DashboardDemo.Domain", Path.DirectorySeparatorChar))); + }); + } + } + + private void ConfigureLocalizationServices() + { + Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpValidationResource), + typeof(AbpUiResource) + ); + + options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština")); + options.Languages.Add(new LanguageInfo("en", "en", "English")); + options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português")); + options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe")); + options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + }); + } + + private void ConfigureNavigationServices() + { + Configure(options => + { + options.MenuContributors.Add(new DashboardDemoMenuContributor()); + }); + } + + private void ConfigureAutoApiControllers() + { + Configure(options => + { + options.ConventionalControllers.Create(typeof(DashboardDemoApplicationModule).Assembly); + }); + } + + private void ConfigureSwaggerServices(IServiceCollection services) + { + services.AddSwaggerGen( + options => + { + options.SwaggerDoc("v1", new Info { Title = "DashboardDemo API", Version = "v1" }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseErrorPage(); + } + + app.UseVirtualFiles(); + app.UseAuthentication(); + + if (DashboardDemoConsts.IsMultiTenancyEnabled) + { + app.UseMultiTenancy(); + } + + app.UseAbpRequestLocalization(); + + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "DashboardDemo API"); + }); + + app.UseAuditing(); + + app.UseMvcWithDefaultRouteAndArea(); + + SeedDatabase(context); + } + + private static void SeedDatabase(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + AsyncHelper.RunSync(async () => + { + await scope.ServiceProvider + .GetRequiredService() + .SeedAsync(); + }); + } + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Dashboards/DashboardDefinitionProvider.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Dashboards/DashboardDefinitionProvider.cs new file mode 100644 index 0000000000..583424c6c3 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Dashboards/DashboardDefinitionProvider.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using DashboardDemo.Localization.DashboardDemo; +using DashboardDemo.Pages.widgets; +using DashboardDemo.Widgets; +using Volo.Abp.AspNetCore.Mvc.UI.Dashboards; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; +using Volo.Abp.Localization; + +namespace DashboardDemo.Dashboards +{ + public static class DashboardDefinitionProvider + { + public static List GetDefinitions() + { + var myDashboard = new DashboardDefinition( + DashboardNames.MyDashboard, + LocalizableString.Create("MyDashboard") + ) + .WithWidget(RoleListWidgetViewComponent.WidgetName) + .WithWidget(UserCountWidgetViewComponent.WidgetName); + + return new List + { + myDashboard + }; + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Dashboards/DashboardNames.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Dashboards/DashboardNames.cs new file mode 100644 index 0000000000..c58f4e60d1 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Dashboards/DashboardNames.cs @@ -0,0 +1,14 @@ +using Volo.Abp.Reflection; + +namespace DashboardDemo.Dashboards +{ + public static class DashboardNames + { + public const string MyDashboard = "MyDashboard"; + + public static string[] GetAll() + { + return ReflectionHelper.GetPublicConstantsRecursively(typeof(DashboardNames)); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Menus/DashboardDemoMenuContributor.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Menus/DashboardDemoMenuContributor.cs new file mode 100644 index 0000000000..d04a04a125 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Menus/DashboardDemoMenuContributor.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using DashboardDemo.Localization.DashboardDemo; +using Volo.Abp.TenantManagement.Web.Navigation; +using Volo.Abp.UI.Navigation; + +namespace DashboardDemo.Menus +{ + public class DashboardDemoMenuContributor : IMenuContributor + { + public async Task ConfigureMenuAsync(MenuConfigurationContext context) + { + if (context.Menu.Name == StandardMenus.Main) + { + await ConfigureMainMenuAsync(context); + } + } + + private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) + { + if (!DashboardDemoConsts.IsMultiTenancyEnabled) + { + ApplicationMenuItem administration = context.Menu.GetAdministration(); + administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName); + } + + var l = context.ServiceProvider.GetRequiredService>(); + + context.Menu.Items.Insert(0, new ApplicationMenuItem("DashboardDemo.Home", l["Menu:Home"], "/")); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/DashboardDemoPageBase.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/DashboardDemoPageBase.cs new file mode 100644 index 0000000000..a31d4762f9 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/DashboardDemoPageBase.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc.Localization; +using Microsoft.AspNetCore.Mvc.Razor.Internal; +using DashboardDemo.Localization.DashboardDemo; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace DashboardDemo.Pages +{ + public abstract class DashboardDemoPageBase : AbpPage + { + [RazorInject] + public IHtmlLocalizer L { get; set; } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/DashboardDemoPageModelBase.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/DashboardDemoPageModelBase.cs new file mode 100644 index 0000000000..5a81bfeb8b --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/DashboardDemoPageModelBase.cs @@ -0,0 +1,13 @@ +using DashboardDemo.Localization.DashboardDemo; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace DashboardDemo.Pages +{ + public abstract class DashboardDemoPageModelBase : AbpPageModel + { + protected DashboardDemoPageModelBase() + { + LocalizationResourceType = typeof(DashboardDemoResource); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/Index.cshtml b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/Index.cshtml new file mode 100644 index 0000000000..e4aa8c6063 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/Index.cshtml @@ -0,0 +1,30 @@ +@page +@inject IOptions OptionsDashboadOptions +@inject IStringLocalizerFactory localizer +@using Microsoft.Extensions.Options +@using Volo.Abp.AspNetCore.Mvc.UI.Dashboards +@using DashboardDemo.Pages +@using DashboardDemo.Pages.widgets +@using DashboardDemo.Widgets +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Widgets +@inherits DashboardDemoPageBase +@inject IWidgetRenderer WidgetRenderer +@model IndexModel +@section styles { + + +} +@section scripts { + +} + + @L["Welcome"] + + @await WidgetRenderer.RenderAsync(Component, UserCountWidgetViewComponent.WidgetName) + + +@foreach (var dashboard in OptionsDashboadOptions.Value.Dashboards) +{ + Go to @dashboard.DisplayName.Localize(localizer) +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/Index.cshtml.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/Index.cshtml.cs new file mode 100644 index 0000000000..c8f5717baa --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/Index.cshtml.cs @@ -0,0 +1,10 @@ +namespace DashboardDemo.Pages +{ + public class IndexModel : DashboardDemoPageModelBase + { + public void OnGet() + { + + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboard.cshtml b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboard.cshtml new file mode 100644 index 0000000000..9bfa30bafe --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboard.cshtml @@ -0,0 +1,19 @@ +@page +@using DashboardDemo.Dashboards +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Dashboards +@using Volo.Abp.AspNetCore.Mvc.UI.Widgets +@inject IWidgetRenderer WidgetRenderer +@inject IDashboardRenderer DashboardRenderer +@inject IStringLocalizerFactory localizer +@model DashboardDemo.Pages.MyDashboardModel +@{ +} +@section styles { + +} +@section scripts { + +} + +@await DashboardRenderer.RenderAsync(Component, new { dashboardName = DashboardNames.MyDashboard }) \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboard.cshtml.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboard.cshtml.cs new file mode 100644 index 0000000000..14d582808e --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboard.cshtml.cs @@ -0,0 +1,32 @@ +using System.Linq; +using DashboardDemo.Dashboards; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.UI.Dashboards; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace DashboardDemo.Pages +{ + public class MyDashboardModel : DashboardDemoPageModelBase + { + public DashboardDefinition Dashboard { get; set; } + + public readonly DashboardOptions _dashboardOptions; + public readonly WidgetOptions _widgetOptions; + + public MyDashboardModel(IOptions dashboardOptions, IOptions widgetOptions) + { + _dashboardOptions = dashboardOptions.Value; + _widgetOptions = widgetOptions.Value; + } + + public void OnGet() + { + Dashboard = _dashboardOptions.Dashboards.Single(d => d.Name.Equals(DashboardNames.MyDashboard)); + } + + public WidgetDefinition GetWidget(string name) + { + return _widgetOptions.Widgets.Single(d => d.Name.Equals(name)); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboardScriptBundleContributor.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboardScriptBundleContributor.cs new file mode 100644 index 0000000000..6b188a7cd5 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboardScriptBundleContributor.cs @@ -0,0 +1,20 @@ +using DashboardDemo.Pages.widgets; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Dashboards; +using Volo.Abp.Modularity; + +namespace DashboardDemo.Pages +{ + [DependsOn( + typeof(AbpBasicDashboardScriptContributor), + typeof(UserCountWidgetScriptBundleContributor), + typeof(RoleListWidgetScriptContributor) + )] + public class MyDashboardScriptBundleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboardStyleBundleContributor.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboardStyleBundleContributor.cs new file mode 100644 index 0000000000..5ef18e0b25 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/MyDashboardStyleBundleContributor.cs @@ -0,0 +1,20 @@ +using DashboardDemo.Pages.widgets; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Dashboards; +using Volo.Abp.Modularity; + +namespace DashboardDemo.Pages +{ + [DependsOn( + typeof(AbpBasicDashboardStyleContributor), + typeof(UserCountWidgetStyleBundleContributor), + typeof(RoleListWidgetStyleContributor) + )] + public class MyDashboardStyleBundleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + + } + } +} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/_ViewImports.cshtml b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/_ViewImports.cshtml similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/_ViewImports.cshtml rename to samples/DashboardDemo/src/DashboardDemo.Web/Pages/_ViewImports.cshtml diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidget.css b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidget.css new file mode 100644 index 0000000000..587c5f117b --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidget.css @@ -0,0 +1,3 @@ +body { + margin: 0; +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidget.js b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidget.js new file mode 100644 index 0000000000..fdc0587f3d --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidget.js @@ -0,0 +1,15 @@ +(function ($) { + var $container = $('#RoleListWidgetContainer'); + if ($container.length > 0) { + var _identityUserAppService = volo.abp.identity.identityRole; + + _identityUserAppService.getList({}).then(function (result) { + var html = ''; + for (var i = 0; i < result.items.length; i++) { + html += '
  • ' + result.items[i].name + '
  • '; + } + + $container.find('#RoleList').html(html); + }); + } +})(jQuery); diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetScriptContributor.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetScriptContributor.cs new file mode 100644 index 0000000000..20fc9dc06f --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetScriptContributor.cs @@ -0,0 +1,15 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.Clipboard; +using Volo.Abp.Modularity; + +namespace DashboardDemo.Pages.widgets +{ + [DependsOn(typeof(ClipboardScriptBundleContributor))] + public class RoleListWidgetScriptContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.Add("/Pages/widgets/RoleListWidget.js"); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetStyleContributor.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetStyleContributor.cs new file mode 100644 index 0000000000..07bb566a71 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetStyleContributor.cs @@ -0,0 +1,12 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; + +namespace DashboardDemo.Pages.widgets +{ + public class RoleListWidgetStyleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.Add("/Pages/widgets/RoleListWidget.css"); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetViewComponent.cshtml b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetViewComponent.cshtml new file mode 100644 index 0000000000..84e7190b6c --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetViewComponent.cshtml @@ -0,0 +1,17 @@ +@inject IHtmlLocalizer L +@using DashboardDemo.Localization.DashboardDemo +@using Microsoft.AspNetCore.Mvc.Localization +@model DashboardDemo.Pages.widgets.RoleListWidgetViewComponent +@{ +} +
    + + @L["RolesInApplication"] : + +
      + +
    +
    +
    +
    + diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetViewComponent.cshtml.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetViewComponent.cshtml.cs new file mode 100644 index 0000000000..d837424239 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/RoleListWidgetViewComponent.cshtml.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc; + +namespace DashboardDemo.Pages.widgets +{ + [ViewComponent] + public class RoleListWidgetViewComponent : ViewComponent + { + public const string WidgetName = "RoleListWidget"; + + public const string DisplayName = "RoleListWidgets"; + + public IViewComponentResult Invoke() + { + return View("/Pages/widgets/RoleListWidgetViewComponent.cshtml", new RoleListWidgetViewComponent()); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidget.css b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidget.css new file mode 100644 index 0000000000..587c5f117b --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidget.css @@ -0,0 +1,3 @@ +body { + margin: 0; +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidget.js b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidget.js new file mode 100644 index 0000000000..02e5dddcec --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidget.js @@ -0,0 +1,10 @@ +(function ($) { + + var $container = $('#UserCountWidgetContainer'); + if ($container.length > 0) { + var _identityUserAppService = volo.abp.identity.identityUser; + _identityUserAppService.getList({}).then(function(result) { + $container.find('#UserCount').text(result.items.length); + }); + } +})(jQuery); diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetScriptBundleContributor.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetScriptBundleContributor.cs new file mode 100644 index 0000000000..7a84d59c6a --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetScriptBundleContributor.cs @@ -0,0 +1,17 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.Clipboard; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; +using Volo.Abp.Modularity; + +namespace DashboardDemo.Pages.widgets +{ + [DependsOn(typeof(JQueryScriptContributor))] + [DependsOn(typeof(ClipboardScriptBundleContributor))] + public class UserCountWidgetScriptBundleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.Add("/Pages/widgets/UserCountWidget.js"); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetStyleBundleContributor.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetStyleBundleContributor.cs new file mode 100644 index 0000000000..c11ab7f7d1 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetStyleBundleContributor.cs @@ -0,0 +1,17 @@ +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap; +using Volo.Abp.AspNetCore.Mvc.UI.Packages.HighlightJs; +using Volo.Abp.Modularity; + +namespace DashboardDemo.Pages.widgets +{ + [DependsOn(typeof(BootstrapStyleContributor))] + [DependsOn(typeof(HighlightJsStyleContributor))] + public class UserCountWidgetStyleBundleContributor : BundleContributor + { + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.Add("/Pages/widgets/UserCountWidget.css"); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetViewComponent.cshtml b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetViewComponent.cshtml new file mode 100644 index 0000000000..f512805845 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetViewComponent.cshtml @@ -0,0 +1,14 @@ +@inject IHtmlLocalizer L +@using DashboardDemo.Localization.DashboardDemo +@using Microsoft.AspNetCore.Mvc.Localization +@model DashboardDemo.Pages.widgets.UserCountWidgetViewComponent +@{ +} +
    + + @L["UserCount"] : + +

    +
    +
    +
    diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetViewComponent.cshtml.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetViewComponent.cshtml.cs new file mode 100644 index 0000000000..9a4d9a73aa --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Pages/widgets/UserCountWidgetViewComponent.cshtml.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace DashboardDemo.Pages.widgets +{ + public class UserCountWidgetViewComponent : AbpViewComponent + { + public const string WidgetName = "UserCountWidget"; + + public const string DisplayName = "UserCountWidgett"; + + public IViewComponentResult Invoke() + { + return View("/Pages/widgets/UserCountWidgetViewComponent.cshtml", new UserCountWidgetViewComponent()); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Program.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Program.cs new file mode 100644 index 0000000000..8a6c828ce1 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Program.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using Microsoft.AspNetCore.Hosting; +using Serilog; +using Serilog.Events; + +namespace DashboardDemo +{ + public class Program + { + public static int Main(string[] args) + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() //TODO: Should be configurable! + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.File("Logs/logs.txt") + .CreateLogger(); + + try + { + Log.Information("Starting web host."); + BuildWebHostInternal(args).Run(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IWebHost BuildWebHostInternal(string[] args) => + new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .UseSerilog() + .Build(); + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Properties/launchSettings.json b/samples/DashboardDemo/src/DashboardDemo.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..05ae81bcc3 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:53929/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "DashboardDemo.Web": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:53932/" + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Startup.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Startup.cs new file mode 100644 index 0000000000..8b8e9f4d7d --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Startup.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Volo.Abp; + +namespace DashboardDemo +{ + public class Startup + { + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddApplication(options => + { + options.UseAutofac(); + }); + + return services.BuildServiceProviderFromFactory(); + } + + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + app.InitializeApplication(); + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Views/_ViewImports.cshtml b/samples/DashboardDemo/src/DashboardDemo.Web/Views/_ViewImports.cshtml new file mode 100644 index 0000000000..e4d518b1b8 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Views/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/Widgets/WidgetDefinitionProvider.cs b/samples/DashboardDemo/src/DashboardDemo.Web/Widgets/WidgetDefinitionProvider.cs new file mode 100644 index 0000000000..2df0bfdf99 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/Widgets/WidgetDefinitionProvider.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using DashboardDemo.Localization.DashboardDemo; +using DashboardDemo.Pages.widgets; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; +using Volo.Abp.Identity; +using Volo.Abp.Localization; + +namespace DashboardDemo.Widgets +{ + public static class WidgetDefinitionProvider + { + public static List GetDefinitions() + { + var userCountWidget = new WidgetDefinition( + UserCountWidgetViewComponent.WidgetName, + LocalizableString.Create(UserCountWidgetViewComponent.DisplayName), + typeof(UserCountWidgetViewComponent) + ) + .SetDefaultDimension(6, 3) + .AddRequiredPermission(IdentityPermissions.Users.Default); + + var roleListWidget = new WidgetDefinition( + RoleListWidgetViewComponent.WidgetName, + LocalizableString.Create(RoleListWidgetViewComponent.DisplayName), + typeof(RoleListWidgetViewComponent) + ) + .SetDefaultDimension(6, 3) + .AddRequiredPermission(IdentityPermissions.Roles.Default); + + return new List + { + userCountWidget, + roleListWidget + }; + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/abp.resourcemapping.js b/samples/DashboardDemo/src/DashboardDemo.Web/abp.resourcemapping.js new file mode 100644 index 0000000000..122e8e926a --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/abp.resourcemapping.js @@ -0,0 +1,8 @@ +module.exports = { + aliases: { + + }, + mappings: { + + } +}; diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/appsettings.json b/samples/DashboardDemo/src/DashboardDemo.Web/appsettings.json new file mode 100644 index 0000000000..b33c687822 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "Default": "Server=localhost;Database=DashboardDemo;Trusted_Connection=True;MultipleActiveResultSets=true" + } +} \ No newline at end of file diff --git a/samples/BookStore/src/Acme.BookStore.Web/compilerconfig.json b/samples/DashboardDemo/src/DashboardDemo.Web/compilerconfig.json similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Web/compilerconfig.json rename to samples/DashboardDemo/src/DashboardDemo.Web/compilerconfig.json diff --git a/samples/BookStore/src/Acme.BookStore.Web/compilerconfig.json.defaults b/samples/DashboardDemo/src/DashboardDemo.Web/compilerconfig.json.defaults similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Web/compilerconfig.json.defaults rename to samples/DashboardDemo/src/DashboardDemo.Web/compilerconfig.json.defaults diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/gulpfile.js b/samples/DashboardDemo/src/DashboardDemo.Web/gulpfile.js new file mode 100644 index 0000000000..db4c6fd03c --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/gulpfile.js @@ -0,0 +1,11 @@ +"use strict"; + +var gulp = require("gulp"), + path = require('path'), + copyResources = require('./node_modules/@abp/aspnetcore.mvc.ui/gulp/copy-resources.js'); + +copyResources.init(path.resolve('./')); + +gulp.task('default', [copyResources.taskName], function () { + +}); \ No newline at end of file diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/package-lock.json b/samples/DashboardDemo/src/DashboardDemo.Web/package-lock.json new file mode 100644 index 0000000000..b24a100c01 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/package-lock.json @@ -0,0 +1,2390 @@ +{ + "name": "DashboardDemo", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-0.4.9.tgz", + "integrity": "sha512-AveMEi6WRQmD1tM9yVNLAe6ffcbtoL3ZGQKTPA95Q+dy5xBXVg4Y8jdhxawNPK64KYArhfrVkQDfa4mq3pW5Qw==", + "requires": { + "ansi-colors": "^1.1.0", + "extend-object": "^1.0.0", + "gulp": "^3.9.1", + "merge-stream": "^1.0.1", + "path": "^0.12.7", + "rimraf": "^2.6.2" + } + }, + "@abp/aspnetcore.mvc.ui.theme.basic": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-0.4.9.tgz", + "integrity": "sha512-g3Zby8x8+vWw3oTJsSPasjm4tv7BG4270PEf7jK9w925+lIyKYHn6UxOcFbSU2pi9mJTnaF+Fk/rScIcdxwZZw==", + "requires": { + "@abp/aspnetcore.mvc.ui.theme.shared": "^0.4.9" + } + }, + "@abp/aspnetcore.mvc.ui.theme.shared": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-0.4.9.tgz", + "integrity": "sha512-Fe4UfQ215Pz2V6D5HUYhDJFeJWjKLcv2gsZfOheWLlOBgYh274pdnUIwceoZ4Btpa9aPECLG+LAH4dTbs8vjRg==", + "requires": { + "@abp/aspnetcore.mvc.ui": "^0.4.9", + "@abp/bootstrap": "^0.4.9", + "@abp/datatables.net-bs4": "^0.4.9", + "@abp/font-awesome": "^0.4.9", + "@abp/jquery-form": "^0.4.9", + "@abp/jquery-validation-unobtrusive": "^0.4.9", + "@abp/lodash": "^0.4.9", + "@abp/select2": "^0.4.9", + "@abp/sweetalert": "^0.4.9", + "@abp/timeago": "^0.4.9", + "@abp/toastr": "^0.4.9" + } + }, + "@abp/bootstrap": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/bootstrap/-/bootstrap-0.4.9.tgz", + "integrity": "sha512-A7lDHo43KnqjINo0xWECx/WzNQe3vqvE3GPv6uXq3KDi+gStQLAFBq3bVcVW/kSxiquDQ/tuCLRpE0qlhoobag==", + "requires": { + "@abp/core": "^0.4.9", + "bootstrap": "^4.1.1" + } + }, + "@abp/core": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/core/-/core-0.4.9.tgz", + "integrity": "sha512-JbibIPDz0w/C9YhexNagMD763qbZCclcHLVlTpKEZUcxzp6rZXwPR6ekh/PnvzD0R7wmHOh+bHl0hs5JSdy3oA==" + }, + "@abp/datatables.net": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/datatables.net/-/datatables.net-0.4.9.tgz", + "integrity": "sha512-URimiGMFBusEMo9aAadLFCossbYyrN2R6+6YrQ+heqxdheCPmm2wupsXSFoUBHBhD0tzF4rY+OL//oOg3Wtg4g==", + "requires": { + "@abp/core": "^0.4.9", + "datatables.net": "^1.10.16" + } + }, + "@abp/datatables.net-bs4": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/datatables.net-bs4/-/datatables.net-bs4-0.4.9.tgz", + "integrity": "sha512-Dsz0fy2haBz3dl2MLXWjW2bhDBzp1cpWXSXj8TEEzYazjhyoqBy6AiFlEnRCqO49KZD6Ps1cONSRxx4c4On9Lg==", + "requires": { + "@abp/datatables.net": "^0.4.9", + "datatables.net-bs4": "^1.10.16" + } + }, + "@abp/font-awesome": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/font-awesome/-/font-awesome-0.4.9.tgz", + "integrity": "sha512-H4H/PJeSypoq++jP7eqS3y5jpbPIglUByPymHnho7U84gO06tZBUIoK00zyLACUVLzNztWANhmVqyA6OaHQ6xQ==", + "requires": { + "@abp/core": "^0.4.9", + "font-awesome": "^4.7.0" + } + }, + "@abp/jquery": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/jquery/-/jquery-0.4.9.tgz", + "integrity": "sha512-Lte2rfDhcVXRA18mJMyPs0oFoiX/7x10F9cwMyX4+8e9QulA0dg+MjJZ4HIbxRoKvaYPz0AdE5VZ511folFeTg==", + "requires": { + "@abp/core": "^0.4.9", + "jquery": "^3.3.1" + } + }, + "@abp/jquery-form": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/jquery-form/-/jquery-form-0.4.9.tgz", + "integrity": "sha512-T1LzHNO+L/ATSfsaSu9H5rq9oUGjjBrwcJBVQhWRuGbB/Nsv1t4CdCPZHqbYBsn3ji/6T9C80FMdL+s4iOfy8A==", + "requires": { + "@abp/jquery": "^0.4.9", + "jquery-form": "^4.2.2" + } + }, + "@abp/jquery-validation": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/jquery-validation/-/jquery-validation-0.4.9.tgz", + "integrity": "sha512-8C58G9BTYszW6E0EO6NYqXLEbABXt/yGlOqAFCkPiLVfUcoC6uARqkgIgNVnE99ZUeSmluNsYeCczP0Qb0yNzg==", + "requires": { + "@abp/jquery": "^0.4.9", + "jquery-validation": "^1.17.0" + } + }, + "@abp/jquery-validation-unobtrusive": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-0.4.9.tgz", + "integrity": "sha512-GR62QEPexX4uLcqF3y72V39nlx/70C7ubeaIxS1ZEq+0wGklwAiyq1p19qpB5Cuj2GJL+vFrIig5w+B79P2iAw==", + "requires": { + "@abp/jquery-validation": "^0.4.9", + "jquery-validation-unobtrusive": "^3.2.9" + } + }, + "@abp/lodash": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/lodash/-/lodash-0.4.9.tgz", + "integrity": "sha512-/itYbXQL145WigKIGpnyWgARml7QGie7xqhC1WZG5iUkdqLx+4YBXesdzGFykthDewiQNz8mB2gPk64d376AAw==", + "requires": { + "@abp/core": "^0.4.9", + "lodash": "^4.17.10" + }, + "dependencies": { + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + } + } + }, + "@abp/select2": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/select2/-/select2-0.4.9.tgz", + "integrity": "sha512-AaJHWy9fkP/LqVkruZGfvzgdZXyT96/FpnflUpoNPXjuvVRptkJpd/BQiWjpyjS+qzuZ+m3X9dms0d0lAyFKYA==", + "requires": { + "@abp/core": "^0.4.9", + "select2": "^4.0.5" + } + }, + "@abp/sweetalert": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/sweetalert/-/sweetalert-0.4.9.tgz", + "integrity": "sha512-FaxtELo7Um/uasasbOGi1j1XuBtZ819mUo7+6pMSWj2CSAkg2A/RyT9NhYcX8oyGpv188W5CjZfJ1DM0bRH+pQ==", + "requires": { + "@abp/core": "^0.4.9", + "sweetalert": "^2.1.0" + } + }, + "@abp/timeago": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/timeago/-/timeago-0.4.9.tgz", + "integrity": "sha512-RsAyJCl+rEWQL0q0Nx/ijy+iOBYm5IC5Re6y4SQ1jYF/B4n88GM2PFy6kLrE+HHGl5Z5hkWr5OGXmFem50Y3Wg==", + "requires": { + "@abp/jquery": "^0.4.9", + "timeago": "^1.6.3" + } + }, + "@abp/toastr": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@abp/toastr/-/toastr-0.4.9.tgz", + "integrity": "sha512-KjnETa1Og5EIMDw/pIxPoKLTABBcsxBN8BqwIG33ya2+BIRfAI7b0lEMoKK+qmwVwI5dkXZlKFGIUmPtb+zVCw==", + "requires": { + "@abp/jquery": "^0.4.9", + "toastr": "^2.1.4" + } + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=" + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=" + }, + "bootstrap": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", + "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "datatables.net": { + "version": "1.10.19", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.19.tgz", + "integrity": "sha512-+ljXcI6Pj3PTGy5pesp3E5Dr3x3AV45EZe0o1r0gKENN2gafBKXodVnk2ypKwl2tTmivjxbkiqoWnipTefyBTA==", + "requires": { + "jquery": ">=1.7" + } + }, + "datatables.net-bs4": { + "version": "1.10.19", + "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.19.tgz", + "integrity": "sha512-pgeP17w4aPR7HIxIwuJghfqXULjdg1K6xMUUKDyCERJRSNNK4MRToFfELtIsluLNN555YBK4Kx8nihX5/ZT1Fw==", + "requires": { + "datatables.net": "1.10.19", + "jquery": ">=1.7" + } + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "requires": { + "clone": "^1.0.2" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "deprecated": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deprecated/-/deprecated-0.0.1.tgz", + "integrity": "sha1-+cmvVGSvoeepcUWKi97yqpTVuxk=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "requires": { + "readable-stream": "~1.1.9" + } + }, + "end-of-stream": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-0.1.5.tgz", + "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", + "requires": { + "once": "~1.3.0" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", + "integrity": "sha1-QlFPhAFdE1bK9Rh5ad+yvBvaCCM=" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-index": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", + "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=" + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "first-chunk-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz", + "integrity": "sha1-Wb+1DNkF9g18OUzT2ayqtOatk04=" + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gaze": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.2.tgz", + "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", + "requires": { + "globule": "~0.1.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "glob": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^2.0.1", + "once": "^1.3.0" + } + }, + "glob-stream": { + "version": "3.1.18", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-3.1.18.tgz", + "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", + "requires": { + "glob": "^4.3.1", + "glob2base": "^0.0.12", + "minimatch": "^2.0.1", + "ordered-read-streams": "^0.1.0", + "through2": "^0.6.1", + "unique-stream": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + } + } + }, + "glob-watcher": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.6.tgz", + "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", + "requires": { + "gaze": "^0.5.1" + } + }, + "glob2base": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", + "requires": { + "find-index": "^0.1.1" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globule": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", + "requires": { + "glob": "~3.1.21", + "lodash": "~1.0.1", + "minimatch": "~0.2.11" + }, + "dependencies": { + "glob": { + "version": "3.1.21", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", + "requires": { + "graceful-fs": "~1.2.0", + "inherits": "1", + "minimatch": "~0.2.11" + } + }, + "graceful-fs": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", + "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=" + }, + "inherits": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", + "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=" + }, + "minimatch": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + } + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "requires": { + "sparkles": "^1.0.0" + } + }, + "graceful-fs": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", + "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", + "requires": { + "natives": "^1.1.0" + } + }, + "gulp": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-3.9.1.tgz", + "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", + "requires": { + "archy": "^1.0.0", + "chalk": "^1.0.0", + "deprecated": "^0.0.1", + "gulp-util": "^3.0.0", + "interpret": "^1.0.0", + "liftoff": "^2.1.0", + "minimist": "^1.1.0", + "orchestrator": "^0.3.0", + "pretty-hrtime": "^1.0.0", + "semver": "^4.1.0", + "tildify": "^1.0.0", + "v8flags": "^2.0.2", + "vinyl-fs": "^0.3.0" + } + }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "requires": { + "array-differ": "^1.0.0", + "array-uniq": "^1.0.2", + "beeper": "^1.0.0", + "chalk": "^1.0.0", + "dateformat": "^2.0.0", + "fancy-log": "^1.1.0", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "lodash._reescape": "^3.0.0", + "lodash._reevaluate": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.template": "^3.0.0", + "minimist": "^1.1.0", + "multipipe": "^0.1.2", + "object-assign": "^3.0.0", + "replace-ext": "0.0.1", + "through2": "^2.0.0", + "vinyl": "^0.5.0" + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "requires": { + "glogg": "^1.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "requires": { + "sparkles": "^1.0.0" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "jquery": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" + }, + "jquery-form": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/jquery-form/-/jquery-form-4.2.2.tgz", + "integrity": "sha512-HJTef7DRBSg8ge/RNUw8rUTTtB3l8ozO0OhD16AzDl+eIXp4skgCqRTd9fYPsOzL+pN6+1B9wvbTLGjgikz8Tg==", + "requires": { + "jquery": ">=1.7.2" + } + }, + "jquery-validation": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/jquery-validation/-/jquery-validation-1.19.0.tgz", + "integrity": "sha512-i1ygcs9K9eI0jqQ8fiOZdT0Ofw941YII1zyPQDUkBS8t2G8SoSWz8UUEVT6gV1KvsaBbovtL8YxjLALACR0syQ==", + "requires": { + "jquery": "^1.7 || ^2.0 || ^3.1" + } + }, + "jquery-validation-unobtrusive": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.2.11.tgz", + "integrity": "sha512-3FQPllaWdD+Aq55zJLGSW39+eXPDz1HhwAvrSwYi8zHQ8DVcu5IJ1HVeTiCl0BnCnrIBvfFU3zEB/DrGdcoRIQ==", + "requires": { + "jquery": ">=1.8", + "jquery-validation": ">=1.16" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "liftoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz", + "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", + "requires": { + "extend": "^3.0.0", + "findup-sync": "^2.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "lodash": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz", + "integrity": "sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE=" + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=" + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=" + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=" + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "requires": { + "lodash._root": "^3.0.0" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "^3.0.0", + "lodash.isarguments": "^3.0.0", + "lodash.isarray": "^3.0.0" + } + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" + }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "requires": { + "lodash._basecopy": "^3.0.0", + "lodash._basetostring": "^3.0.0", + "lodash._basevalues": "^3.0.0", + "lodash._isiterateecall": "^3.0.0", + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0", + "lodash.keys": "^3.0.0", + "lodash.restparam": "^3.0.0", + "lodash.templatesettings": "^3.0.0" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.escape": "^3.0.0" + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "requires": { + "readable-stream": "^2.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "minimatch": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", + "requires": { + "brace-expansion": "^1.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "requires": { + "duplexer2": "0.0.2" + } + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natives": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", + "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==" + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", + "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", + "requires": { + "wrappy": "1" + } + }, + "orchestrator": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/orchestrator/-/orchestrator-0.3.8.tgz", + "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", + "requires": { + "end-of-stream": "~0.1.5", + "sequencify": "~0.0.7", + "stream-consume": "~0.1.0" + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz", + "integrity": "sha1-/VZamvjrRHO6abbtijQ1LLVS8SY=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==" + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "promise-polyfill": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz", + "integrity": "sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=" + }, + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "select2": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/select2/-/select2-4.0.7.tgz", + "integrity": "sha512-t46Z2nkEEYnBYdRpLNw5dj9qqO+YTiTlWKnpJ/B5e1Q3hPND00JRgcLJxYRzqpeLZd+ZkszkHEMagLKIfbTjVA==" + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" + }, + "sequencify": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/sequencify/-/sequencify-0.0.7.tgz", + "integrity": "sha1-kM/xnQLgcCf9dn9erT57ldHnOAw=" + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-consume": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==" + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", + "requires": { + "first-chunk-stream": "^1.0.0", + "is-utf8": "^0.2.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "sweetalert": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/sweetalert/-/sweetalert-2.1.2.tgz", + "integrity": "sha512-iWx7X4anRBNDa/a+AdTmvAzQtkN1+s4j/JJRWlHpYE8Qimkohs8/XnFcWeYHH2lMA8LRCa5tj2d244If3S/hzA==", + "requires": { + "es6-object-assign": "^1.1.0", + "promise-polyfill": "^6.0.2" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "tildify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-1.2.0.tgz", + "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", + "requires": { + "os-homedir": "^1.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=" + }, + "timeago": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/timeago/-/timeago-1.6.7.tgz", + "integrity": "sha512-FikcjN98+ij0siKH4VO4dZ358PR3oDDq4Vdl1+sN9gWz1/+JXGr3uZbUShYH/hL7bMhcTpPbplJU5Tej4b4jbQ==", + "requires": { + "jquery": ">=1.5.0 <4.0" + } + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toastr": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/toastr/-/toastr-2.1.4.tgz", + "integrity": "sha1-i0O+ZPudDEFIcURvLbjoyk6V8YE=", + "requires": { + "jquery": ">=1.12.0" + } + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-1.0.0.tgz", + "integrity": "sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "user-home": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", + "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=" + }, + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "v8flags": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", + "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", + "requires": { + "user-home": "^1.1.1" + } + }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "requires": { + "clone": "^1.0.0", + "clone-stats": "^0.0.1", + "replace-ext": "0.0.1" + } + }, + "vinyl-fs": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-0.3.14.tgz", + "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", + "requires": { + "defaults": "^1.0.0", + "glob-stream": "^3.1.5", + "glob-watcher": "^0.0.6", + "graceful-fs": "^3.0.0", + "mkdirp": "^0.5.0", + "strip-bom": "^1.0.0", + "through2": "^0.6.1", + "vinyl": "^0.4.0" + }, + "dependencies": { + "clone": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz", + "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", + "requires": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "vinyl": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", + "requires": { + "clone": "^0.2.0", + "clone-stats": "^0.0.1" + } + } + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } +} diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/package.json b/samples/DashboardDemo/src/DashboardDemo.Web/package.json new file mode 100644 index 0000000000..517e06b053 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/package.json @@ -0,0 +1,8 @@ +{ + "version": "0.1.0", + "name": "DashboardDemo", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^0.4.9" + } +} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/abp/core/abp.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/abp/core/abp.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/abp/core/abp.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/abp/core/abp.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/abp/jquery/abp.jquery.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/abp/jquery/abp.jquery.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/abp/jquery/abp.jquery.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/abp/jquery/abp.jquery.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/bootstrap/css/bootstrap.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/bootstrap/css/bootstrap.css similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/bootstrap/css/bootstrap.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/bootstrap/css/bootstrap.css diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/bootstrap/js/bootstrap.bundle.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/bootstrap/js/bootstrap.bundle.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/bootstrap/js/bootstrap.bundle.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/bootstrap/js/bootstrap.bundle.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/datatables.net-bs4/css/dataTables.bootstrap4.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/datatables.net-bs4/css/dataTables.bootstrap4.css similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/datatables.net-bs4/css/dataTables.bootstrap4.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/datatables.net-bs4/css/dataTables.bootstrap4.css diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/datatables.net-bs4/js/dataTables.bootstrap4.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/datatables.net-bs4/js/dataTables.bootstrap4.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/datatables.net-bs4/js/dataTables.bootstrap4.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/datatables.net-bs4/js/dataTables.bootstrap4.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/datatables.net/js/jquery.dataTables.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/datatables.net/js/jquery.dataTables.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/datatables.net/js/jquery.dataTables.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/datatables.net/js/jquery.dataTables.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/css/font-awesome.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/css/font-awesome.css similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/css/font-awesome.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/css/font-awesome.css diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/FontAwesome.otf b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/FontAwesome.otf similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/FontAwesome.otf rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/FontAwesome.otf diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.eot b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.eot similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.eot rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.eot diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.svg b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.svg similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.svg rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.svg diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.ttf b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.ttf similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.ttf rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.ttf diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff2 b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff2 similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff2 rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/font-awesome/fonts/fontawesome-webfont.woff2 diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-form/jquery.form.min.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-form/jquery.form.min.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-form/jquery.form.min.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-form/jquery.form.min.js diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js new file mode 100644 index 0000000000..6af3efad07 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js @@ -0,0 +1,431 @@ +// Unobtrusive validation support library for jQuery and jQuery Validate +// Copyright (C) Microsoft Corporation. All rights reserved. +// @version v3.2.9 + +/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */ +/*global document: false, jQuery: false */ + +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define("jquery.validate.unobtrusive", ['jquery.validation'], factory); + } else if (typeof module === 'object' && module.exports) { + // CommonJS-like environments that support module.exports + module.exports = factory(require('jquery-validation')); + } else { + // Browser global + jQuery.validator.unobtrusive = factory(jQuery); + } +}(function ($) { + var $jQval = $.validator, + adapters, + data_validation = "unobtrusiveValidation"; + + function setValidationValues(options, ruleName, value) { + options.rules[ruleName] = value; + if (options.message) { + options.messages[ruleName] = options.message; + } + } + + function splitAndTrim(value) { + return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g); + } + + function escapeAttributeValue(value) { + // As mentioned on http://api.jquery.com/category/selectors/ + return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1"); + } + + function getModelPrefix(fieldName) { + return fieldName.substr(0, fieldName.lastIndexOf(".") + 1); + } + + function appendModelPrefix(value, prefix) { + if (value.indexOf("*.") === 0) { + value = value.replace("*.", prefix); + } + return value; + } + + function onError(error, inputElement) { // 'this' is the form element + var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"), + replaceAttrValue = container.attr("data-valmsg-replace"), + replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; + + container.removeClass("field-validation-valid").addClass("field-validation-error"); + error.data("unobtrusiveContainer", container); + + if (replace) { + container.empty(); + error.removeClass("input-validation-error").appendTo(container); + } + else { + error.hide(); + } + } + + function onErrors(event, validator) { // 'this' is the form element + var container = $(this).find("[data-valmsg-summary=true]"), + list = container.find("ul"); + + if (list && list.length && validator.errorList.length) { + list.empty(); + container.addClass("validation-summary-errors").removeClass("validation-summary-valid"); + + $.each(validator.errorList, function () { + $("
  • ").html(this.message).appendTo(list); + }); + } + } + + function onSuccess(error) { // 'this' is the form element + var container = error.data("unobtrusiveContainer"); + + if (container) { + var replaceAttrValue = container.attr("data-valmsg-replace"), + replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null; + + container.addClass("field-validation-valid").removeClass("field-validation-error"); + error.removeData("unobtrusiveContainer"); + + if (replace) { + container.empty(); + } + } + } + + function onReset(event) { // 'this' is the form element + var $form = $(this), + key = '__jquery_unobtrusive_validation_form_reset'; + if ($form.data(key)) { + return; + } + // Set a flag that indicates we're currently resetting the form. + $form.data(key, true); + try { + $form.data("validator").resetForm(); + } finally { + $form.removeData(key); + } + + $form.find(".validation-summary-errors") + .addClass("validation-summary-valid") + .removeClass("validation-summary-errors"); + $form.find(".field-validation-error") + .addClass("field-validation-valid") + .removeClass("field-validation-error") + .removeData("unobtrusiveContainer") + .find(">*") // If we were using valmsg-replace, get the underlying error + .removeData("unobtrusiveContainer"); + } + + function validationInfo(form) { + var $form = $(form), + result = $form.data(data_validation), + onResetProxy = $.proxy(onReset, form), + defaultOptions = $jQval.unobtrusive.options || {}, + execInContext = function (name, args) { + var func = defaultOptions[name]; + func && $.isFunction(func) && func.apply(form, args); + }; + + if (!result) { + result = { + options: { // options structure passed to jQuery Validate's validate() method + errorClass: defaultOptions.errorClass || "input-validation-error", + errorElement: defaultOptions.errorElement || "span", + errorPlacement: function () { + onError.apply(form, arguments); + execInContext("errorPlacement", arguments); + }, + invalidHandler: function () { + onErrors.apply(form, arguments); + execInContext("invalidHandler", arguments); + }, + messages: {}, + rules: {}, + success: function () { + onSuccess.apply(form, arguments); + execInContext("success", arguments); + } + }, + attachValidation: function () { + $form + .off("reset." + data_validation, onResetProxy) + .on("reset." + data_validation, onResetProxy) + .validate(this.options); + }, + validate: function () { // a validation function that is called by unobtrusive Ajax + $form.validate(); + return $form.valid(); + } + }; + $form.data(data_validation, result); + } + + return result; + } + + $jQval.unobtrusive = { + adapters: [], + + parseElement: function (element, skipAttach) { + /// + /// Parses a single HTML element for unobtrusive validation attributes. + /// + /// The HTML element to be parsed. + /// [Optional] true to skip attaching the + /// validation to the form. If parsing just this single element, you should specify true. + /// If parsing several elements, you should specify false, and manually attach the validation + /// to the form when you are finished. The default is false. + var $element = $(element), + form = $element.parents("form")[0], + valInfo, rules, messages; + + if (!form) { // Cannot do client-side validation without a form + return; + } + + valInfo = validationInfo(form); + valInfo.options.rules[element.name] = rules = {}; + valInfo.options.messages[element.name] = messages = {}; + + $.each(this.adapters, function () { + var prefix = "data-val-" + this.name, + message = $element.attr(prefix), + paramValues = {}; + + if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy) + prefix += "-"; + + $.each(this.params, function () { + paramValues[this] = $element.attr(prefix + this); + }); + + this.adapt({ + element: element, + form: form, + message: message, + params: paramValues, + rules: rules, + messages: messages + }); + } + }); + + $.extend(rules, { "__dummy__": true }); + + if (!skipAttach) { + valInfo.attachValidation(); + } + }, + + parse: function (selector) { + /// + /// Parses all the HTML elements in the specified selector. It looks for input elements decorated + /// with the [data-val=true] attribute value and enables validation according to the data-val-* + /// attribute values. + /// + /// Any valid jQuery selector. + + // $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one + // element with data-val=true + var $selector = $(selector), + $forms = $selector.parents() + .addBack() + .filter("form") + .add($selector.find("form")) + .has("[data-val=true]"); + + $selector.find("[data-val=true]").each(function () { + $jQval.unobtrusive.parseElement(this, true); + }); + + $forms.each(function () { + var info = validationInfo(this); + if (info) { + info.attachValidation(); + } + }); + } + }; + + adapters = $jQval.unobtrusive.adapters; + + adapters.add = function (adapterName, params, fn) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). + /// [Optional] An array of parameter names (strings) that will + /// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and + /// mmmm is the parameter name). + /// The function to call, which adapts the values from the HTML + /// attributes into jQuery Validate rules and/or messages. + /// + if (!fn) { // Called with no params, just a function + fn = params; + params = []; + } + this.push({ name: adapterName, params: params, adapt: fn }); + return this; + }; + + adapters.addBool = function (adapterName, ruleName) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where + /// the jQuery Validate validation rule has no parameter values. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). + /// [Optional] The name of the jQuery Validate rule. If not provided, the value + /// of adapterName will be used instead. + /// + return this.add(adapterName, function (options) { + setValidationValues(options, ruleName || adapterName, true); + }); + }; + + adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where + /// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and + /// one for min-and-max). The HTML parameters are expected to be named -min and -max. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute (where nnnn is the adapter name). + /// The name of the jQuery Validate rule to be used when you only + /// have a minimum value. + /// The name of the jQuery Validate rule to be used when you only + /// have a maximum value. + /// The name of the jQuery Validate rule to be used when you + /// have both a minimum and maximum value. + /// [Optional] The name of the HTML attribute that + /// contains the minimum value. The default is "min". + /// [Optional] The name of the HTML attribute that + /// contains the maximum value. The default is "max". + /// + return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) { + var min = options.params.min, + max = options.params.max; + + if (min && max) { + setValidationValues(options, minMaxRuleName, [min, max]); + } + else if (min) { + setValidationValues(options, minRuleName, min); + } + else if (max) { + setValidationValues(options, maxRuleName, max); + } + }); + }; + + adapters.addSingleVal = function (adapterName, attribute, ruleName) { + /// Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where + /// the jQuery Validate validation rule has a single value. + /// The name of the adapter to be added. This matches the name used + /// in the data-val-nnnn HTML attribute(where nnnn is the adapter name). + /// [Optional] The name of the HTML attribute that contains the value. + /// The default is "val". + /// [Optional] The name of the jQuery Validate rule. If not provided, the value + /// of adapterName will be used instead. + /// + return this.add(adapterName, [attribute || "val"], function (options) { + setValidationValues(options, ruleName || adapterName, options.params[attribute]); + }); + }; + + $jQval.addMethod("__dummy__", function (value, element, params) { + return true; + }); + + $jQval.addMethod("regex", function (value, element, params) { + var match; + if (this.optional(element)) { + return true; + } + + match = new RegExp(params).exec(value); + return (match && (match.index === 0) && (match[0].length === value.length)); + }); + + $jQval.addMethod("nonalphamin", function (value, element, nonalphamin) { + var match; + if (nonalphamin) { + match = value.match(/\W/g); + match = match && match.length >= nonalphamin; + } + return match; + }); + + if ($jQval.methods.extension) { + adapters.addSingleVal("accept", "mimtype"); + adapters.addSingleVal("extension", "extension"); + } else { + // for backward compatibility, when the 'extension' validation method does not exist, such as with versions + // of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for + // validating the extension, and ignore mime-type validations as they are not supported. + adapters.addSingleVal("extension", "extension", "accept"); + } + + adapters.addSingleVal("regex", "pattern"); + adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"); + adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range"); + adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength"); + adapters.add("equalto", ["other"], function (options) { + var prefix = getModelPrefix(options.element.name), + other = options.params.other, + fullOtherName = appendModelPrefix(other, prefix), + element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0]; + + setValidationValues(options, "equalTo", element); + }); + adapters.add("required", function (options) { + // jQuery Validate equates "required" with "mandatory" for checkbox elements + if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") { + setValidationValues(options, "required", true); + } + }); + adapters.add("remote", ["url", "type", "additionalfields"], function (options) { + var value = { + url: options.params.url, + type: options.params.type || "GET", + data: {} + }, + prefix = getModelPrefix(options.element.name); + + $.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) { + var paramName = appendModelPrefix(fieldName, prefix); + value.data[paramName] = function () { + var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']"); + // For checkboxes and radio buttons, only pick up values from checked fields. + if (field.is(":checkbox")) { + return field.filter(":checked").val() || field.filter(":hidden").val() || ''; + } + else if (field.is(":radio")) { + return field.filter(":checked").val() || ''; + } + return field.val(); + }; + }); + + setValidationValues(options, "remote", value); + }); + adapters.add("password", ["min", "nonalphamin", "regex"], function (options) { + if (options.params.min) { + setValidationValues(options, "minlength", options.params.min); + } + if (options.params.nonalphamin) { + setValidationValues(options, "nonalphamin", options.params.nonalphamin); + } + if (options.params.regex) { + setValidationValues(options, "regex", options.params.regex); + } + }); + adapters.add("fileextensions", ["extensions"], function (options) { + setValidationValues(options, "extension", options.params.extensions); + }); + + $(function () { + $jQval.unobtrusive.parse(document); + }); + + return $jQval.unobtrusive; +})); \ No newline at end of file diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/jquery.validate.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/jquery.validate.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/jquery.validate.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/jquery.validate.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ar.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ar.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ar.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ar.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_az.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_az.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_az.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_az.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_bg.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_bg.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_bg.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_bg.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_bn_BD.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_bn_BD.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_bn_BD.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_bn_BD.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ca.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ca.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ca.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ca.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_cs.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_cs.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_cs.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_cs.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_da.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_da.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_da.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_da.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_de.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_de.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_de.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_de.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_el.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_el.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_el.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_el.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_es.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_es.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_es.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_es.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_es_AR.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_es_AR.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_es_AR.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_es_AR.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_es_PE.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_es_PE.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_es_PE.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_es_PE.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_et.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_et.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_et.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_et.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_eu.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_eu.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_eu.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_eu.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_fa.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_fa.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_fa.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_fa.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_fi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_fi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_fi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_fi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_fr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_fr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_fr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_fr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ge.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ge.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ge.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ge.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_gl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_gl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_gl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_gl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_he.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_he.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_he.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_he.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_hr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_hr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_hr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_hr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_hu.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_hu.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_hu.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_hu.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_hy_AM.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_hy_AM.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_hy_AM.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_hy_AM.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_id.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_id.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_id.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_id.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_is.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_is.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_is.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_is.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_it.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_it.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_it.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_it.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ja.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ja.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ja.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ja.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ka.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ka.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ka.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ka.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_kk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_kk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_kk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_kk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ko.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ko.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ko.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ko.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_lt.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_lt.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_lt.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_lt.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_lv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_lv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_lv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_lv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_mk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_mk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_mk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_mk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_my.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_my.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_my.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_my.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_nl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_nl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_nl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_nl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_no.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_no.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_no.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_no.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_pl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_pl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_pl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_pl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_pt_BR.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_pt_BR.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_pt_BR.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_pt_BR.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_pt_PT.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_pt_PT.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_pt_PT.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_pt_PT.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ro.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ro.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ro.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ro.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ru.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ru.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ru.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ru.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sd.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sd.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sd.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sd.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_si.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_si.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_si.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_si.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sr_lat.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sr_lat.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sr_lat.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sr_lat.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_sv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_sv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_th.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_th.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_th.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_th.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_tj.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_tj.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_tj.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_tj.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_tr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_tr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_tr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_tr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_uk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_uk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_uk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_uk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ur.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ur.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_ur.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_ur.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_vi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_vi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_vi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_vi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_zh.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_zh.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_zh.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_zh.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_zh_TW.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_zh_TW.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/messages_zh_TW.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/messages_zh_TW.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_de.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_de.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_de.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_de.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_es_CL.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_es_CL.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_es_CL.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_es_CL.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_fi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_fi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_fi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_fi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_nl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_nl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_nl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_nl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_pt.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_pt.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery-validation/localization/methods_pt.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery-validation/localization/methods_pt.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery/jquery.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery/jquery.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/jquery/jquery.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/jquery/jquery.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/lodash/lodash.min.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/lodash/lodash.min.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/lodash/lodash.min.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/lodash/lodash.min.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/css/select2.min.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/css/select2.min.css similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/css/select2.min.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/css/select2.min.css diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/af.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/af.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/af.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/af.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ar.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ar.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ar.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ar.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/az.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/az.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/az.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/az.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/bg.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/bg.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/bg.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/bg.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/bs.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/bs.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/bs.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/bs.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ca.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ca.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ca.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ca.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/cs.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/cs.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/cs.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/cs.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/da.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/da.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/da.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/da.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/de.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/de.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/de.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/de.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/dsb.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/dsb.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/dsb.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/dsb.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/el.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/el.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/el.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/el.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/en.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/en.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/en.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/en.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/es.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/es.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/es.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/es.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/et.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/et.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/et.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/et.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/eu.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/eu.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/eu.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/eu.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/fa.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/fa.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/fa.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/fa.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/fi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/fi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/fi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/fi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/fr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/fr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/fr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/fr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/gl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/gl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/gl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/gl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/he.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/he.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/he.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/he.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hsb.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hsb.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hsb.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hsb.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hu.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hu.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hu.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hu.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hy.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hy.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/hy.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/hy.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/id.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/id.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/id.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/id.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/is.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/is.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/is.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/is.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/it.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/it.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/it.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/it.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ja.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ja.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ja.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ja.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/km.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/km.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/km.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/km.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ko.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ko.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ko.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ko.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/lt.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/lt.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/lt.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/lt.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/lv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/lv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/lv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/lv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/mk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/mk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/mk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/mk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ms.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ms.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ms.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ms.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/nb.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/nb.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/nb.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/nb.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/nl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/nl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/nl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/nl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/pl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/pl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/pl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/pl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ps.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ps.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ps.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ps.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/pt-BR.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/pt-BR.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/pt-BR.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/pt-BR.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/pt.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/pt.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/pt.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/pt.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ro.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ro.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ro.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ro.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ru.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ru.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/ru.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/ru.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sr-Cyrl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sr-Cyrl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sr-Cyrl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sr-Cyrl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/sv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/sv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/th.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/th.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/th.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/th.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/tr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/tr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/tr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/tr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/uk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/uk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/uk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/uk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/vi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/vi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/vi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/vi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/zh-CN.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/zh-CN.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/zh-CN.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/zh-CN.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/zh-TW.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/zh-TW.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/i18n/zh-TW.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/i18n/zh-TW.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/select2.full.min.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/select2.full.min.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/select2.full.min.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/select2.full.min.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/select2.min.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/select2.min.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/select2/js/select2.min.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/select2/js/select2.min.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/sweetalert/sweetalert.min.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/sweetalert/sweetalert.min.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/sweetalert/sweetalert.min.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/sweetalert/sweetalert.min.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/jquery.timeago.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/jquery.timeago.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/jquery.timeago.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/jquery.timeago.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/README.md b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/README.md similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/README.md rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/README.md diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.af.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.af.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.af.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.af.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.am.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.am.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.am.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.am.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ar.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ar.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ar.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ar.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.az-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.az-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.az-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.az-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.az.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.az.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.az.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.az.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.bg.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.bg.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.bg.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.bg.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.bs.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.bs.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.bs.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.bs.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ca.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ca.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ca.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ca.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.cs.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.cs.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.cs.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.cs.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.cy.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.cy.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.cy.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.cy.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.da.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.da.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.da.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.da.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.de-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.de-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.de-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.de-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.de.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.de.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.de.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.de.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.dv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.dv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.dv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.dv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.el.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.el.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.el.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.el.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.en-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.en-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.en-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.en-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.en.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.en.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.en.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.en.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.es-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.es-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.es-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.es-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.es.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.es.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.es.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.es.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.et.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.et.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.et.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.et.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.eu.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.eu.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.eu.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.eu.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fa-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fa-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fa-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fa-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fa.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fa.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fa.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fa.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fr-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fr-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fr-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fr-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.fr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.fr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.gl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.gl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.gl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.gl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.he.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.he.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.he.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.he.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.hr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.hr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.hr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.hr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.hu.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.hu.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.hu.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.hu.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.hy.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.hy.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.hy.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.hy.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.id.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.id.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.id.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.id.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.is.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.is.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.is.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.is.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.it-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.it-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.it-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.it-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.it.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.it.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.it.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.it.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ja.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ja.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ja.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ja.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.jv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.jv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.jv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.jv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ko.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ko.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ko.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ko.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ky.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ky.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ky.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ky.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.lt.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.lt.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.lt.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.lt.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.lv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.lv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.lv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.lv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.mk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.mk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.mk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.mk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.nl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.nl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.nl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.nl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.no.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.no.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.no.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.no.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt-br-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-br-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt-br-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-br-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt-br.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-br.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt-br.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-br.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.pt.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.pt.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ro.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ro.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ro.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ro.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.rs.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.rs.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.rs.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.rs.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ru.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ru.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ru.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ru.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.rw.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.rw.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.rw.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.rw.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.si.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.si.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.si.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.si.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sl.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sl.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sl.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sl.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sq.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sq.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sq.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sq.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sv.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sv.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.sv.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.sv.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.th.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.th.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.th.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.th.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.tr-short.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.tr-short.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.tr-short.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.tr-short.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.tr.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.tr.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.tr.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.tr.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.uk.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.uk.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.uk.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.uk.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ur.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ur.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.ur.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.ur.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.uz.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.uz.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.uz.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.uz.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.vi.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.vi.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.vi.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.vi.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.zh-CN.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.zh-CN.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.zh-CN.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.zh-CN.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.zh-TW.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.zh-TW.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/timeago/locales/jquery.timeago.zh-TW.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/timeago/locales/jquery.timeago.zh-TW.js diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.css similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.css diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.js.map b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.js.map similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.js.map rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.js.map diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.min.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.min.css similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.min.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.min.css diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.min.js b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.min.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/wwwroot/libs/toastr/toastr.min.js rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/libs/toastr/toastr.min.js diff --git a/samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/index.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/pages/index.css similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/index.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/pages/index.css diff --git a/samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/index.min.css b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/pages/index.min.css similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/index.min.css rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/pages/index.min.css diff --git a/samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/index.scss b/samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/pages/index.scss similarity index 100% rename from samples/BookStore/src/Acme.BookStore.Web/wwwroot/pages/index.scss rename to samples/DashboardDemo/src/DashboardDemo.Web/wwwroot/pages/index.scss diff --git a/samples/DashboardDemo/src/DashboardDemo.Web/yarn.lock b/samples/DashboardDemo/src/DashboardDemo.Web/yarn.lock new file mode 100644 index 0000000000..e5f81f3581 --- /dev/null +++ b/samples/DashboardDemo/src/DashboardDemo.Web/yarn.lock @@ -0,0 +1,1660 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@abp/aspnetcore.mvc.ui.theme.basic@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.basic/-/aspnetcore.mvc.ui.theme.basic-0.4.9.tgz#926a25c641225598c8666b658c1df3fa7fbed7cf" + dependencies: + "@abp/aspnetcore.mvc.ui.theme.shared" "^0.4.9" + +"@abp/aspnetcore.mvc.ui.theme.shared@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui.theme.shared/-/aspnetcore.mvc.ui.theme.shared-0.4.9.tgz#cc8d83162329e3044e12a358c3f3ce6fb3e75f1b" + dependencies: + "@abp/aspnetcore.mvc.ui" "^0.4.9" + "@abp/bootstrap" "^0.4.9" + "@abp/datatables.net-bs4" "^0.4.9" + "@abp/font-awesome" "^0.4.9" + "@abp/jquery-form" "^0.4.9" + "@abp/jquery-validation-unobtrusive" "^0.4.9" + "@abp/lodash" "^0.4.9" + "@abp/select2" "^0.4.9" + "@abp/sweetalert" "^0.4.9" + "@abp/timeago" "^0.4.9" + "@abp/toastr" "^0.4.9" + +"@abp/aspnetcore.mvc.ui@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/aspnetcore.mvc.ui/-/aspnetcore.mvc.ui-0.4.9.tgz#cb7a1905ec003fd459e13e577ef7b3d2ada55f5f" + dependencies: + ansi-colors "^1.1.0" + extend-object "^1.0.0" + gulp "^3.9.1" + merge-stream "^1.0.1" + path "^0.12.7" + rimraf "^2.6.2" + +"@abp/bootstrap@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/bootstrap/-/bootstrap-0.4.9.tgz#2092858956c9b770509619ffa70d46b382531ce1" + dependencies: + "@abp/core" "^0.4.9" + bootstrap "^4.1.1" + +"@abp/core@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/core/-/core-0.4.9.tgz#d644dde25fb9749e4d179bd72729afeae3b6e230" + +"@abp/datatables.net-bs4@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/datatables.net-bs4/-/datatables.net-bs4-0.4.9.tgz#a51dcc6c166af8a3126f121cf410a170027e6d11" + dependencies: + "@abp/datatables.net" "^0.4.9" + datatables.net-bs4 "^1.10.16" + +"@abp/datatables.net@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/datatables.net/-/datatables.net-0.4.9.tgz#1c96f4c800b9b05fc0dcec29dac471e1e5c5624a" + dependencies: + "@abp/core" "^0.4.9" + datatables.net "^1.10.16" + +"@abp/font-awesome@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/font-awesome/-/font-awesome-0.4.9.tgz#eb1691ff33be53832b5063aa2a382dc7d65a3f43" + dependencies: + "@abp/core" "^0.4.9" + font-awesome "^4.7.0" + +"@abp/jquery-form@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/jquery-form/-/jquery-form-0.4.9.tgz#70af1af668f6a55dbadb455f64b6d1cbdc63caf8" + dependencies: + "@abp/jquery" "^0.4.9" + jquery-form "^4.2.2" + +"@abp/jquery-validation-unobtrusive@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-0.4.9.tgz#3512f1f7fbf9f982b084b8f9cb68f91553dfbcef" + dependencies: + "@abp/jquery-validation" "^0.4.9" + jquery-validation-unobtrusive "^3.2.9" + +"@abp/jquery-validation@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/jquery-validation/-/jquery-validation-0.4.9.tgz#5751da6dac87a6783dd4d1165240b3c29eed3ac3" + dependencies: + "@abp/jquery" "^0.4.9" + jquery-validation "^1.17.0" + +"@abp/jquery@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/jquery/-/jquery-0.4.9.tgz#4033a4e3d6df643babc6baf29cd47ba5519338f0" + dependencies: + "@abp/core" "^0.4.9" + jquery "^3.3.1" + +"@abp/lodash@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/lodash/-/lodash-0.4.9.tgz#5d30d2d1f6245bf519160d5ba3ce5f4d08e9b48b" + dependencies: + "@abp/core" "^0.4.9" + lodash "^4.17.10" + +"@abp/select2@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/select2/-/select2-0.4.9.tgz#6bbb4d8ffd67876787a400647659f5bb7a1d642d" + dependencies: + "@abp/core" "^0.4.9" + select2 "^4.0.5" + +"@abp/sweetalert@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/sweetalert/-/sweetalert-0.4.9.tgz#34cd4f36004229621c454c3d80cc0d2d8d57d42e" + dependencies: + "@abp/core" "^0.4.9" + sweetalert "^2.1.0" + +"@abp/timeago@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/timeago/-/timeago-0.4.9.tgz#9314bb2c7194636d2dc294ab0b8a07522e26e905" + dependencies: + "@abp/jquery" "^0.4.9" + timeago "^1.6.3" + +"@abp/toastr@^0.4.9": + version "0.4.9" + resolved "https://registry.yarnpkg.com/@abp/toastr/-/toastr-0.4.9.tgz#c95703330fefb4e9204292970d17d9facebdffb5" + dependencies: + "@abp/jquery" "^0.4.9" + toastr "^2.1.4" + +almond@~0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/almond/-/almond-0.3.3.tgz#a0e7c95ac7624d6417b4494b1e68bff693168a20" + +ansi-colors@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" + dependencies: + ansi-wrap "^0.1.0" + +ansi-gray@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" + dependencies: + ansi-wrap "0.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + +array-each@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" + +array-slice@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" + +array-uniq@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +atob@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.0.tgz#ab2b150e51d7b122b9efc8d7340c06b6c41076bc" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +beeper@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" + +bootstrap@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.1.tgz#3aec85000fa619085da8d2e4983dfd67cf2114cb" + +brace-expansion@^1.0.0, brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +chalk@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clone-stats@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + +clone@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" + +clone@^1.0.0, clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +datatables.net-bs4@^1.10.16: + version "1.10.16" + resolved "https://registry.yarnpkg.com/datatables.net-bs4/-/datatables.net-bs4-1.10.16.tgz#9eee67cfa8565bd3807a603a188305f7d0e20e32" + dependencies: + datatables.net "1.10.16" + jquery ">=1.7" + +datatables.net@1.10.16, datatables.net@^1.10.16: + version "1.10.16" + resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.10.16.tgz#4b052d1082824261b68eed9d22741b711d3d2469" + dependencies: + jquery ">=1.7" + +dateformat@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" + +debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +defaults@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +deprecated@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" + +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + +duplexer2@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + dependencies: + readable-stream "~1.1.9" + +end-of-stream@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" + dependencies: + once "~1.3.0" + +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + dependencies: + homedir-polyfill "^1.0.1" + +extend-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/extend-object/-/extend-object-1.0.0.tgz#42514f84015d1356caf5187969dfb2bc1bda0823" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fancy-log@^1.1.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + time-stamp "^1.0.0" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +find-index@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" + +findup-sync@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" + dependencies: + detect-file "^1.0.0" + is-glob "^3.1.0" + micromatch "^3.0.4" + resolve-dir "^1.0.1" + +fined@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" + dependencies: + expand-tilde "^2.0.2" + is-plain-object "^2.0.3" + object.defaults "^1.1.0" + object.pick "^1.2.0" + parse-filepath "^1.0.1" + +first-chunk-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" + +flagged-respawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" + +font-awesome@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + dependencies: + for-in "^1.0.1" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +gaze@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" + dependencies: + globule "~0.1.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +glob-stream@^3.1.5: + version "3.1.18" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" + dependencies: + glob "^4.3.1" + glob2base "^0.0.12" + minimatch "^2.0.1" + ordered-read-streams "^0.1.0" + through2 "^0.6.1" + unique-stream "^1.0.0" + +glob-watcher@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" + dependencies: + gaze "^0.5.1" + +glob2base@^0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" + dependencies: + find-index "^0.1.1" + +glob@^4.3.1: + version "4.5.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "^2.0.1" + once "^1.3.0" + +glob@^7.0.5: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~3.1.21: + version "3.1.21" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" + dependencies: + graceful-fs "~1.2.0" + inherits "1" + minimatch "~0.2.11" + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + +globule@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" + dependencies: + glob "~3.1.21" + lodash "~1.0.1" + minimatch "~0.2.11" + +glogg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" + dependencies: + sparkles "^1.0.0" + +graceful-fs@^3.0.0: + version "3.0.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" + dependencies: + natives "^1.1.0" + +graceful-fs@~1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" + +gulp-util@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" + dependencies: + array-differ "^1.0.0" + array-uniq "^1.0.2" + beeper "^1.0.0" + chalk "^1.0.0" + dateformat "^2.0.0" + fancy-log "^1.1.0" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + lodash._reescape "^3.0.0" + lodash._reevaluate "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.template "^3.0.0" + minimist "^1.1.0" + multipipe "^0.1.2" + object-assign "^3.0.0" + replace-ext "0.0.1" + through2 "^2.0.0" + vinyl "^0.5.0" + +gulp@^3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.9.1.tgz#571ce45928dd40af6514fc4011866016c13845b4" + dependencies: + archy "^1.0.0" + chalk "^1.0.0" + deprecated "^0.0.1" + gulp-util "^3.0.0" + interpret "^1.0.0" + liftoff "^2.1.0" + minimist "^1.1.0" + orchestrator "^0.3.0" + pretty-hrtime "^1.0.0" + semver "^4.1.0" + tildify "^1.0.0" + v8flags "^2.0.2" + vinyl-fs "^0.3.0" + +gulplog@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + dependencies: + glogg "^1.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-gulplog@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" + dependencies: + sparkles "^1.0.0" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +homedir-polyfill@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + dependencies: + parse-passwd "^1.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" + +inherits@2, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +interpret@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + +is-absolute@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + dependencies: + is-relative "^1.0.0" + is-windows "^1.0.1" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" + dependencies: + is-number "^4.0.0" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-relative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + dependencies: + is-unc-path "^1.0.0" + +is-unc-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + dependencies: + unc-path-regex "^0.1.2" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-windows@^1.0.1, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +jquery-form@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/jquery-form/-/jquery-form-4.2.2.tgz#9f96fb141ec9cbe0cdaf58b4d3f1dcbb009cdd52" + dependencies: + jquery ">=1.7.2" + +jquery-mousewheel@~3.1.13: + version "3.1.13" + resolved "https://registry.yarnpkg.com/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz#06f0335f16e353a695e7206bf50503cb523a6ee5" + +jquery-validation-unobtrusive@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/jquery-validation-unobtrusive/-/jquery-validation-unobtrusive-3.2.9.tgz#674bec080408c54c12a22d4234ad3904f47fd5d5" + dependencies: + jquery ">=1.8" + jquery-validation ">=1.16" + +jquery-validation@>=1.16, jquery-validation@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/jquery-validation/-/jquery-validation-1.17.0.tgz#ab66b6b583d7740b9bbd148993e50e8ac041f35b" + dependencies: + jquery "^1.7 || ^2.0 || ^3.1" + +jquery@>=1.12.0, jquery@>=1.2.3, jquery@>=1.7, jquery@>=1.7.2, jquery@>=1.8, "jquery@^1.7 || ^2.0 || ^3.1", jquery@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +liftoff@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" + dependencies: + extend "^3.0.0" + findup-sync "^2.0.0" + fined "^1.0.1" + flagged-respawn "^1.0.0" + is-plain-object "^2.0.4" + object.map "^1.0.0" + rechoir "^0.6.2" + resolve "^1.1.7" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + +lodash._basetostring@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" + +lodash._basevalues@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + +lodash._reescape@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" + +lodash._reevaluate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + +lodash._root@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + +lodash.escape@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" + dependencies: + lodash._root "^3.0.0" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + +lodash.template@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" + dependencies: + lodash._basecopy "^3.0.0" + lodash._basetostring "^3.0.0" + lodash._basevalues "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + lodash.keys "^3.0.0" + lodash.restparam "^3.0.0" + lodash.templatesettings "^3.0.0" + +lodash.templatesettings@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + +lodash@^4.17.10: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +lodash@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +make-iterator@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" + dependencies: + kind-of "^6.0.2" + +map-cache@^0.2.0, map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + dependencies: + readable-stream "^2.0.1" + +micromatch@^3.0.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +minimatch@^2.0.1: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + dependencies: + brace-expansion "^1.0.0" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimatch@~0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +multipipe@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" + dependencies: + duplexer2 "0.0.2" + +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-odd "^2.0.0" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natives@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.3.tgz#44a579be64507ea2d6ed1ca04a9415915cf75558" + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.defaults@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" + dependencies: + array-each "^1.0.1" + array-slice "^1.0.0" + for-own "^1.0.0" + isobject "^3.0.0" + +object.map@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +object.pick@^1.2.0, object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +orchestrator@^0.3.0: + version "0.3.8" + resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" + dependencies: + end-of-stream "~0.1.5" + sequencify "~0.0.7" + stream-consume "~0.1.0" + +ordered-read-streams@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +parse-filepath@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + dependencies: + is-absolute "^1.0.0" + map-cache "^0.2.0" + path-root "^0.1.1" + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + dependencies: + path-root-regex "^0.1.0" + +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + dependencies: + process "^0.11.1" + util "^0.10.3" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +pretty-hrtime@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +process@^0.11.1: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +promise-polyfill@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-6.1.0.tgz#dfa96943ea9c121fca4de9b5868cb39d3472e057" + +"readable-stream@>=1.0.33-1 <1.1.0-0": + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.1, readable-stream@^2.1.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +replace-ext@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@^1.1.6, resolve@^1.1.7: + version "1.7.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" + dependencies: + path-parse "^1.0.5" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rimraf@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +select2@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/select2/-/select2-4.0.5.tgz#7aac50692561985b34d3b82ec55e226f8960d40a" + dependencies: + almond "~0.3.1" + jquery-mousewheel "~3.1.13" + +semver@^4.1.0: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + +sequencify@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" + dependencies: + atob "^2.0.0" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +sparkles@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stream-consume@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.1.tgz#d3bdb598c2bd0ae82b8cac7ac50b1107a7996c48" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-bom@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" + dependencies: + first-chunk-stream "^1.0.0" + is-utf8 "^0.2.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +sweetalert@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sweetalert/-/sweetalert-2.1.0.tgz#d605dec840058fa8ad4a1f7c2c8c194bc72c27fa" + dependencies: + es6-object-assign "^1.1.0" + promise-polyfill "^6.0.2" + +through2@^0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + +through2@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + +tildify@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" + dependencies: + os-homedir "^1.0.0" + +time-stamp@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + +timeago@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/timeago/-/timeago-1.6.3.tgz#162a1adae99356297df59339837d09f1b0f36465" + dependencies: + jquery ">=1.2.3" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toastr@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" + dependencies: + jquery ">=1.12.0" + +unc-path-regex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +unique-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +use@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" + dependencies: + kind-of "^6.0.2" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +v8flags@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" + dependencies: + user-home "^1.1.1" + +vinyl-fs@^0.3.0: + version "0.3.14" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" + dependencies: + defaults "^1.0.0" + glob-stream "^3.1.5" + glob-watcher "^0.0.6" + graceful-fs "^3.0.0" + mkdirp "^0.5.0" + strip-bom "^1.0.0" + through2 "^0.6.1" + vinyl "^0.4.0" + +vinyl@^0.4.0: + version "0.4.6" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" + dependencies: + clone "^0.2.0" + clone-stats "^0.0.1" + +vinyl@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +which@^1.2.14: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" diff --git a/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemo.Application.Tests.csproj b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemo.Application.Tests.csproj new file mode 100644 index 0000000000..9ad04d3181 --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemo.Application.Tests.csproj @@ -0,0 +1,28 @@ + + + + + + netcoreapp2.2 + DashboardDemo + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoApplicationTestBase.cs b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoApplicationTestBase.cs new file mode 100644 index 0000000000..0e33bd0c0e --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoApplicationTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp; + +namespace DashboardDemo +{ + public abstract class DashboardDemoApplicationTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoApplicationTestModule.cs b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoApplicationTestModule.cs new file mode 100644 index 0000000000..4c748d66ab --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoApplicationTestModule.cs @@ -0,0 +1,78 @@ +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using DashboardDemo.EntityFrameworkCore; +using Volo.Abp; +using Volo.Abp.Autofac; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Modularity; + +namespace DashboardDemo +{ + [DependsOn( + typeof(DashboardDemoApplicationModule), + typeof(DashboardDemoEntityFrameworkCoreModule), + typeof(AbpAutofacModule), + typeof(AbpTestBaseModule) + )] + public class DashboardDemoApplicationTestModule : AbpModule + { + private SqliteConnection _sqliteConnection; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddAlwaysAllowAuthorization(); + + ConfigureInMemorySqlite(context.Services); + } + + private void ConfigureInMemorySqlite(IServiceCollection services) + { + _sqliteConnection = CreateDatabaseAndGetConnection(); + + services.Configure(options => + { + options.Configure(context => { context.DbContextOptions.UseSqlite(_sqliteConnection); }); + }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + SeedTestData(context); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + _sqliteConnection.Dispose(); + } + + private static SqliteConnection CreateDatabaseAndGetConnection() + { + var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + + using (var context = new DashboardDemoMigrationsDbContext(options)) + { + context.GetService().CreateTables(); + } + + return connection; + } + + private static void SeedTestData(ApplicationInitializationContext context) + { + using (var scope = context.ServiceProvider.CreateScope()) + { + scope.ServiceProvider + .GetRequiredService() + .Build(); + } + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoTestDataBuilder.cs b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoTestDataBuilder.cs new file mode 100644 index 0000000000..8d519fe0ba --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/DashboardDemoTestDataBuilder.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Identity; +using Volo.Abp.Threading; + +namespace DashboardDemo +{ + public class DashboardDemoTestDataBuilder : ITransientDependency + { + private readonly IDataSeeder _dataSeeder; + + public DashboardDemoTestDataBuilder(IDataSeeder dataSeeder) + { + _dataSeeder = dataSeeder; + } + + public void Build() + { + AsyncHelper.RunSync(BuildInternalAsync); + } + + public async Task BuildInternalAsync() + { + await _dataSeeder.SeedAsync(); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/test/DashboardDemo.Application.Tests/Samples/SampleTests.cs b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/Samples/SampleTests.cs new file mode 100644 index 0000000000..60999e1a60 --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Application.Tests/Samples/SampleTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using DashboardDemo.Users; +using Shouldly; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; +using Volo.Abp.Uow; +using Xunit; + +namespace DashboardDemo.Samples +{ + public class SampleTests : DashboardDemoApplicationTestBase + { + private readonly IIdentityUserAppService _userAppService; + private readonly IRepository _appUserRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public SampleTests() + { + _userAppService = ServiceProvider.GetRequiredService(); + _appUserRepository = ServiceProvider.GetRequiredService>(); + _unitOfWorkManager = ServiceProvider.GetRequiredService(); + } + + [Fact] + public async Task Initial_Data_Should_Contain_Admin_User() + { + //Act + var result = await _userAppService.GetListAsync(new GetIdentityUsersInput()); + + //Assert + result.TotalCount.ShouldBeGreaterThan(0); + result.Items.ShouldContain(u => u.UserName == "admin"); + } + + [Fact] + public async Task Should_Query_AppUser() + { + /* Need to manually start Unit Of Work because + * FirstOrDefaultAsync should be executed while db connection / context is available. + */ + using (var uow = _unitOfWorkManager.Begin()) + { + //Act + var adminUser = await _appUserRepository + .Where(u => u.UserName == "admin") + .FirstOrDefaultAsync(); + + //Assert + adminUser.ShouldNotBeNull(); + + await uow.CompleteAsync(); + } + } + } +} diff --git a/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemo.Web.Tests.csproj b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemo.Web.Tests.csproj new file mode 100644 index 0000000000..ead7427378 --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemo.Web.Tests.csproj @@ -0,0 +1,39 @@ + + + + + + netcoreapp2.2 + Exe + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + DashboardDemo + true + true + true + true + + + + + + + + + + + + Always + PreserveNewest + + + + + + + + + + + + + diff --git a/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestBase.cs b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestBase.cs new file mode 100644 index 0000000000..b81ae13794 --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestBase.cs @@ -0,0 +1,43 @@ +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Shouldly; +using Volo.Abp.AspNetCore.TestBase; + +namespace DashboardDemo +{ + public abstract class DashboardDemoWebTestBase : AbpAspNetCoreIntegratedTestBase + { + protected override IWebHostBuilder CreateWebHostBuilder() + { + return base + .CreateWebHostBuilder() + .UseContentRoot(WebContentDirectoryFinder.CalculateContentRootFolder()); + } + + protected virtual async Task GetResponseAsObjectAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) + { + var strResponse = await GetResponseAsStringAsync(url, expectedStatusCode); + return JsonConvert.DeserializeObject(strResponse, new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + } + + protected virtual async Task GetResponseAsStringAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) + { + var response = await GetResponseAsync(url, expectedStatusCode); + return await response.Content.ReadAsStringAsync(); + } + + protected virtual async Task GetResponseAsync(string url, HttpStatusCode expectedStatusCode = HttpStatusCode.OK) + { + var response = await Client.GetAsync(url); + response.StatusCode.ShouldBe(expectedStatusCode); + return response; + } + } +} diff --git a/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestModule.cs b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestModule.cs new file mode 100644 index 0000000000..9e4bcfa289 --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestModule.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Globalization; +using Localization.Resources.AbpUi; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Localization; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using Microsoft.AspNetCore.Mvc.Internal; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using DashboardDemo.Localization.DashboardDemo; +using DashboardDemo.Menus; +using Volo.Abp; +using Volo.Abp.Account.Web; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; +using Volo.Abp.AspNetCore.TestBase; +using Volo.Abp.Identity.Web; +using Volo.Abp.Localization; +using Volo.Abp.Localization.Resources.AbpValidation; +using Volo.Abp.Modularity; +using Volo.Abp.UI.Navigation; + +namespace DashboardDemo +{ + [DependsOn( + typeof(AbpAspNetCoreTestBaseModule), + typeof(DashboardDemoApplicationTestModule), + typeof(AbpIdentityWebModule), + typeof(AbpAccountWebModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule) + )] + public class DashboardDemoWebTestModule : AbpModule + { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.PreConfigure(builder => + { + builder.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(DashboardDemoWebModule).Assembly)); + }); + } + + public override void ConfigureServices(ServiceConfigurationContext context) + { + ConfigureLocalizationServices(context.Services); + ConfigureNavigationServices(context.Services); + } + + private static void ConfigureLocalizationServices(IServiceCollection services) + { + var cultures = new List { new CultureInfo("en"), new CultureInfo("tr") }; + services.Configure(options => + { + options.DefaultRequestCulture = new RequestCulture("en"); + options.SupportedCultures = cultures; + options.SupportedUICultures = cultures; + }); + + services.Configure(options => + { + options.Resources + .Get() + .AddBaseTypes( + typeof(AbpValidationResource), + typeof(AbpUiResource) + ); + }); + } + + private static void ConfigureNavigationServices(IServiceCollection services) + { + services.Configure(options => + { + options.MenuContributors.Add(new DashboardDemoMenuContributor()); + }); + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + var env = context.GetEnvironment(); + + //app.UseErrorPage(); + + app.UseVirtualFiles(); + app.UseAuthentication(); + + app.UseRequestLocalization(app.ApplicationServices.GetRequiredService>().Value); + + app.UseMvcWithDefaultRouteAndArea(); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestStartup.cs b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestStartup.cs new file mode 100644 index 0000000000..1ab2b1d5d9 --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/DashboardDemoWebTestStartup.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Volo.Abp; + +namespace DashboardDemo +{ + public class DashboardDemoWebTestStartup + { + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddApplication(options => + { + options.UseAutofac(); + }); + + return services.BuildServiceProviderFromFactory(); + } + + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + app.InitializeApplication(); + } + } +} \ No newline at end of file diff --git a/samples/DashboardDemo/test/DashboardDemo.Web.Tests/Samples/SampleWebTest.cs b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/Samples/SampleWebTest.cs new file mode 100644 index 0000000000..d4b28c6a2a --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/Samples/SampleWebTest.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace DashboardDemo.Samples +{ + public class SampleWebTest : DashboardDemoWebTestBase + { + [Fact(Skip = "This is disabled since not working")] + public async Task Welcome_Page() + { + var response = await GetResponseAsStringAsync("/"); + response.ShouldNotBeNull(); + } + + [Fact(Skip = "This is disabled since not working")] + public async Task Login_Page() + { + var response = await GetResponseAsStringAsync("/Account/Login/"); + response.ShouldNotBeNull(); + } + } +} diff --git a/samples/DashboardDemo/test/DashboardDemo.Web.Tests/WebContentDirectoryFinder.cs b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/WebContentDirectoryFinder.cs new file mode 100644 index 0000000000..b4e5512bcd --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/WebContentDirectoryFinder.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using System.Linq; + +namespace DashboardDemo +{ + /// + /// This class is used to find root path of the web project. Used for; + /// 1. unit tests (to find views). + /// 2. entity framework core command line commands (to find the conn string). + /// + public static class WebContentDirectoryFinder + { + public static string CalculateContentRootFolder() + { + var domainAssemblyDirectoryPath = Path.GetDirectoryName(typeof(DashboardDemoDomainModule).Assembly.Location); + if (domainAssemblyDirectoryPath == null) + { + throw new Exception($"Could not find location of {typeof(DashboardDemoDomainModule).Assembly.FullName} assembly!"); + } + + var directoryInfo = new DirectoryInfo(domainAssemblyDirectoryPath); + while (!DirectoryContains(directoryInfo.FullName, "DashboardDemo.sln")) + { + if (directoryInfo.Parent == null) + { + throw new Exception("Could not find content root folder!"); + } + + directoryInfo = directoryInfo.Parent; + } + + var webFolder = Path.Combine(directoryInfo.FullName, $"src{Path.DirectorySeparatorChar}DashboardDemo.Web"); + if (Directory.Exists(webFolder)) + { + return webFolder; + } + + throw new Exception("Could not find root folder of the web project!"); + } + + private static bool DirectoryContains(string directory, string fileName) + { + return Directory.GetFiles(directory).Any(filePath => string.Equals(Path.GetFileName(filePath), fileName)); + } + } +} diff --git a/samples/DashboardDemo/test/DashboardDemo.Web.Tests/xunit.runner.json b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/xunit.runner.json new file mode 100644 index 0000000000..34b2fe2cdd --- /dev/null +++ b/samples/DashboardDemo/test/DashboardDemo.Web.Tests/xunit.runner.json @@ -0,0 +1,3 @@ +{ + "shadowCopy": false +} \ No newline at end of file diff --git a/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj b/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj index 0b938960f5..3fb01d55d7 100644 --- a/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj +++ b/samples/MicroserviceDemo/applications/AuthServer.Host/AuthServer.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs b/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs index 1560c383d1..43a36383fd 100644 --- a/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs +++ b/samples/MicroserviceDemo/applications/ConsoleClientDemo/ClientDemoService.cs @@ -14,13 +14,13 @@ namespace ConsoleClientDemo { private readonly IIdentityUserAppService _userAppService; private readonly IProductAppService _productAppService; - private readonly IIdentityModelHttpClientAuthenticator _authenticator; + private readonly IIdentityModelAuthenticationService _authenticator; private readonly RemoteServiceOptions _remoteServiceOptions; public ClientDemoService( IIdentityUserAppService userAppService, IProductAppService productAppService, - IIdentityModelHttpClientAuthenticator authenticator, + IIdentityModelAuthenticationService authenticator, IOptions remoteServiceOptions) { _userAppService = userAppService; @@ -38,7 +38,7 @@ namespace ConsoleClientDemo /// /// Shows how to manually create an HttpClient and authenticate using the - /// IIdentityModelHttpClientAuthenticator service. + /// IIdentityModelAuthenticationService service. /// private async Task TestWithHttpClient() { @@ -49,7 +49,7 @@ namespace ConsoleClientDemo { using (var client = new HttpClient()) { - await _authenticator.AuthenticateAsync(client); + await _authenticator.TryAuthenticateAsync(client); var url = GetServerUrl() + "Test/Index"; @@ -75,7 +75,7 @@ namespace ConsoleClientDemo /// /// Shows how to use application service interfaces (IIdentityUserAppService in this sample) /// to call a remote service which is possible by the dynamic http client proxy system. - /// No need to use IIdentityModelHttpClientAuthenticator since the dynamic http client proxy + /// No need to use IIdentityModelAuthenticationService since the dynamic http client proxy /// system internally uses it. You just inject a service (IIdentityUserAppService) /// and call a method (GetListAsync) like a local method. /// @@ -104,7 +104,7 @@ namespace ConsoleClientDemo /// /// Shows how to use application service interfaces (IProductAppService in this sample) /// to call a remote service which is possible by the dynamic http client proxy system. - /// No need to use IIdentityModelHttpClientAuthenticator since the dynamic http client proxy + /// No need to use IIdentityModelAuthenticationService since the dynamic http client proxy /// system internally uses it. You just inject a service (IProductAppService) /// and call a method (GetListAsync) like a local method. /// diff --git a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj index 28bb533c46..3ae6528058 100644 --- a/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj +++ b/samples/MicroserviceDemo/gateways/BackendAdminAppGateway.Host/BackendAdminAppGateway.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj b/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj index dafa931f24..7aa8f1eab1 100644 --- a/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj +++ b/samples/MicroserviceDemo/gateways/InternalGateway.Host/InternalGateway.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj b/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj index c04e6bdfaa..ecc2bcdfe5 100644 --- a/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj +++ b/samples/MicroserviceDemo/gateways/PublicWebSiteGateway.Host/PublicWebSiteGateway.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/k8s/DeployToKubernetes.ps1 b/samples/MicroserviceDemo/k8s/DeployToKubernetes.ps1 new file mode 100644 index 0000000000..93fcbe7c88 --- /dev/null +++ b/samples/MicroserviceDemo/k8s/DeployToKubernetes.ps1 @@ -0,0 +1,33 @@ +echo "Creating non-deployments" +kubectl create -f (Get-ChildItem -Recurse -File -Filter "*.yaml" -Exclude "*deployment*.yaml" | +Group-Object -Property Directory | +ForEach-Object { + @( + $_.Group | + Resolve-Path -Relative | # make relative path + ForEach-Object Substring 2 # cut '.\' part + )-join',' +}) + +echo "Creating deployments" +kubectl create -f (Get-ChildItem -Recurse -File -Filter "*deployment*.yaml" | +Group-Object -Property Directory | +ForEach-Object { + @( + $_.Group | + Resolve-Path -Relative | # make relative path + ForEach-Object Substring 2 # cut '.\' part + )-join',' +}) + +echo "Forwarding ports" +$jobs=@() +$portforward = { + param($app, $port) + kubectl port-forward "$app" "$port" +} + +$jobs+=Start-Job -ScriptBlock $portforward -ArgumentList deployment.apps/auth-server,51511:51511 +$jobs+=Start-Job -ScriptBlock $portforward -ArgumentList deployment.apps/backend-admin-app,51512:80 +$jobs+=Start-Job -ScriptBlock $portforward -ArgumentList deployment.apps/public-website,51513:80 +Wait-Job $jobs diff --git a/samples/MicroserviceDemo/k8s/DeployToKubernetes.sh b/samples/MicroserviceDemo/k8s/DeployToKubernetes.sh new file mode 100644 index 0000000000..e16a08b853 --- /dev/null +++ b/samples/MicroserviceDemo/k8s/DeployToKubernetes.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +kubectl create -f $(ls -x | grep .yaml | grep -v deployment | tr " \t\n\r" "," | sed 's/.$//') +kubectl create -f $(ls -x | grep .yaml | grep deployment | tr " \t\n\r" "," | sed 's/.$//') + +kubectl port-forward deployment.apps/auth-server 51511:51511 & +kubectl port-forward deployment.apps/backend-admin-app 51512:80 & +kubectl port-forward deployment.apps/public-website 51513:80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/k8s/expose-service.yaml b/samples/MicroserviceDemo/k8s/expose-service.yaml index b1b668a533..682e63f103 100644 --- a/samples/MicroserviceDemo/k8s/expose-service.yaml +++ b/samples/MicroserviceDemo/k8s/expose-service.yaml @@ -3,39 +3,36 @@ kind: Service metadata: name: auth-server-expose spec: - type: LoadBalancer + type: NodePort selector: app: auth-server ports: - protocol: TCP port: 51511 targetPort: 51511 - nodePort: 30001 --- apiVersion: v1 kind: Service metadata: name: backend-admin-app-expose spec: - type: LoadBalancer + type: NodePort selector: app: backend-admin-app ports: - protocol: TCP port: 80 targetPort: 80 - nodePort: 30002 --- apiVersion: v1 kind: Service metadata: name: public-website-expose spec: - type: LoadBalancer + type: NodePort selector: app: public-website ports: - protocol: TCP port: 80 - targetPort: 80 - nodePort: 30003 \ No newline at end of file + targetPort: 80 \ No newline at end of file diff --git a/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj b/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj index 88671a50f4..972fbd9d31 100644 --- a/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj +++ b/samples/MicroserviceDemo/microservices/BloggingService.Host/BloggingService.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj b/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj index 810c69a198..bc11e9759f 100644 --- a/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj +++ b/samples/MicroserviceDemo/microservices/IdentityService.Host/IdentityService.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj b/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj index ce42a3f22e..4cfac32df9 100644 --- a/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj +++ b/samples/MicroserviceDemo/microservices/ProductService.Host/ProductService.Host.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/cs.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/cs.json new file mode 100644 index 0000000000..fb05fd06ac --- /dev/null +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/Localization/ApplicationContracts/cs.json @@ -0,0 +1,10 @@ +{ + "culture": "cs", + "texts": { + "Permission:ProductManagement": "Správa produktů", + "Permission:Products": "Produkty", + "Permission:Edit": "Upravit", + "Permission:Delete": "Smazat", + "Permission:Create": "Vytvořit" + } +} \ No newline at end of file diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementPermissions.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementPermissions.cs index b4dd949a73..eebc8cb29c 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementPermissions.cs +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Application.Contracts/ProductManagement/ProductManagementPermissions.cs @@ -1,4 +1,6 @@ -namespace ProductManagement +using Volo.Abp.Reflection; + +namespace ProductManagement { public class ProductManagementPermissions { @@ -14,14 +16,7 @@ } public static string[] GetAll() { - return new[] - { - GroupName, - Products.Default, - Products.Delete, - Products.Update, - Products.Create - }; + return ReflectionHelper.GetPublicConstantsRecursively(typeof(ProductManagementPermissions)); } } } \ No newline at end of file diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement.Domain.Shared.csproj b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement.Domain.Shared.csproj index e694ad7425..18c95e3826 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement.Domain.Shared.csproj +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement.Domain.Shared.csproj @@ -7,6 +7,7 @@ + diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/ProductStockCountChangedEto.cs b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement/ProductStockCountChangedEto.cs similarity index 100% rename from samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/ProductStockCountChangedEto.cs rename to samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain.Shared/ProductManagement/ProductStockCountChangedEto.cs diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement.Domain.csproj b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement.Domain.csproj index 217120e629..e6f75956c5 100644 --- a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement.Domain.csproj +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement.Domain.csproj @@ -6,7 +6,6 @@ - diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/cs.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/cs.json new file mode 100644 index 0000000000..6a060a54c1 --- /dev/null +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Domain/ProductManagement/Localization/Domain/cs.json @@ -0,0 +1,6 @@ +{ + "culture": "cs", + "texts": { + + } +} \ No newline at end of file diff --git a/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/cs.json b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/cs.json new file mode 100644 index 0000000000..489699d9a6 --- /dev/null +++ b/samples/MicroserviceDemo/modules/product/src/ProductManagement.Web/Localization/Resources/ProductManagement/cs.json @@ -0,0 +1,16 @@ +{ + "culture": "cs", + "texts": { + "Menu:ProductManagement": "Správa produktů", + "Menu:Products": "Produkty", + "ProductManagement": "Správa produktů", + "CreateANewProduct": "Vytvořit nový produkt", + "Products": "Produkty", + "StockCount": "Skladem", + "Code": "Kód", + "Name": "Název", + "Price": "Cena", + "ImageName": "Název obrázku", + "ProductDeletionWarningMessage": "Opravdu chcete smazat tento produkt?" + } +} \ No newline at end of file diff --git a/samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/ProductManagement.EntityFrameworkCore.Tests.csproj b/samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/ProductManagement.EntityFrameworkCore.Tests.csproj index d322f0e7b3..a5d1b1b349 100644 --- a/samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/ProductManagement.EntityFrameworkCore.Tests.csproj +++ b/samples/MicroserviceDemo/modules/product/test/ProductManagement.EntityFrameworkCore.Tests/ProductManagement.EntityFrameworkCore.Tests.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/templates/module/.gitignore b/templates/module/.gitignore deleted file mode 100644 index 1114901d9b..0000000000 --- a/templates/module/.gitignore +++ /dev/null @@ -1,255 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# MyProjectName -src/MyCompanyName.MyProjectName.Web/Logs/logs.txt diff --git a/templates/module/MyCompanyName.MyProjectName.sln b/templates/module/MyCompanyName.MyProjectName.sln deleted file mode 100644 index de0a97c8bc..0000000000 --- a/templates/module/MyCompanyName.MyProjectName.sln +++ /dev/null @@ -1,132 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2026 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Domain.Shared", "src\MyCompanyName.MyProjectName.Domain.Shared\MyCompanyName.MyProjectName.Domain.Shared.csproj", "{D64C1577-4929-4B60-939E-96DE1534891A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Domain", "src\MyCompanyName.MyProjectName.Domain\MyCompanyName.MyProjectName.Domain.csproj", "{F2840BC7-0188-4606-9126-DADD0F5ABF7A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Application.Contracts", "src\MyCompanyName.MyProjectName.Application.Contracts\MyCompanyName.MyProjectName.Application.Contracts.csproj", "{BD65D04F-08D5-40C1-8C24-77CA0BACB877}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Application", "src\MyCompanyName.MyProjectName.Application\MyCompanyName.MyProjectName.Application.csproj", "{78040F9E-3501-4A40-82DF-00A597710F35}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{649A3FFA-182F-4E56-9717-E6A9A2BEC545}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{E400416D-2895-4512-9D17-90681EEC7E0A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.EntityFrameworkCore", "src\MyCompanyName.MyProjectName.EntityFrameworkCore\MyCompanyName.MyProjectName.EntityFrameworkCore.csproj", "{0CE86223-D31D-4315-A1F5-87BA3EE1B844}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.MongoDB", "src\MyCompanyName.MyProjectName.MongoDB\MyCompanyName.MyProjectName.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.HttpApi", "src\MyCompanyName.MyProjectName.HttpApi\MyCompanyName.MyProjectName.HttpApi.csproj", "{077AA5F8-8B61-420C-A6B5-0150A66FDB34}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.HttpApi.Client", "src\MyCompanyName.MyProjectName.HttpApi.Client\MyCompanyName.MyProjectName.HttpApi.Client.csproj", "{36E2735F-CEAB-44C8-A6D1-2CDAFF399751}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Web", "src\MyCompanyName.MyProjectName.Web\MyCompanyName.MyProjectName.Web.csproj", "{8C5B0D35-9734-4439-977C-2F1AC9E1D69E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.TestBase", "test\MyCompanyName.MyProjectName.TestBase\MyCompanyName.MyProjectName.TestBase.csproj", "{6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Domain.Tests", "test\MyCompanyName.MyProjectName.Domain.Tests\MyCompanyName.MyProjectName.Domain.Tests.csproj", "{44FB6636-5427-415D-8883-CB7E42D548F2}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.EntityFrameworkCore.Tests", "test\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj", "{E5906DE1-B2F5-472E-BE1B-1D96A68B834D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.MongoDB.Tests", "test\MyCompanyName.MyProjectName.MongoDB.Tests\MyCompanyName.MyProjectName.MongoDB.Tests.csproj", "{AA783A34-86E4-41A5-AE21-5D9FBD98D858}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Application.Tests", "test\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj", "{13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyCompanyName.MyProjectName.DemoApp", "app\MyCompanyName.MyProjectName.DemoApp\MyCompanyName.MyProjectName.DemoApp.csproj", "{E7353C9A-4357-4A40-A39A-52B73F5A0CA1}" -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 - {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.Build.0 = Release|Any CPU - {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.Build.0 = Debug|Any CPU - {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.ActiveCfg = Release|Any CPU - {78040F9E-3501-4A40-82DF-00A597710F35}.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 - {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.Build.0 = Debug|Any CPU - {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.ActiveCfg = Release|Any CPU - {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.Build.0 = Release|Any CPU - {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.Build.0 = Debug|Any CPU - {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Release|Any CPU.ActiveCfg = Release|Any CPU - {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Release|Any CPU.Build.0 = Release|Any CPU - {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8C5B0D35-9734-4439-977C-2F1AC9E1D69E}.Release|Any CPU.Build.0 = Release|Any CPU - {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8}.Release|Any CPU.Build.0 = Release|Any CPU - {44FB6636-5427-415D-8883-CB7E42D548F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {44FB6636-5427-415D-8883-CB7E42D548F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {44FB6636-5427-415D-8883-CB7E42D548F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {44FB6636-5427-415D-8883-CB7E42D548F2}.Release|Any CPU.Build.0 = Release|Any CPU - {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E5906DE1-B2F5-472E-BE1B-1D96A68B834D}.Release|Any CPU.Build.0 = Release|Any CPU - {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA783A34-86E4-41A5-AE21-5D9FBD98D858}.Release|Any CPU.Build.0 = Release|Any CPU - {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264}.Release|Any CPU.Build.0 = Release|Any CPU - {E7353C9A-4357-4A40-A39A-52B73F5A0CA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7353C9A-4357-4A40-A39A-52B73F5A0CA1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7353C9A-4357-4A40-A39A-52B73F5A0CA1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7353C9A-4357-4A40-A39A-52B73F5A0CA1}.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} - {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} - {78040F9E-3501-4A40-82DF-00A597710F35} = {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} - {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} - {36E2735F-CEAB-44C8-A6D1-2CDAFF399751} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} - {8C5B0D35-9734-4439-977C-2F1AC9E1D69E} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} - {6E5B22E7-E2DB-45D4-B828-8019D8FD51E8} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} - {44FB6636-5427-415D-8883-CB7E42D548F2} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} - {E5906DE1-B2F5-472E-BE1B-1D96A68B834D} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} - {AA783A34-86E4-41A5-AE21-5D9FBD98D858} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} - {13A9EAD6-F3A4-4357-BA4A-A7E8FEB4A264} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} - {E7353C9A-4357-4A40-A39A-52B73F5A0CA1} = {E400416D-2895-4512-9D17-90681EEC7E0A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} - EndGlobalSection -EndGlobal diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppDbContext.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppDbContext.cs deleted file mode 100644 index 70d778b63d..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppDbContext.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using MyCompanyName.MyProjectName.EntityFrameworkCore; -using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.Identity.EntityFrameworkCore; -using Volo.Abp.PermissionManagement.EntityFrameworkCore; -using Volo.Abp.SettingManagement.EntityFrameworkCore; - -namespace MyCompanyName.MyProjectName.DemoApp -{ - public class DemoAppDbContext : AbpDbContext - { - public DemoAppDbContext(DbContextOptions options) - : base(options) - { - - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.ConfigurePermissionManagement(); - modelBuilder.ConfigureSettingManagement(); - modelBuilder.ConfigureIdentity(); - modelBuilder.ConfigureMyProjectName(); - } - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppDbContextFactory.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppDbContextFactory.cs deleted file mode 100644 index 117ac7f8d3..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppDbContextFactory.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.IO; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Design; -using Microsoft.Extensions.Configuration; - -namespace MyCompanyName.MyProjectName.DemoApp -{ - public class DemoAppDbContextFactory : IDesignTimeDbContextFactory - { - public DemoAppDbContext CreateDbContext(string[] args) - { - var configuration = BuildConfiguration(); - - var builder = new DbContextOptionsBuilder() - .UseSqlServer(configuration.GetConnectionString("Default")); - - return new DemoAppDbContext(builder.Options); - } - - private static IConfigurationRoot BuildConfiguration() - { - var builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false); - - return builder.Build(); - } - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppModule.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppModule.cs deleted file mode 100644 index 442ffa7d07..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/DemoAppModule.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using MyCompanyName.MyProjectName.EntityFrameworkCore; -using Swashbuckle.AspNetCore.Swagger; -using System.IO; -using System.Linq; -using Volo.Abp; -using Volo.Abp.Account.Web; -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; -using Volo.Abp.Authorization.Permissions; -using Volo.Abp.Autofac; -using Volo.Abp.Data; -using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.EntityFrameworkCore.SqlServer; -using Volo.Abp.Identity; -using Volo.Abp.Identity.EntityFrameworkCore; -using Volo.Abp.Identity.Web; -using Volo.Abp.Localization; -using Volo.Abp.Modularity; -using Volo.Abp.PermissionManagement; -using Volo.Abp.PermissionManagement.EntityFrameworkCore; -using Volo.Abp.PermissionManagement.Identity; -using Volo.Abp.SettingManagement.EntityFrameworkCore; -using Volo.Abp.Threading; -using Volo.Abp.VirtualFileSystem; - -namespace MyCompanyName.MyProjectName.DemoApp -{ - [DependsOn( - typeof(MyProjectNameWebModule), - typeof(MyProjectNameApplicationModule), - typeof(MyProjectNameEntityFrameworkCoreModule), - - typeof(AbpPermissionManagementEntityFrameworkCoreModule), - typeof(AbpSettingManagementEntityFrameworkCoreModule), - typeof(AbpIdentityEntityFrameworkCoreModule), - typeof(AbpEntityFrameworkCoreSqlServerModule), - - typeof(AbpAccountWebModule), - typeof(AbpIdentityWebModule), - typeof(AbpIdentityApplicationModule), - typeof(AbpPermissionManagementApplicationModule), - typeof(AbpPermissionManagementDomainIdentityModule), - typeof(AbpAutofacModule), - typeof(AbpAspNetCoreMvcUiBasicThemeModule) - )] - public class DemoAppModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - var hostingEnvironment = context.Services.GetHostingEnvironment(); - var configuration = context.Services.BuildConfiguration(); - - Configure(options => - { - options.ConnectionStrings.Default = configuration.GetConnectionString("Default"); - }); - - Configure(options => - { - options.UseSqlServer(); - }); - - if (hostingEnvironment.IsDevelopment()) - { - Configure(options => - { - options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Web", Path.DirectorySeparatorChar))); - options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Domain", Path.DirectorySeparatorChar))); - options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Application", Path.DirectorySeparatorChar))); - options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Application.Contracts", Path.DirectorySeparatorChar))); - }); - } - - context.Services.AddSwaggerGen( - options => - { - options.SwaggerDoc("v1", new Info { Title = "MyProjectName API", Version = "v1" }); - options.DocInclusionPredicate((docName, description) => true); - options.CustomSchemaIds(type => type.FullName); - }); - - Configure(options => - { - options.Languages.Add(new LanguageInfo("en", "en", "English")); - //...add other languages - }); - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - var app = context.GetApplicationBuilder(); - - if (context.GetEnvironment().IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseErrorPage(); - } - - app.UseVirtualFiles(); - - app.UseSwagger(); - app.UseSwaggerUI(options => - { - options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API"); - }); - - app.UseAuthentication(); - app.UseAbpRequestLocalization(); - app.UseAuditing(); - - app.UseMvcWithDefaultRouteAndArea(); - - using (var scope = context.ServiceProvider.CreateScope()) - { - AsyncHelper.RunSync(async () => - { - await scope.ServiceProvider - .GetRequiredService() - .SeedAsync(); - }); - } - } - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190410095748_Initial.Designer.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190410095748_Initial.Designer.cs deleted file mode 100644 index 9189ba1c41..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190410095748_Initial.Designer.cs +++ /dev/null @@ -1,421 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using MyCompanyName.MyProjectName.DemoApp; - -namespace MyCompanyName.MyProjectName.DemoApp.Migrations -{ - [DbContext(typeof(DemoAppDbContext))] - [Migration("20190410095748_Initial")] - partial class Initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("Description") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("Regex") - .HasMaxLength(512); - - b.Property("RegexDescription") - .HasMaxLength(128); - - b.Property("Required"); - - b.Property("ValueType"); - - b.HasKey("Id"); - - b.ToTable("AbpClaimTypes"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDefault") - .HasColumnName("IsDefault"); - - b.Property("IsPublic") - .HasColumnName("IsPublic"); - - b.Property("IsStatic") - .HasColumnName("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("NormalizedName") - .IsRequired() - .HasMaxLength(256); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName"); - - b.ToTable("AbpRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AbpRoleClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount") - .ValueGeneratedOnAdd() - .HasColumnName("AccessFailedCount") - .HasDefaultValue(0); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnName("DeletionTime"); - - b.Property("Email") - .HasColumnName("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("EmailConfirmed") - .HasDefaultValue(false); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnName("IsDeleted") - .HasDefaultValue(false); - - b.Property("LastModificationTime") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnName("LastModifierId"); - - b.Property("LockoutEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("LockoutEnabled") - .HasDefaultValue(false); - - b.Property("LockoutEnd"); - - b.Property("Name") - .HasColumnName("Name") - .HasMaxLength(64); - - b.Property("NormalizedEmail") - .HasColumnName("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .IsRequired() - .HasColumnName("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash") - .HasColumnName("PasswordHash") - .HasMaxLength(256); - - b.Property("PhoneNumber") - .HasColumnName("PhoneNumber") - .HasMaxLength(16); - - b.Property("PhoneNumberConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("PhoneNumberConfirmed") - .HasDefaultValue(false); - - b.Property("SecurityStamp") - .IsRequired() - .HasColumnName("SecurityStamp") - .HasMaxLength(256); - - b.Property("Surname") - .HasColumnName("Surname") - .HasMaxLength(64); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.Property("TwoFactorEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("TwoFactorEnabled") - .HasDefaultValue(false); - - b.Property("UserName") - .IsRequired() - .HasColumnName("UserName") - .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") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("TenantId"); - - b.Property("UserId"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AbpUserClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("ProviderDisplayName") - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(196); - - b.Property("TenantId"); - - b.HasKey("UserId", "LoginProvider"); - - b.HasIndex("LoginProvider", "ProviderKey"); - - b.ToTable("AbpUserLogins"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId", "UserId"); - - b.ToTable("AbpUserRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("Name") - .HasMaxLength(128); - - b.Property("TenantId"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AbpUserTokens"); - }); - - modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(64); - - b.Property("ProviderName") - .IsRequired() - .HasMaxLength(64); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpPermissionGrants"); - }); - - modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .HasMaxLength(64); - - b.Property("ProviderName") - .HasMaxLength(64); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2048); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpSettings"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany("Claims") - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Claims") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Logins") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Roles") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Tokens") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190410095748_Initial.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190410095748_Initial.cs deleted file mode 100644 index 53361cc99e..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/20190410095748_Initial.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace MyCompanyName.MyProjectName.DemoApp.Migrations -{ - public partial class Initial : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AbpClaimTypes", - columns: table => new - { - Id = table.Column(nullable: false), - ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), - Name = table.Column(maxLength: 256, nullable: false), - Required = table.Column(nullable: false), - IsStatic = table.Column(nullable: false), - Regex = table.Column(maxLength: 512, nullable: true), - RegexDescription = table.Column(maxLength: 128, nullable: true), - Description = table.Column(maxLength: 256, nullable: true), - ValueType = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpPermissionGrants", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - Name = table.Column(maxLength: 128, nullable: false), - ProviderName = table.Column(maxLength: 64, nullable: false), - ProviderKey = table.Column(maxLength: 64, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpRoles", - columns: table => new - { - Id = table.Column(nullable: false), - ExtraProperties = table.Column(nullable: true), - ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), - TenantId = table.Column(nullable: true), - Name = table.Column(maxLength: 256, nullable: false), - NormalizedName = table.Column(maxLength: 256, nullable: false), - IsDefault = table.Column(nullable: false), - IsStatic = table.Column(nullable: false), - IsPublic = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpSettings", - columns: table => new - { - Id = table.Column(nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - Value = table.Column(maxLength: 2048, nullable: false), - ProviderName = table.Column(maxLength: 64, nullable: true), - ProviderKey = table.Column(maxLength: 64, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpSettings", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpUsers", - 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), - UserName = table.Column(maxLength: 256, nullable: false), - NormalizedUserName = table.Column(maxLength: 256, nullable: false), - Name = table.Column(maxLength: 64, nullable: true), - Surname = table.Column(maxLength: 64, nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false, defaultValue: false), - PasswordHash = table.Column(maxLength: 256, nullable: true), - SecurityStamp = table.Column(maxLength: 256, nullable: false), - PhoneNumber = table.Column(maxLength: 16, nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false, defaultValue: false), - TwoFactorEnabled = table.Column(nullable: false, defaultValue: false), - LockoutEnd = table.Column(nullable: true), - LockoutEnabled = table.Column(nullable: false, defaultValue: false), - AccessFailedCount = table.Column(nullable: false, defaultValue: 0) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AbpRoleClaims", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - ClaimType = table.Column(maxLength: 256, nullable: false), - ClaimValue = table.Column(maxLength: 1024, nullable: true), - RoleId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AbpRoleClaims_AbpRoles_RoleId", - column: x => x.RoleId, - principalTable: "AbpRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserClaims", - columns: table => new - { - Id = table.Column(nullable: false), - TenantId = table.Column(nullable: true), - ClaimType = table.Column(maxLength: 256, nullable: false), - ClaimValue = table.Column(maxLength: 1024, nullable: true), - UserId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AbpUserClaims_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserLogins", - columns: table => new - { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 64, nullable: false), - TenantId = table.Column(nullable: true), - ProviderKey = table.Column(maxLength: 196, nullable: false), - ProviderDisplayName = table.Column(maxLength: 128, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserLogins", x => new { x.UserId, x.LoginProvider }); - table.ForeignKey( - name: "FK_AbpUserLogins_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserRoles", - columns: table => new - { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false), - TenantId = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AbpUserRoles_AbpRoles_RoleId", - column: x => x.RoleId, - principalTable: "AbpRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AbpUserRoles_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AbpUserTokens", - columns: table => new - { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(maxLength: 64, nullable: false), - Name = table.Column(maxLength: 128, nullable: false), - TenantId = table.Column(nullable: true), - Value = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AbpUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AbpUserTokens_AbpUsers_UserId", - column: x => x.UserId, - principalTable: "AbpUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", - table: "AbpPermissionGrants", - columns: new[] { "Name", "ProviderName", "ProviderKey" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpRoleClaims_RoleId", - table: "AbpRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpRoles_NormalizedName", - table: "AbpRoles", - column: "NormalizedName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpSettings_Name_ProviderName_ProviderKey", - table: "AbpSettings", - columns: new[] { "Name", "ProviderName", "ProviderKey" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserClaims_UserId", - table: "AbpUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserLogins_LoginProvider_ProviderKey", - table: "AbpUserLogins", - columns: new[] { "LoginProvider", "ProviderKey" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUserRoles_RoleId_UserId", - table: "AbpUserRoles", - columns: new[] { "RoleId", "UserId" }); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_Email", - table: "AbpUsers", - column: "Email"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_NormalizedEmail", - table: "AbpUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_NormalizedUserName", - table: "AbpUsers", - column: "NormalizedUserName"); - - migrationBuilder.CreateIndex( - name: "IX_AbpUsers_UserName", - table: "AbpUsers", - column: "UserName"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AbpClaimTypes"); - - migrationBuilder.DropTable( - name: "AbpPermissionGrants"); - - migrationBuilder.DropTable( - name: "AbpRoleClaims"); - - migrationBuilder.DropTable( - name: "AbpSettings"); - - migrationBuilder.DropTable( - name: "AbpUserClaims"); - - migrationBuilder.DropTable( - name: "AbpUserLogins"); - - migrationBuilder.DropTable( - name: "AbpUserRoles"); - - migrationBuilder.DropTable( - name: "AbpUserTokens"); - - migrationBuilder.DropTable( - name: "AbpRoles"); - - migrationBuilder.DropTable( - name: "AbpUsers"); - } - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs deleted file mode 100644 index b3474a1d80..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Migrations/DemoAppDbContextModelSnapshot.cs +++ /dev/null @@ -1,419 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using MyCompanyName.MyProjectName.DemoApp; - -namespace MyCompanyName.MyProjectName.DemoApp.Migrations -{ - [DbContext(typeof(DemoAppDbContext))] - partial class DemoAppDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("Description") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("Regex") - .HasMaxLength(512); - - b.Property("RegexDescription") - .HasMaxLength(128); - - b.Property("Required"); - - b.Property("ValueType"); - - b.HasKey("Id"); - - b.ToTable("AbpClaimTypes"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .IsRequired() - .HasColumnName("ConcurrencyStamp") - .HasMaxLength(256); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDefault") - .HasColumnName("IsDefault"); - - b.Property("IsPublic") - .HasColumnName("IsPublic"); - - b.Property("IsStatic") - .HasColumnName("IsStatic"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256); - - b.Property("NormalizedName") - .IsRequired() - .HasMaxLength(256); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName"); - - b.ToTable("AbpRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AbpRoleClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount") - .ValueGeneratedOnAdd() - .HasColumnName("AccessFailedCount") - .HasDefaultValue(0); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnName("ConcurrencyStamp"); - - b.Property("CreationTime") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnName("DeletionTime"); - - b.Property("Email") - .HasColumnName("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("EmailConfirmed") - .HasDefaultValue(false); - - b.Property("ExtraProperties") - .HasColumnName("ExtraProperties"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnName("IsDeleted") - .HasDefaultValue(false); - - b.Property("LastModificationTime") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnName("LastModifierId"); - - b.Property("LockoutEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("LockoutEnabled") - .HasDefaultValue(false); - - b.Property("LockoutEnd"); - - b.Property("Name") - .HasColumnName("Name") - .HasMaxLength(64); - - b.Property("NormalizedEmail") - .HasColumnName("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .IsRequired() - .HasColumnName("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash") - .HasColumnName("PasswordHash") - .HasMaxLength(256); - - b.Property("PhoneNumber") - .HasColumnName("PhoneNumber") - .HasMaxLength(16); - - b.Property("PhoneNumberConfirmed") - .ValueGeneratedOnAdd() - .HasColumnName("PhoneNumberConfirmed") - .HasDefaultValue(false); - - b.Property("SecurityStamp") - .IsRequired() - .HasColumnName("SecurityStamp") - .HasMaxLength(256); - - b.Property("Surname") - .HasColumnName("Surname") - .HasMaxLength(64); - - b.Property("TenantId") - .HasColumnName("TenantId"); - - b.Property("TwoFactorEnabled") - .ValueGeneratedOnAdd() - .HasColumnName("TwoFactorEnabled") - .HasDefaultValue(false); - - b.Property("UserName") - .IsRequired() - .HasColumnName("UserName") - .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") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType") - .IsRequired() - .HasMaxLength(256); - - b.Property("ClaimValue") - .HasMaxLength(1024); - - b.Property("TenantId"); - - b.Property("UserId"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AbpUserClaims"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("ProviderDisplayName") - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(196); - - b.Property("TenantId"); - - b.HasKey("UserId", "LoginProvider"); - - b.HasIndex("LoginProvider", "ProviderKey"); - - b.ToTable("AbpUserLogins"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.Property("TenantId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId", "UserId"); - - b.ToTable("AbpUserRoles"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider") - .HasMaxLength(64); - - b.Property("Name") - .HasMaxLength(128); - - b.Property("TenantId"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AbpUserTokens"); - }); - - modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .IsRequired() - .HasMaxLength(64); - - b.Property("ProviderName") - .IsRequired() - .HasMaxLength(64); - - b.Property("TenantId"); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpPermissionGrants"); - }); - - modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128); - - b.Property("ProviderKey") - .HasMaxLength(64); - - b.Property("ProviderName") - .HasMaxLength(64); - - b.Property("Value") - .IsRequired() - .HasMaxLength(2048); - - b.HasKey("Id"); - - b.HasIndex("Name", "ProviderName", "ProviderKey"); - - b.ToTable("AbpSettings"); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany("Claims") - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Claims") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Logins") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => - { - b.HasOne("Volo.Abp.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Roles") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => - { - b.HasOne("Volo.Abp.Identity.IdentityUser") - .WithMany("Tokens") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/MyCompanyName.MyProjectName.DemoApp.csproj b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/MyCompanyName.MyProjectName.DemoApp.csproj deleted file mode 100644 index 4e019c6d9b..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/MyCompanyName.MyProjectName.DemoApp.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - netcoreapp2.2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/Index.cshtml b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/Index.cshtml deleted file mode 100644 index 0532ccc90b..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/Index.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@page -@model MyCompanyName.MyProjectName.DemoApp.Pages.IndexModel -

    Welcome to the MyProjectName demo application.

    -Login \ No newline at end of file diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/Index.cshtml.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/Index.cshtml.cs deleted file mode 100644 index aae7b3c55a..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Pages/Index.cshtml.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace MyCompanyName.MyProjectName.DemoApp.Pages -{ - public class IndexModel : PageModel - { - public void OnGet() - { - } - } -} \ No newline at end of file diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Program.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Program.cs deleted file mode 100644 index a723fd53a8..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Program.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.IO; -using Microsoft.AspNetCore.Hosting; -using Serilog; -using Serilog.Events; - -namespace MyCompanyName.MyProjectName.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."); - BuildWebHostInternal(args).Run(); - return 0; - } - catch (Exception ex) - { - Log.Fatal(ex, "Host terminated unexpectedly!"); - return 1; - } - finally - { - Log.CloseAndFlush(); - } - } - - public static IWebHost BuildWebHostInternal(string[] args) => - new WebHostBuilder() - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseStartup() - .UseSerilog() - .Build(); - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Properties/launchSettings.json b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Properties/launchSettings.json deleted file mode 100644 index 9dcbdbe130..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:57891", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "MyCompanyName.MyProjectName.DemoApp": { - "commandName": "Project", - "launchBrowser": true, - "applicationUrl": "http://localhost:5000", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Startup.cs b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Startup.cs deleted file mode 100644 index 18fac8ac52..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/Startup.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Volo.Abp; - -namespace MyCompanyName.MyProjectName.DemoApp -{ - public class Startup - { - public IServiceProvider ConfigureServices(IServiceCollection services) - { - services.AddApplication(options => - { - options.UseAutofac(); - }); - - return services.BuildServiceProviderFromFactory(); - } - - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - app.InitializeApplication(); - } - } -} diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/appsettings.json b/templates/module/app/MyCompanyName.MyProjectName.DemoApp/appsettings.json deleted file mode 100644 index fc45293f2b..0000000000 --- a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "Default": "Server=localhost;Database=MyProjectNameDemoApp;Trusted_Connection=True;MultipleActiveResultSets=true" - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj b/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj deleted file mode 100644 index 317662c6ac..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName.MyProjectName.Application.Contracts.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/Localization/ApplicationContracts/zh-Hans.json b/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/Localization/ApplicationContracts/zh-Hans.json deleted file mode 100644 index 15d26f90a0..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/Localization/ApplicationContracts/zh-Hans.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "zh-Hans", - "texts": { - "Permission:MyProjectName": "MyProjectName" - } - } \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs b/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs deleted file mode 100644 index 84be3eb86f..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNameApplicationContractsModule.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using MyCompanyName.MyProjectName.Localization; -using Volo.Abp.Application; -using Volo.Abp.Authorization.Permissions; -using Volo.Abp.Localization; -using Volo.Abp.Modularity; -using Volo.Abp.VirtualFileSystem; - -namespace MyCompanyName.MyProjectName -{ - [DependsOn( - typeof(MyProjectNameDomainSharedModule), - typeof(AbpDddApplicationModule) - )] - public class MyProjectNameApplicationContractsModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.FileSets.AddEmbedded(); - }); - - Configure(options => - { - options.Resources - .Get() - .AddVirtualJson("/MyCompanyName/MyProjectName/Localization/ApplicationContracts"); - }); - } - } -} diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNamePermissionDefinitionProvider.cs b/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNamePermissionDefinitionProvider.cs deleted file mode 100644 index b4ff9e358d..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNamePermissionDefinitionProvider.cs +++ /dev/null @@ -1,19 +0,0 @@ -using MyCompanyName.MyProjectName.Localization; -using Volo.Abp.Authorization.Permissions; -using Volo.Abp.Localization; - -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNamePermissionDefinitionProvider : PermissionDefinitionProvider - { - public override void Define(IPermissionDefinitionContext context) - { - //var moduleGroup = context.AddGroup(MyProjectNamePermissions.GroupName, L("Permission:MyProjectName")); - } - - private static LocalizableString L(string name) - { - return LocalizableString.Create(name); - } - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNamePermissions.cs b/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNamePermissions.cs deleted file mode 100644 index 88e7a3c05b..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application.Contracts/MyCompanyName/MyProjectName/MyProjectNamePermissions.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNamePermissions - { - public const string GroupName = "MyProjectName"; - - public static string[] GetAll() - { - return new[] - { - GroupName - }; - } - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj b/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj deleted file mode 100644 index 03a3c8d193..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName.MyProjectName.Application.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationAutoMapperProfile.cs b/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationAutoMapperProfile.cs deleted file mode 100644 index 8a01585f69..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationAutoMapperProfile.cs +++ /dev/null @@ -1,12 +0,0 @@ -using AutoMapper; - -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNameApplicationAutoMapperProfile : Profile - { - public MyProjectNameApplicationAutoMapperProfile() - { - - } - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs b/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs deleted file mode 100644 index 891ff26af3..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameApplicationModule.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.AutoMapper; -using Volo.Abp.Modularity; -using Volo.Abp.Settings; - -namespace MyCompanyName.MyProjectName -{ - [DependsOn( - typeof(MyProjectNameDomainModule), - typeof(MyProjectNameApplicationContractsModule), - typeof(AbpAutoMapperModule) - )] - public class MyProjectNameApplicationModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.AddProfile(validate: true); - }); - } - } -} diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameSettingDefinitionProvider.cs b/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameSettingDefinitionProvider.cs deleted file mode 100644 index 0d5cee1168..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameSettingDefinitionProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Volo.Abp.Settings; - -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNameSettingDefinitionProvider : SettingDefinitionProvider - { - public override void Define(ISettingDefinitionContext context) - { - /* Define module settings here. - * Use names from MyProjectNameSettings class. - */ - } - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameSettings.cs b/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameSettings.cs deleted file mode 100644 index 5a50555ea5..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Application/MyCompanyName/MyProjectName/MyProjectNameSettings.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MyCompanyName.MyProjectName -{ - public static class MyProjectNameSettings - { - public const string GroupName = "MyProjectName"; - - /* Add constants for setting names. Example: - * public const string MySettingName = GroupName + ".MySettingName"; - */ - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj b/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj deleted file mode 100644 index 21e138f24b..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName.MyProjectName.Domain.Shared.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName/MyProjectName/MyProjectNameDomainErrorCodes.cs b/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName/MyProjectName/MyProjectNameDomainErrorCodes.cs deleted file mode 100644 index 55a2ed72bc..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName/MyProjectName/MyProjectNameDomainErrorCodes.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MyCompanyName.MyProjectName -{ - public static class MyProjectNameDomainErrorCodes - { - //Add your business exception error codes here... - } -} diff --git a/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName/MyProjectName/MyProjectNameDomainSharedModule.cs b/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName/MyProjectName/MyProjectNameDomainSharedModule.cs deleted file mode 100644 index 98c279b543..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Domain.Shared/MyCompanyName/MyProjectName/MyProjectNameDomainSharedModule.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Modularity; -using Volo.Abp.Localization; -using MyCompanyName.MyProjectName.Localization; - -namespace MyCompanyName.MyProjectName -{ - [DependsOn( - typeof(AbpLocalizationModule) - )] - public class MyProjectNameDomainSharedModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.Resources.Add("en"); - }); - } - } -} diff --git a/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj b/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj deleted file mode 100644 index 59e1709df7..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName/MyProjectName/Localization/Domain/zh-Hans.json b/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName/MyProjectName/Localization/Domain/zh-Hans.json deleted file mode 100644 index e4557f0ad2..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName/MyProjectName/Localization/Domain/zh-Hans.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "zh-Hans", - "texts": { - - } - } \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName/MyProjectName/MyProjectNameDomainModule.cs b/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName/MyProjectName/MyProjectNameDomainModule.cs deleted file mode 100644 index 81f815a022..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Domain/MyCompanyName/MyProjectName/MyProjectNameDomainModule.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using MyCompanyName.MyProjectName.Localization; -using Volo.Abp.Localization; -using Volo.Abp.Localization.ExceptionHandling; -using Volo.Abp.Modularity; -using Volo.Abp.VirtualFileSystem; - -namespace MyCompanyName.MyProjectName -{ - [DependsOn( - typeof(MyProjectNameDomainSharedModule) - )] - public class MyProjectNameDomainModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.FileSets.AddEmbedded(); - }); - - Configure(options => - { - options.Resources.Get().AddVirtualJson("/MyCompanyName/MyProjectName/Localization/Domain"); - }); - - Configure(options => - { - options.MapCodeNamespace("MyProjectName", typeof(MyProjectNameResource)); - }); - } - } -} diff --git a/templates/module/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj b/templates/module/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj deleted file mode 100644 index 0ca5581406..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName.MyProjectName.EntityFrameworkCore.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName/MyProjectName/EntityFrameworkCore/MyProjectNameDbContextModelCreatingExtensions.cs b/templates/module/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName/MyProjectName/EntityFrameworkCore/MyProjectNameDbContextModelCreatingExtensions.cs deleted file mode 100644 index ed7788dab7..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.EntityFrameworkCore/MyCompanyName/MyProjectName/EntityFrameworkCore/MyProjectNameDbContextModelCreatingExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore; -using Volo.Abp; - -namespace MyCompanyName.MyProjectName.EntityFrameworkCore -{ - public static class MyProjectNameDbContextModelCreatingExtensions - { - public static void ConfigureMyProjectName( - this ModelBuilder builder, - Action optionsAction = null) - { - Check.NotNull(builder, nameof(builder)); - - var options = new MyProjectNameModelBuilderConfigurationOptions(); - - optionsAction?.Invoke(options); - - /* Configure all entities here. Example: - - builder.Entity(b => - { - //Configure table & schema name - //b.ToTable(options.TablePrefix + "Questions", options.Schema); - - //Properties - //b.Property(q => q.Title).IsRequired().HasMaxLength(QuestionConsts.MaxTitleLength); - - //Configure relations - //b.HasMany(question => question.Tags).WithOne().HasForeignKey(qt => qt.QuestionId); - - //Configure indexes - //b.HasIndex(q => q.CreationTime); - }); - */ - } - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj b/templates/module/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj deleted file mode 100644 index c169b10e80..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.HttpApi.Client/MyCompanyName.MyProjectName.HttpApi.Client.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj b/templates/module/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj deleted file mode 100644 index 4050c826f6..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.HttpApi/MyCompanyName.MyProjectName.HttpApi.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj b/templates/module/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj deleted file mode 100644 index ec90deacf4..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.MongoDB/MyCompanyName.MyProjectName.MongoDB.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - netstandard2.0 - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.Web/Localization/Resources/MyProjectName/zh-Hans.json b/templates/module/src/MyCompanyName.MyProjectName.Web/Localization/Resources/MyProjectName/zh-Hans.json deleted file mode 100644 index e4557f0ad2..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Web/Localization/Resources/MyProjectName/zh-Hans.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "zh-Hans", - "texts": { - - } - } \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj b/templates/module/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj deleted file mode 100644 index 3eb58c5168..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - netstandard2.0 - $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; - true - Library - MyCompanyName.MyProjectName - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameMenuContributor.cs b/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameMenuContributor.cs deleted file mode 100644 index b844d86ae2..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameMenuContributor.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Threading.Tasks; -using Volo.Abp.UI.Navigation; - -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNameMenuContributor : IMenuContributor - { - public async Task ConfigureMenuAsync(MenuConfigurationContext context) - { - if (context.Menu.Name == StandardMenus.Main) - { - await ConfigureMainMenu(context); - } - } - - private Task ConfigureMainMenu(MenuConfigurationContext context) - { - //Add main menu items. - - return Task.CompletedTask; - } - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebAutoMapperProfile.cs b/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebAutoMapperProfile.cs deleted file mode 100644 index a0f14e727d..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebAutoMapperProfile.cs +++ /dev/null @@ -1,12 +0,0 @@ -using AutoMapper; - -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNameWebAutoMapperProfile : Profile - { - public MyProjectNameWebAutoMapperProfile() - { - //Create mappings. - } - } -} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs b/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs deleted file mode 100644 index ab6fd865bd..0000000000 --- a/templates/module/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Localization.Resources.AbpUi; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.DependencyInjection; -using MyCompanyName.MyProjectName.Localization; -using Volo.Abp.AspNetCore.Mvc.Localization; -using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; -using Volo.Abp.AutoMapper; -using Volo.Abp.Localization; -using Volo.Abp.Localization.Resources.AbpValidation; -using Volo.Abp.Modularity; -using Volo.Abp.UI.Navigation; -using Volo.Abp.VirtualFileSystem; - -namespace MyCompanyName.MyProjectName -{ - [DependsOn(typeof(MyProjectNameHttpApiModule))] - [DependsOn(typeof(AbpAspNetCoreMvcUiThemeSharedModule))] - [DependsOn(typeof(AbpAutoMapperModule))] - public class MyProjectNameWebModule : AbpModule - { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - context.Services.PreConfigure(options => - { - options.AddAssemblyResource(typeof(MyProjectNameResource), typeof(MyProjectNameWebModule).Assembly); - }); - } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.MenuContributors.Add(new MyProjectNameMenuContributor()); - }); - - Configure(options => - { - options.FileSets.AddEmbedded("MyCompanyName.MyProjectName"); - }); - - Configure(options => - { - options.Resources - .Get() - .AddBaseTypes( - typeof(AbpValidationResource), - typeof(AbpUiResource) - ).AddVirtualJson("/Localization/Resources/MyProjectName"); - }); - - Configure(options => - { - options.AddProfile(validate: true); - }); - - Configure(options => - { - //Configure authorization. - }); - } - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj b/templates/module/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj deleted file mode 100644 index 7451bac8bf..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.Application.Tests/MyCompanyName.MyProjectName.Application.Tests.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - netcoreapp2.2 - - - - - - - - - - - - - diff --git a/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj b/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj deleted file mode 100644 index fa8a0e6ae7..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName.MyProjectName.Domain.Tests.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - netcoreapp2.2 - - - - - - - - - - - - diff --git a/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName/MyProjectName/MyProjectNameDomainTestBase.cs b/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName/MyProjectName/MyProjectNameDomainTestBase.cs deleted file mode 100644 index 8065776e8d..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName/MyProjectName/MyProjectNameDomainTestBase.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MyCompanyName.MyProjectName -{ - public abstract class MyProjectNameDomainTestBase : MyProjectNameTestBase - { - - } -} \ No newline at end of file diff --git a/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName/MyProjectName/MyProjectNameDomainTestModule.cs b/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName/MyProjectName/MyProjectNameDomainTestModule.cs deleted file mode 100644 index 027e63b867..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.Domain.Tests/MyCompanyName/MyProjectName/MyProjectNameDomainTestModule.cs +++ /dev/null @@ -1,13 +0,0 @@ -using MyCompanyName.MyProjectName.EntityFrameworkCore; -using Volo.Abp.Modularity; - -namespace MyCompanyName.MyProjectName -{ - [DependsOn( - typeof(MyProjectNameEntityFrameworkCoreTestModule) - )] - public class MyProjectNameDomainTestModule : AbpModule - { - - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj b/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj deleted file mode 100644 index 495c869ba8..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - netcoreapp2.2 - - - - - - - - - - - - - - - - diff --git a/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName/MyProjectName/EntityFrameworkCore/MyEntityRepository_Tests.cs b/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName/MyProjectName/EntityFrameworkCore/MyEntityRepository_Tests.cs deleted file mode 100644 index 09111d2f39..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName/MyProjectName/EntityFrameworkCore/MyEntityRepository_Tests.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MyCompanyName.MyProjectName.EntityFrameworkCore -{ - public class MyEntityRepository_Tests : MyEntityRepository_Tests - { - - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName/MyProjectName/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreTestModule.cs b/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName/MyProjectName/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreTestModule.cs deleted file mode 100644 index 9c555de7a6..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.EntityFrameworkCore.Tests/MyCompanyName/MyProjectName/EntityFrameworkCore/MyProjectNameEntityFrameworkCoreTestModule.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.Modularity; - -namespace MyCompanyName.MyProjectName.EntityFrameworkCore -{ - [DependsOn( - typeof(MyProjectNameTestBaseModule), - typeof(MyProjectNameEntityFrameworkCoreModule) - )] - public class MyProjectNameEntityFrameworkCoreTestModule : 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 MyProjectNameDbContext( - new DbContextOptionsBuilder().UseSqlite(connection).Options - ).GetService().CreateTables(); - - return connection; - } - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj b/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj deleted file mode 100644 index 2592dd9973..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName.MyProjectName.MongoDB.Tests.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - netcoreapp2.2 - - - - - - - - - - - - - - diff --git a/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName/MyProjectName/MongoDB/MyEntityRepository_Tests.cs b/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName/MyProjectName/MongoDB/MyEntityRepository_Tests.cs deleted file mode 100644 index f797ff7067..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName/MyProjectName/MongoDB/MyEntityRepository_Tests.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MyCompanyName.MyProjectName.MongoDB -{ - public class MyEntityRepository_Tests : MyEntityRepository_Tests - { - - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName/MyProjectName/MongoDB/MyProjectNameMongoDbTestModule.cs b/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName/MyProjectName/MongoDB/MyProjectNameMongoDbTestModule.cs deleted file mode 100644 index e1cc8a627a..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.MongoDB.Tests/MyCompanyName/MyProjectName/MongoDB/MyProjectNameMongoDbTestModule.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Mongo2Go; -using Volo.Abp; -using Volo.Abp.Data; -using Volo.Abp.Modularity; - -namespace MyCompanyName.MyProjectName.MongoDB -{ - [DependsOn( - typeof(MyProjectNameTestBaseModule), - typeof(MyProjectNameMongoDbModule) - )] - public class MyProjectNameMongoDbTestModule : AbpModule - { - private MongoDbRunner _mongoDbRunner; - - public override void ConfigureServices(ServiceConfigurationContext context) - { - _mongoDbRunner = MongoDbRunner.Start(); - - Configure(options => - { - options.ConnectionStrings.Default = _mongoDbRunner.ConnectionString; - }); - } - - public override void OnApplicationShutdown(ApplicationShutdownContext context) - { - _mongoDbRunner.Dispose(); - } - } -} \ No newline at end of file diff --git a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj b/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj deleted file mode 100644 index 5c4d2085f9..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName.MyProjectName.TestBase.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - netcoreapp2.2 - - - - - - - - - - - - - - - - - - - - diff --git a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyEntityRepository_Tests.cs b/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyEntityRepository_Tests.cs deleted file mode 100644 index b3dfbda4ad..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyEntityRepository_Tests.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using Volo.Abp.Modularity; -using Xunit; - -namespace MyCompanyName.MyProjectName -{ - public abstract class MyEntityRepository_Tests : MyProjectNameTestBase - where TStartupModule : IAbpModule - { - [Fact] - public async Task Test1() - { - - } - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestBase.cs b/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestBase.cs deleted file mode 100644 index a7b4c4c57a..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestBase.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Volo.Abp; -using Volo.Abp.Modularity; - -namespace MyCompanyName.MyProjectName -{ - public abstract class MyProjectNameTestBase : AbpIntegratedTest - where TStartupModule : IAbpModule - { - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestBaseModule.cs b/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestBaseModule.cs deleted file mode 100644 index 3e03927dee..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestBaseModule.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp; -using Volo.Abp.Authorization; -using Volo.Abp.Autofac; -using Volo.Abp.Modularity; - -namespace MyCompanyName.MyProjectName -{ - [DependsOn( - typeof(AbpAutofacModule), - typeof(AbpTestBaseModule), - typeof(AbpAuthorizationModule), - typeof(MyProjectNameDomainModule) - )] - public class MyProjectNameTestBaseModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - context.Services.AddAlwaysAllowAuthorization(); - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - SeedTestData(context); - } - - private static void SeedTestData(ApplicationInitializationContext context) - { - using (var scope = context.ServiceProvider.CreateScope()) - { - scope.ServiceProvider - .GetRequiredService() - .Build(); - } - } - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestData.cs b/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestData.cs deleted file mode 100644 index 703ff643e4..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestData.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Volo.Abp.DependencyInjection; - -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNameTestData : ISingletonDependency - { - } -} diff --git a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestDataBuilder.cs b/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestDataBuilder.cs deleted file mode 100644 index 0449115b14..0000000000 --- a/templates/module/test/MyCompanyName.MyProjectName.TestBase/MyCompanyName/MyProjectName/MyProjectNameTestDataBuilder.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Volo.Abp.DependencyInjection; -using Volo.Abp.Guids; - -namespace MyCompanyName.MyProjectName -{ - public class MyProjectNameTestDataBuilder : ITransientDependency - { - private readonly IGuidGenerator _guidGenerator; - private MyProjectNameTestData _testData; - - public MyProjectNameTestDataBuilder( - IGuidGenerator guidGenerator, - MyProjectNameTestData testData) - { - _guidGenerator = guidGenerator; - _testData = testData; - } - - public void Build() - { - - } - } -} \ No newline at end of file diff --git a/templates/service/.gitattributes b/templates/mvc-module/.gitattributes similarity index 100% rename from templates/service/.gitattributes rename to templates/mvc-module/.gitattributes diff --git a/templates/mvc-module/.gitignore b/templates/mvc-module/.gitignore new file mode 100644 index 0000000000..09f6d97377 --- /dev/null +++ b/templates/mvc-module/.gitignore @@ -0,0 +1,258 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# MyProjectName +host/MyCompanyName.MyProjectName.IdentityServer/Logs/logs.txt +host/MyCompanyName.MyProjectName.HttpApi.Host/Logs/logs.txt +host/MyCompanyName.MyProjectName.Web.Host/Logs/logs.txt +host/MyCompanyName.MyProjectName.Web.Unified/Logs/logs.txt diff --git a/templates/mvc-module/MyCompanyName.MyProjectName.sln b/templates/mvc-module/MyCompanyName.MyProjectName.sln new file mode 100644 index 0000000000..9ca4ac6631 --- /dev/null +++ b/templates/mvc-module/MyCompanyName.MyProjectName.sln @@ -0,0 +1,167 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Domain.Shared", "src\MyCompanyName.MyProjectName.Domain.Shared\MyCompanyName.MyProjectName.Domain.Shared.csproj", "{D64C1577-4929-4B60-939E-96DE1534891A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Domain", "src\MyCompanyName.MyProjectName.Domain\MyCompanyName.MyProjectName.Domain.csproj", "{F2840BC7-0188-4606-9126-DADD0F5ABF7A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Application.Contracts", "src\MyCompanyName.MyProjectName.Application.Contracts\MyCompanyName.MyProjectName.Application.Contracts.csproj", "{BD65D04F-08D5-40C1-8C24-77CA0BACB877}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Application", "src\MyCompanyName.MyProjectName.Application\MyCompanyName.MyProjectName.Application.csproj", "{78040F9E-3501-4A40-82DF-00A597710F35}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{649A3FFA-182F-4E56-9717-E6A9A2BEC545}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{E400416D-2895-4512-9D17-90681EEC7E0A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.EntityFrameworkCore", "src\MyCompanyName.MyProjectName.EntityFrameworkCore\MyCompanyName.MyProjectName.EntityFrameworkCore.csproj", "{0CE86223-D31D-4315-A1F5-87BA3EE1B844}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.MongoDB", "src\MyCompanyName.MyProjectName.MongoDB\MyCompanyName.MyProjectName.MongoDB.csproj", "{F1C58097-4C08-4D88-8976-6B3389391481}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.HttpApi", "src\MyCompanyName.MyProjectName.HttpApi\MyCompanyName.MyProjectName.HttpApi.csproj", "{077AA5F8-8B61-420C-A6B5-0150A66FDB34}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.HttpApi.Client", "src\MyCompanyName.MyProjectName.HttpApi.Client\MyCompanyName.MyProjectName.HttpApi.Client.csproj", "{36E2735F-CEAB-44C8-A6D1-2CDAFF399751}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.TestBase", "test\MyCompanyName.MyProjectName.TestBase\MyCompanyName.MyProjectName.TestBase.csproj", "{C5BB573D-3030-4BCB-88B7-F6A85C32766C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.EntityFrameworkCore.Tests", "test\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj", "{527F645C-C1FC-406E-8479-81386C8ECF13}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.MongoDB.Tests", "test\MyCompanyName.MyProjectName.MongoDB.Tests\MyCompanyName.MyProjectName.MongoDB.Tests.csproj", "{D0AD9179-125C-40B2-A8EE-CD4C1EE24BB6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Domain.Tests", "test\MyCompanyName.MyProjectName.Domain.Tests\MyCompanyName.MyProjectName.Domain.Tests.csproj", "{E60895E5-79C4-447D-88B7-85CB5BA336A4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Application.Tests", "test\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj", "{90CB5DC4-C040-45C7-8900-9688B26405BC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.HttpApi.Host", "host\MyCompanyName.MyProjectName.HttpApi.Host\MyCompanyName.MyProjectName.HttpApi.Host.csproj", "{37B135B0-DAFE-4616-B25C-1BDF32FC44A2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Web", "src\MyCompanyName.MyProjectName.Web\MyCompanyName.MyProjectName.Web.csproj", "{3B7B6317-1B85-4164-8E11-75574F80AE17}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp", "test\MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp\MyCompanyName.MyProjectName.HttpApi.Client.ConsoleTestApp.csproj", "{1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Web.Host", "host\MyCompanyName.MyProjectName.Web.Host\MyCompanyName.MyProjectName.Web.Host.csproj", "{73513786-B6C6-4A21-89C5-0FBDD0A46107}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.IdentityServer", "host\MyCompanyName.MyProjectName.IdentityServer\MyCompanyName.MyProjectName.IdentityServer.csproj", "{690203F4-3CD5-4569-88D9-EE831EEA5F5F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Host.Shared", "host\MyCompanyName.MyProjectName.Host.Shared\MyCompanyName.MyProjectName.Host.Shared.csproj", "{F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyCompanyName.MyProjectName.Web.Unified", "host\MyCompanyName.MyProjectName.Web.Unified\MyCompanyName.MyProjectName.Web.Unified.csproj", "{3D872C41-E226-45C8-89C1-9D3DBD7C73F2}" +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 + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD65D04F-08D5-40C1-8C24-77CA0BACB877}.Release|Any CPU.Build.0 = Release|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78040F9E-3501-4A40-82DF-00A597710F35}.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 + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {077AA5F8-8B61-420C-A6B5-0150A66FDB34}.Release|Any CPU.Build.0 = Release|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751}.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 + {90CB5DC4-C040-45C7-8900-9688B26405BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90CB5DC4-C040-45C7-8900-9688B26405BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90CB5DC4-C040-45C7-8900-9688B26405BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90CB5DC4-C040-45C7-8900-9688B26405BC}.Release|Any CPU.Build.0 = Release|Any CPU + {37B135B0-DAFE-4616-B25C-1BDF32FC44A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37B135B0-DAFE-4616-B25C-1BDF32FC44A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37B135B0-DAFE-4616-B25C-1BDF32FC44A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37B135B0-DAFE-4616-B25C-1BDF32FC44A2}.Release|Any CPU.Build.0 = Release|Any CPU + {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 + {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8}.Release|Any CPU.Build.0 = Release|Any CPU + {73513786-B6C6-4A21-89C5-0FBDD0A46107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73513786-B6C6-4A21-89C5-0FBDD0A46107}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73513786-B6C6-4A21-89C5-0FBDD0A46107}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73513786-B6C6-4A21-89C5-0FBDD0A46107}.Release|Any CPU.Build.0 = Release|Any CPU + {690203F4-3CD5-4569-88D9-EE831EEA5F5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {690203F4-3CD5-4569-88D9-EE831EEA5F5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {690203F4-3CD5-4569-88D9-EE831EEA5F5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {690203F4-3CD5-4569-88D9-EE831EEA5F5F}.Release|Any CPU.Build.0 = Release|Any CPU + {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB}.Release|Any CPU.Build.0 = Release|Any CPU + {3D872C41-E226-45C8-89C1-9D3DBD7C73F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D872C41-E226-45C8-89C1-9D3DBD7C73F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D872C41-E226-45C8-89C1-9D3DBD7C73F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D872C41-E226-45C8-89C1-9D3DBD7C73F2}.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} + {BD65D04F-08D5-40C1-8C24-77CA0BACB877} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {78040F9E-3501-4A40-82DF-00A597710F35} = {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} + {077AA5F8-8B61-420C-A6B5-0150A66FDB34} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {36E2735F-CEAB-44C8-A6D1-2CDAFF399751} = {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} + {90CB5DC4-C040-45C7-8900-9688B26405BC} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {37B135B0-DAFE-4616-B25C-1BDF32FC44A2} = {E400416D-2895-4512-9D17-90681EEC7E0A} + {3B7B6317-1B85-4164-8E11-75574F80AE17} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545} + {1EDCD6D4-DF3A-4E3B-ABB6-C0D0B373EAB8} = {CCD2960C-23CC-4AB4-B84D-60C7AAA52F4D} + {73513786-B6C6-4A21-89C5-0FBDD0A46107} = {E400416D-2895-4512-9D17-90681EEC7E0A} + {690203F4-3CD5-4569-88D9-EE831EEA5F5F} = {E400416D-2895-4512-9D17-90681EEC7E0A} + {F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB} = {E400416D-2895-4512-9D17-90681EEC7E0A} + {3D872C41-E226-45C8-89C1-9D3DBD7C73F2} = {E400416D-2895-4512-9D17-90681EEC7E0A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD} + EndGlobalSection +EndGlobal diff --git a/templates/module/MyCompanyName.MyProjectName.sln.DotSettings b/templates/mvc-module/MyCompanyName.MyProjectName.sln.DotSettings similarity index 100% rename from templates/module/MyCompanyName.MyProjectName.sln.DotSettings rename to templates/mvc-module/MyCompanyName.MyProjectName.sln.DotSettings diff --git a/templates/module/common.props b/templates/mvc-module/common.props similarity index 100% rename from templates/module/common.props rename to templates/mvc-module/common.props diff --git a/templates/service/database/Dockerfile b/templates/mvc-module/database/Dockerfile similarity index 100% rename from templates/service/database/Dockerfile rename to templates/mvc-module/database/Dockerfile diff --git a/templates/service/database/entrypoint.sh b/templates/mvc-module/database/entrypoint.sh similarity index 100% rename from templates/service/database/entrypoint.sh rename to templates/mvc-module/database/entrypoint.sh diff --git a/templates/service/docker-compose.migrations.yml b/templates/mvc-module/docker-compose.migrations.yml similarity index 100% rename from templates/service/docker-compose.migrations.yml rename to templates/mvc-module/docker-compose.migrations.yml diff --git a/templates/service/docker-compose.override.yml b/templates/mvc-module/docker-compose.override.yml similarity index 100% rename from templates/service/docker-compose.override.yml rename to templates/mvc-module/docker-compose.override.yml diff --git a/templates/service/docker-compose.yml b/templates/mvc-module/docker-compose.yml similarity index 100% rename from templates/service/docker-compose.yml rename to templates/mvc-module/docker-compose.yml diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.Host.Shared/MultiTenancy/MultiTenancyConsts.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.Host.Shared/MultiTenancy/MultiTenancyConsts.cs new file mode 100644 index 0000000000..0c81fd8a49 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.Host.Shared/MultiTenancy/MultiTenancyConsts.cs @@ -0,0 +1,10 @@ +namespace MyCompanyName.MyProjectName.MultiTenancy +{ + public static class MultiTenancyConsts + { + /* Enable/disable multi-tenancy in a single point + * to test your module with multi-tenancy. + */ + public const bool IsEnabled = false; + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.Host.Shared/MyCompanyName.MyProjectName.Host.Shared.csproj b/templates/mvc-module/host/MyCompanyName.MyProjectName.Host.Shared/MyCompanyName.MyProjectName.Host.Shared.csproj new file mode 100644 index 0000000000..9b5949c5fe --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.Host.Shared/MyCompanyName.MyProjectName.Host.Shared.csproj @@ -0,0 +1,10 @@ + + + + + + netstandard2.0 + MyCompanyName.MyProjectName + + + diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Controllers/HomeController.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Controllers/HomeController.cs new file mode 100644 index 0000000000..66ab9f58d0 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Controllers/HomeController.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc; + +namespace MyCompanyName.MyProjectName.Controllers +{ + public class HomeController : AbpController + { + public ActionResult Index() + { + return Redirect("/swagger"); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Dockerfile b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Dockerfile new file mode 100644 index 0000000000..827af3a36f --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Dockerfile @@ -0,0 +1,18 @@ +FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/dotnet:2.2-sdk AS build +WORKDIR /src +COPY . . +WORKDIR /src/templates/service/host/MyCompanyName.MyProjectName.HttpApi.Host +RUN dotnet restore -nowarn:msb3202,nu1503 +RUN dotnet build --no-restore -c Release -o /app + +FROM build AS publish +RUN dotnet publish --no-restore -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "MyCompanyName.MyProjectName.HttpApi.Host.dll"] diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/EntityFrameworkCore/MyProjectHttpApiHostMigrationsDbContext.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/EntityFrameworkCore/MyProjectHttpApiHostMigrationsDbContext.cs new file mode 100644 index 0000000000..1e3386b031 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/EntityFrameworkCore/MyProjectHttpApiHostMigrationsDbContext.cs @@ -0,0 +1,21 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public class MyProjectHttpApiHostMigrationsDbContext : AbpDbContext + { + public MyProjectHttpApiHostMigrationsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigureMyProjectName(); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/EntityFrameworkCore/MyProjectHttpApiHostMigrationsDbContextFactory.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/EntityFrameworkCore/MyProjectHttpApiHostMigrationsDbContextFactory.cs new file mode 100644 index 0000000000..8b83ae0091 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/EntityFrameworkCore/MyProjectHttpApiHostMigrationsDbContextFactory.cs @@ -0,0 +1,29 @@ +using System.IO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public class MyProjectHttpApiHostMigrationsDbContextFactory : IDesignTimeDbContextFactory + { + public MyProjectHttpApiHostMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); + + return new MyProjectHttpApiHostMigrationsDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj new file mode 100644 index 0000000000..9e51bd05cf --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/MyCompanyName.MyProjectName.HttpApi.Host.csproj @@ -0,0 +1,48 @@ + + + + + + netcoreapp2.2 + MyCompanyName.MyProjectName + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + signed + + + + + diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs new file mode 100644 index 0000000000..2a330c49a8 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/MyProjectNameHttpApiHostModule.cs @@ -0,0 +1,129 @@ +using System.IO; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using MyCompanyName.MyProjectName.EntityFrameworkCore; +using MyCompanyName.MyProjectName.MultiTenancy; +using StackExchange.Redis; +using Swashbuckle.AspNetCore.Swagger; +using Volo.Abp; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.Autofac; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.SqlServer; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.MultiTenancy; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.VirtualFileSystem; + +namespace MyCompanyName.MyProjectName +{ + [DependsOn( + typeof(MyProjectNameApplicationModule), + typeof(MyProjectNameEntityFrameworkCoreModule), + typeof(MyProjectNameHttpApiModule), + typeof(AbpAspNetCoreMultiTenancyModule), + typeof(AbpAutofacModule), + typeof(AbpEntityFrameworkCoreSqlServerModule), + typeof(AbpAuditLoggingEntityFrameworkCoreModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule) + )] + public class MyProjectNameHttpApiHostModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.BuildConfiguration(); + + Configure(options => + { + options.UseSqlServer(); + }); + + Configure(options => + { + options.IsEnabled = MultiTenancyConsts.IsEnabled; + }); + + if (hostingEnvironment.IsDevelopment()) + { + Configure(options => + { + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Domain.Shared", Path.DirectorySeparatorChar))); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Domain", Path.DirectorySeparatorChar))); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Application.Contracts", Path.DirectorySeparatorChar))); + options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}MyCompanyName.MyProjectName.Application", Path.DirectorySeparatorChar))); + }); + } + + context.Services.AddSwaggerGen( + options => + { + options.SwaggerDoc("v1", new Info { Title = "MyProjectName API", Version = "v1" }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + }); + + Configure(options => + { + options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština")); + options.Languages.Add(new LanguageInfo("en", "en", "English")); + options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português")); + options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe")); + options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + }); + + context.Services.AddAuthentication("Bearer") + .AddIdentityServerAuthentication(options => + { + options.Authority = configuration["AuthServer:Authority"]; + options.RequireHttpsMetadata = false; + options.ApiName = "MyProjectName"; + }); + + context.Services.AddDistributedRedisCache(options => + { + options.Configuration = configuration["Redis:Configuration"]; + }); + + if (!hostingEnvironment.IsDevelopment()) + { + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); + context.Services + .AddDataProtection() + .PersistKeysToStackExchangeRedis(redis, "MyProjectName-Protection-Keys"); + } + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + + if (!context.GetEnvironment().IsDevelopment()) + { + app.UseHsts(); + } + app.UseHttpsRedirection(); + app.UseCorrelationId(); + app.UseVirtualFiles(); + app.UseAuthentication(); + if (MultiTenancyConsts.IsEnabled) + { + app.UseMultiTenancy(); + } + app.UseAbpRequestLocalization(); + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API"); + }); + app.UseAuditing(); + app.UseMvcWithDefaultRouteAndArea(); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Program.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Program.cs new file mode 100644 index 0000000000..22b560f34f --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Program.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; +using Microsoft.AspNetCore.Hosting; +using Serilog; +using Serilog.Events; + +namespace MyCompanyName.MyProjectName +{ + public class Program + { + public static int Main(string[] args) + { + Log.Logger = new LoggerConfiguration() +#if DEBUG + .MinimumLevel.Debug() +#else + .MinimumLevel.Information() +#endif + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.File("Logs/logs.txt") + .CreateLogger(); + + try + { + Log.Information("Starting web host."); + BuildWebHostInternal(args).Run(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IWebHost BuildWebHostInternal(string[] args) => + new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .UseSerilog() + .Build(); + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Properties/launchSettings.json b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Properties/launchSettings.json new file mode 100644 index 0000000000..469b617f37 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57992", + "sslPort": 44300 //44300 to 44399 (inclusive) + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "MyCompanyName.MyProjectName.DemoApp": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Startup.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Startup.cs new file mode 100644 index 0000000000..19fe569013 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/Startup.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Volo.Abp; + +namespace MyCompanyName.MyProjectName +{ + public class Startup + { + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddApplication(options => + { + options.UseAutofac(); + }); + + return services.BuildServiceProviderFromFactory(); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + app.InitializeApplication(); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/appsettings.json b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/appsettings.json new file mode 100644 index 0000000000..9ee790cc41 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/appsettings.json @@ -0,0 +1,12 @@ +{ + "ConnectionStrings": { + "Default": "Server=localhost;Database=MyProjectName_Main;Trusted_Connection=True;MultipleActiveResultSets=true", + "MyProjectName": "Server=localhost;Database=MyProjectName_Module;Trusted_Connection=True;MultipleActiveResultSets=true" + }, + "Redis": { + "Configuration": "127.0.0.1" + }, + "AuthServer": { + "Authority": "https://localhost:44301/" + } +} \ No newline at end of file diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/yarn.lock b/templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/yarn.lock similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/yarn.lock rename to templates/mvc-module/host/MyCompanyName.MyProjectName.HttpApi.Host/yarn.lock diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Dockerfile b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Dockerfile new file mode 100644 index 0000000000..2c5f24183d --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Dockerfile @@ -0,0 +1,18 @@ +FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base +WORKDIR /app +EXPOSE 80 + +FROM microsoft/dotnet:2.2-sdk AS build +WORKDIR /src +COPY . . +WORKDIR /src/templates/service/host/MyCompanyName.MyProjectName.IdentityServer +RUN dotnet restore -nowarn:msb3202,nu1503 +RUN dotnet build --no-restore -c Release -o /app + +FROM build AS publish +RUN dotnet publish --no-restore -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "MyCompanyName.MyProjectName.IdentityServer.dll"] diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContext.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContext.cs new file mode 100644 index 0000000000..9b54d89be4 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContext.cs @@ -0,0 +1,32 @@ +using Microsoft.EntityFrameworkCore; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.IdentityServer.EntityFrameworkCore; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.TenantManagement.EntityFrameworkCore; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public class IdentityServerHostMigrationsDbContext : AbpDbContext + { + public IdentityServerHostMigrationsDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ConfigurePermissionManagement(); + modelBuilder.ConfigureSettingManagement(); + modelBuilder.ConfigureAuditLogging(); + modelBuilder.ConfigureIdentity(); + modelBuilder.ConfigureIdentityServer(); + modelBuilder.ConfigureTenantManagement(); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContextFactory.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContextFactory.cs new file mode 100644 index 0000000000..4722baae4c --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/EntityFrameworkCore/IdentityServerHostMigrationsDbContextFactory.cs @@ -0,0 +1,29 @@ +using System.IO; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; + +namespace MyCompanyName.MyProjectName.EntityFrameworkCore +{ + public class IdentityServerHostMigrationsDbContextFactory : IDesignTimeDbContextFactory + { + public IdentityServerHostMigrationsDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); + + return new IdentityServerHostMigrationsDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/IdentityServer/IdentityServerDataSeedContributor.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/IdentityServer/IdentityServerDataSeedContributor.cs new file mode 100644 index 0000000000..0c6a66bcb3 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/IdentityServer/IdentityServerDataSeedContributor.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Configuration; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.IdentityServer.ApiResources; +using Volo.Abp.IdentityServer.Clients; +using Volo.Abp.IdentityServer.IdentityResources; +using Volo.Abp.PermissionManagement; +using Volo.Abp.Uow; + +namespace MyCompanyName.MyProjectName.IdentityServer +{ + public class IdentityServerDataSeedContributor : IDataSeedContributor, ITransientDependency + { + private readonly IApiResourceRepository _apiResourceRepository; + private readonly IClientRepository _clientRepository; + private readonly IIdentityResourceDataSeeder _identityResourceDataSeeder; + private readonly IGuidGenerator _guidGenerator; + private readonly IPermissionDataSeeder _permissionDataSeeder; + private readonly IConfigurationAccessor _configurationAccessor; + + public IdentityServerDataSeedContributor( + IClientRepository clientRepository, + IApiResourceRepository apiResourceRepository, + IIdentityResourceDataSeeder identityResourceDataSeeder, + IGuidGenerator guidGenerator, + IPermissionDataSeeder permissionDataSeeder, + IConfigurationAccessor configurationAccessor) + { + _clientRepository = clientRepository; + _apiResourceRepository = apiResourceRepository; + _identityResourceDataSeeder = identityResourceDataSeeder; + _guidGenerator = guidGenerator; + _permissionDataSeeder = permissionDataSeeder; + _configurationAccessor = configurationAccessor; + } + + [UnitOfWork] + public virtual async Task SeedAsync(DataSeedContext context) + { + await _identityResourceDataSeeder.CreateStandardResourcesAsync(); + await CreateApiResourcesAsync(); + await CreateClientsAsync(); + } + + private async Task CreateApiResourcesAsync() + { + var commonApiUserClaims = new[] + { + "email", + "email_verified", + "name", + "phone_number", + "phone_number_verified", + "role" + }; + + await CreateApiResourceAsync("MyProjectName", commonApiUserClaims); + } + + private async Task CreateApiResourceAsync(string name, IEnumerable claims) + { + var apiResource = await _apiResourceRepository.FindByNameAsync(name); + if (apiResource == null) + { + apiResource = await _apiResourceRepository.InsertAsync( + new ApiResource( + _guidGenerator.Create(), + name, + name + " API" + ), + autoSave: true + ); + } + + foreach (var claim in claims) + { + if (apiResource.FindClaim(claim) == null) + { + apiResource.AddUserClaim(claim); + } + } + + return await _apiResourceRepository.UpdateAsync(apiResource); + } + + private async Task CreateClientsAsync() + { + const string commonSecret = "E5Xd4yMqjP5kjWFKrYgySBju6JVfCzMyFp7n2QmMrME="; + + var commonScopes = new[] + { + "email", + "openid", + "profile", + "role", + "phone", + "address", + "MyProjectName" + }; + + var configurationSection = _configurationAccessor.Configuration.GetSection("IdentityServer:Clients"); + + //Web Client + var webClientId = configurationSection["MyProjectName_Web:ClientId"]; + if (!webClientId.IsNullOrWhiteSpace()) + { + var webClientRootUrl = configurationSection["MyProjectName_Web:RootUrl"].EnsureEndsWith('/'); + await CreateClientAsync( + webClientId, + commonScopes, + new[] { "hybrid" }, + commonSecret, + redirectUri: $"{webClientRootUrl}signin-oidc", + postLogoutRedirectUri: $"{webClientRootUrl}signout-callback-oidc" + ); + } + + //Console Test Client + var consoleClientId = configurationSection["MyProjectName_ConsoleTestApp:ClientId"]; + if (!consoleClientId.IsNullOrWhiteSpace()) + { + await CreateClientAsync( + consoleClientId, + commonScopes, + new[] { "password", "client_credentials" }, + commonSecret + ); + } + } + + private async Task CreateClientAsync( + string name, + IEnumerable scopes, + IEnumerable grantTypes, + string secret, + string redirectUri = null, + string postLogoutRedirectUri = null, + IEnumerable permissions = null) + { + var client = await _clientRepository.FindByCliendIdAsync(name); + if (client == null) + { + client = await _clientRepository.InsertAsync( + new Client( + _guidGenerator.Create(), + name + ) + { + ClientName = name, + ProtocolType = "oidc", + Description = name, + AlwaysIncludeUserClaimsInIdToken = true, + AllowOfflineAccess = true, + AbsoluteRefreshTokenLifetime = 31536000, //365 days + AccessTokenLifetime = 31536000, //365 days + AuthorizationCodeLifetime = 300, + IdentityTokenLifetime = 300, + RequireConsent = false + }, + autoSave: true + ); + } + + foreach (var scope in scopes) + { + if (client.FindScope(scope) == null) + { + client.AddScope(scope); + } + } + + foreach (var grantType in grantTypes) + { + if (client.FindGrantType(grantType) == null) + { + client.AddGrantType(grantType); + } + } + + if (client.FindSecret(secret) == null) + { + client.AddSecret(secret); + } + + if (redirectUri != null) + { + if (client.FindRedirectUri(redirectUri) == null) + { + client.AddRedirectUri(redirectUri); + } + } + + if (postLogoutRedirectUri != null) + { + if (client.FindPostLogoutRedirectUri(postLogoutRedirectUri) == null) + { + client.AddPostLogoutRedirectUri(postLogoutRedirectUri); + } + } + + if (permissions != null) + { + await _permissionDataSeeder.SeedAsync( + ClientPermissionValueProvider.ProviderName, + name, + permissions + ); + } + + return await _clientRepository.UpdateAsync(client); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20190527125607_Initial.Designer.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20190527125607_Initial.Designer.cs new file mode 100644 index 0000000000..0c388d5430 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20190527125607_Initial.Designer.cs @@ -0,0 +1,1304 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using MyCompanyName.MyProjectName.EntityFrameworkCore; + +namespace MyCompanyName.MyProjectName.Migrations +{ + [DbContext(typeof(IdentityServerHostMigrationsDbContext))] + [Migration("20190527125607_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TenantName"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTenantId"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .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") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("DisplayName") + .HasMaxLength(128); + + b.Property("Emphasize"); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("ApiResourceId", "Name"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Name", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AbsoluteRefreshTokenLifetime"); + + b.Property("AccessTokenLifetime"); + + b.Property("AccessTokenType"); + + b.Property("AllowAccessTokensViaBrowser"); + + b.Property("AllowOfflineAccess"); + + b.Property("AllowPlainTextPkce"); + + b.Property("AllowRememberConsent"); + + b.Property("AlwaysIncludeUserClaimsInIdToken"); + + b.Property("AlwaysSendClientClaims"); + + b.Property("AuthorizationCodeLifetime"); + + b.Property("BackChannelLogoutSessionRequired"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(300); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ClientName") + .HasMaxLength(200); + + b.Property("ClientUri") + .HasMaxLength(300); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("EnableLocalLogin"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(300); + + b.Property("IdentityTokenLifetime"); + + b.Property("IncludeJwtId"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(300); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200); + + b.Property("RefreshTokenExpiration"); + + b.Property("RefreshTokenUsage"); + + b.Property("RequireClientSecret"); + + b.Property("RequireConsent"); + + b.Property("RequirePkce"); + + b.Property("SlidingRefreshTokenLifetime"); + + b.Property("UpdateAccessTokenClaimsOnRefresh"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(250); + + b.Property("Value") + .HasMaxLength(250); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId"); + + b.Property("Origin") + .HasMaxLength(150); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId"); + + b.Property("GrantType") + .HasMaxLength(196); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId"); + + b.Property("Provider") + .HasMaxLength(64); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId"); + + b.Property("Key") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("ClientId", "Key"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("RedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId"); + + b.Property("Scope") + .HasMaxLength(196); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime"); + + b.Property("Data") + .IsRequired(); + + b.Property("Expiration"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Id"); + + b.Property("SubjectId") + .HasMaxLength(200); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50); + + b.HasKey("Key"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.Property("IdentityResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Emphasize"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiScope") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId", "Name") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20190527125607_Initial.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20190527125607_Initial.cs new file mode 100644 index 0000000000..be2770aabb --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/20190527125607_Initial.cs @@ -0,0 +1,973 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace MyCompanyName.MyProjectName.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AbpAuditLogs", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + ApplicationName = table.Column(maxLength: 96, nullable: true), + UserId = table.Column(nullable: true), + UserName = table.Column(maxLength: 256, nullable: true), + TenantId = table.Column(nullable: true), + TenantName = table.Column(nullable: true), + ImpersonatorUserId = table.Column(nullable: true), + ImpersonatorTenantId = table.Column(nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ClientIpAddress = table.Column(maxLength: 64, nullable: true), + ClientName = table.Column(maxLength: 128, nullable: true), + ClientId = table.Column(maxLength: 64, nullable: true), + CorrelationId = table.Column(maxLength: 64, nullable: true), + BrowserInfo = table.Column(maxLength: 512, nullable: true), + HttpMethod = table.Column(maxLength: 16, nullable: true), + Url = table.Column(maxLength: 256, nullable: true), + Exceptions = table.Column(maxLength: 4000, nullable: true), + Comments = table.Column(maxLength: 256, nullable: true), + HttpStatusCode = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogs", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpClaimTypes", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + Name = table.Column(maxLength: 256, nullable: false), + Required = table.Column(nullable: false), + IsStatic = table.Column(nullable: false), + Regex = table.Column(maxLength: 512, nullable: true), + RegexDescription = table.Column(maxLength: 128, nullable: true), + Description = table.Column(maxLength: 256, nullable: true), + ValueType = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpClaimTypes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpPermissionGrants", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 128, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: false), + ProviderKey = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpPermissionGrants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpRoles", + columns: table => new + { + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(maxLength: 256, nullable: false), + TenantId = table.Column(nullable: true), + Name = table.Column(maxLength: 256, nullable: false), + NormalizedName = table.Column(maxLength: 256, nullable: false), + IsDefault = table.Column(nullable: false), + IsStatic = table.Column(nullable: false), + IsPublic = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpSettings", + columns: table => new + { + Id = table.Column(nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + Value = table.Column(maxLength: 2048, nullable: false), + ProviderName = table.Column(maxLength: 64, nullable: true), + ProviderKey = table.Column(maxLength: 64, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpSettings", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpTenants", + 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), + Name = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenants", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AbpUsers", + 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), + UserName = table.Column(maxLength: 256, nullable: false), + NormalizedUserName = table.Column(maxLength: 256, nullable: false), + Name = table.Column(maxLength: 64, nullable: true), + Surname = table.Column(maxLength: 64, nullable: true), + Email = table.Column(maxLength: 256, nullable: true), + NormalizedEmail = table.Column(maxLength: 256, nullable: true), + EmailConfirmed = table.Column(nullable: false, defaultValue: false), + PasswordHash = table.Column(maxLength: 256, nullable: true), + SecurityStamp = table.Column(maxLength: 256, nullable: false), + PhoneNumber = table.Column(maxLength: 16, nullable: true), + PhoneNumberConfirmed = table.Column(nullable: false, defaultValue: false), + TwoFactorEnabled = table.Column(nullable: false, defaultValue: false), + LockoutEnd = table.Column(nullable: true), + LockoutEnabled = table.Column(nullable: false, defaultValue: false), + AccessFailedCount = table.Column(nullable: false, defaultValue: 0) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiResources", + 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), + Name = table.Column(maxLength: 200, nullable: false), + DisplayName = table.Column(maxLength: 200, nullable: true), + Description = table.Column(maxLength: 1000, nullable: true), + Enabled = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiResources", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClients", + 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), + ClientId = table.Column(maxLength: 200, nullable: false), + ClientName = table.Column(maxLength: 200, nullable: true), + Description = table.Column(maxLength: 1000, nullable: true), + ClientUri = table.Column(maxLength: 300, nullable: true), + LogoUri = table.Column(maxLength: 300, nullable: true), + Enabled = table.Column(nullable: false), + ProtocolType = table.Column(maxLength: 200, nullable: false), + RequireClientSecret = table.Column(nullable: false), + RequireConsent = table.Column(nullable: false), + AllowRememberConsent = table.Column(nullable: false), + AlwaysIncludeUserClaimsInIdToken = table.Column(nullable: false), + RequirePkce = table.Column(nullable: false), + AllowPlainTextPkce = table.Column(nullable: false), + AllowAccessTokensViaBrowser = table.Column(nullable: false), + FrontChannelLogoutUri = table.Column(maxLength: 300, nullable: true), + FrontChannelLogoutSessionRequired = table.Column(nullable: false), + BackChannelLogoutUri = table.Column(maxLength: 300, nullable: true), + BackChannelLogoutSessionRequired = table.Column(nullable: false), + AllowOfflineAccess = table.Column(nullable: false), + IdentityTokenLifetime = table.Column(nullable: false), + AccessTokenLifetime = table.Column(nullable: false), + AuthorizationCodeLifetime = table.Column(nullable: false), + ConsentLifetime = table.Column(nullable: true), + AbsoluteRefreshTokenLifetime = table.Column(nullable: false), + SlidingRefreshTokenLifetime = table.Column(nullable: false), + RefreshTokenUsage = table.Column(nullable: false), + UpdateAccessTokenClaimsOnRefresh = table.Column(nullable: false), + RefreshTokenExpiration = table.Column(nullable: false), + AccessTokenType = table.Column(nullable: false), + EnableLocalLogin = table.Column(nullable: false), + IncludeJwtId = table.Column(nullable: false), + AlwaysSendClientClaims = table.Column(nullable: false), + ClientClaimsPrefix = table.Column(maxLength: 200, nullable: true), + PairWiseSubjectSalt = table.Column(maxLength: 200, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClients", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerIdentityResources", + 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), + Name = table.Column(maxLength: 200, nullable: false), + DisplayName = table.Column(maxLength: 200, nullable: true), + Description = table.Column(maxLength: 1000, nullable: true), + Enabled = table.Column(nullable: false), + Required = table.Column(nullable: false), + Emphasize = table.Column(nullable: false), + ShowInDiscoveryDocument = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerIdentityResources", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerPersistedGrants", + columns: table => new + { + Key = table.Column(maxLength: 200, nullable: false), + Id = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true), + ConcurrencyStamp = table.Column(nullable: true), + Type = table.Column(maxLength: 50, nullable: false), + SubjectId = table.Column(maxLength: 200, nullable: true), + ClientId = table.Column(maxLength: 200, nullable: false), + CreationTime = table.Column(nullable: false), + Expiration = table.Column(nullable: true), + Data = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerPersistedGrants", x => x.Key); + }); + + migrationBuilder.CreateTable( + name: "AbpAuditLogActions", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + AuditLogId = table.Column(nullable: false), + ServiceName = table.Column(maxLength: 256, nullable: true), + MethodName = table.Column(maxLength: 128, nullable: true), + Parameters = table.Column(maxLength: 2000, nullable: true), + ExecutionTime = table.Column(nullable: false), + ExecutionDuration = table.Column(nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpAuditLogActions", x => x.Id); + table.ForeignKey( + name: "FK_AbpAuditLogActions_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityChanges", + columns: table => new + { + Id = table.Column(nullable: false), + AuditLogId = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ChangeTime = table.Column(nullable: false), + ChangeType = table.Column(nullable: false), + EntityTenantId = table.Column(nullable: true), + EntityId = table.Column(maxLength: 128, nullable: false), + EntityTypeFullName = table.Column(maxLength: 128, nullable: false), + ExtraProperties = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityChanges_AbpAuditLogs_AuditLogId", + column: x => x.AuditLogId, + principalTable: "AbpAuditLogs", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpRoleClaims", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ClaimType = table.Column(maxLength: 256, nullable: false), + ClaimValue = table.Column(maxLength: 1024, nullable: true), + RoleId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpRoleClaims_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpTenantConnectionStrings", + columns: table => new + { + TenantId = table.Column(nullable: false), + Name = table.Column(maxLength: 64, nullable: false), + Value = table.Column(maxLength: 1024, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpTenantConnectionStrings", x => new { x.TenantId, x.Name }); + table.ForeignKey( + name: "FK_AbpTenantConnectionStrings_AbpTenants_TenantId", + column: x => x.TenantId, + principalTable: "AbpTenants", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserClaims", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + ClaimType = table.Column(maxLength: 256, nullable: false), + ClaimValue = table.Column(maxLength: 1024, nullable: true), + UserId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AbpUserClaims_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserLogins", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 64, nullable: false), + TenantId = table.Column(nullable: true), + ProviderKey = table.Column(maxLength: 196, nullable: false), + ProviderDisplayName = table.Column(maxLength: 128, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserLogins", x => new { x.UserId, x.LoginProvider }); + table.ForeignKey( + name: "FK_AbpUserLogins_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserRoles", + columns: table => new + { + UserId = table.Column(nullable: false), + RoleId = table.Column(nullable: false), + TenantId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpRoles_RoleId", + column: x => x.RoleId, + principalTable: "AbpRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AbpUserRoles_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpUserTokens", + columns: table => new + { + UserId = table.Column(nullable: false), + LoginProvider = table.Column(maxLength: 64, nullable: false), + Name = table.Column(maxLength: 128, nullable: false), + TenantId = table.Column(nullable: true), + Value = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AbpUserTokens_AbpUsers_UserId", + column: x => x.UserId, + principalTable: "AbpUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiClaims", + columns: table => new + { + Type = table.Column(maxLength: 196, nullable: false), + ApiResourceId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiClaims", x => new { x.ApiResourceId, x.Type }); + table.ForeignKey( + name: "FK_IdentityServerApiClaims_IdentityServerApiResources_ApiResourceId", + column: x => x.ApiResourceId, + principalTable: "IdentityServerApiResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiScopes", + columns: table => new + { + ApiResourceId = table.Column(nullable: false), + Name = table.Column(maxLength: 196, nullable: false), + DisplayName = table.Column(maxLength: 128, nullable: true), + Description = table.Column(maxLength: 256, nullable: true), + Required = table.Column(nullable: false), + Emphasize = table.Column(nullable: false), + ShowInDiscoveryDocument = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiScopes", x => new { x.ApiResourceId, x.Name }); + table.ForeignKey( + name: "FK_IdentityServerApiScopes_IdentityServerApiResources_ApiResourceId", + column: x => x.ApiResourceId, + principalTable: "IdentityServerApiResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiSecrets", + columns: table => new + { + Type = table.Column(maxLength: 32, nullable: false), + Value = table.Column(maxLength: 196, nullable: false), + ApiResourceId = table.Column(nullable: false), + Description = table.Column(maxLength: 256, nullable: true), + Expiration = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiSecrets", x => new { x.ApiResourceId, x.Type, x.Value }); + table.ForeignKey( + name: "FK_IdentityServerApiSecrets_IdentityServerApiResources_ApiResourceId", + column: x => x.ApiResourceId, + principalTable: "IdentityServerApiResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientClaims", + columns: table => new + { + ClientId = table.Column(nullable: false), + Type = table.Column(maxLength: 250, nullable: false), + Value = table.Column(maxLength: 250, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientClaims", x => new { x.ClientId, x.Type, x.Value }); + table.ForeignKey( + name: "FK_IdentityServerClientClaims_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientCorsOrigins", + columns: table => new + { + ClientId = table.Column(nullable: false), + Origin = table.Column(maxLength: 150, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientCorsOrigins", x => new { x.ClientId, x.Origin }); + table.ForeignKey( + name: "FK_IdentityServerClientCorsOrigins_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientGrantTypes", + columns: table => new + { + ClientId = table.Column(nullable: false), + GrantType = table.Column(maxLength: 196, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientGrantTypes", x => new { x.ClientId, x.GrantType }); + table.ForeignKey( + name: "FK_IdentityServerClientGrantTypes_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientIdPRestrictions", + columns: table => new + { + ClientId = table.Column(nullable: false), + Provider = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientIdPRestrictions", x => new { x.ClientId, x.Provider }); + table.ForeignKey( + name: "FK_IdentityServerClientIdPRestrictions_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientPostLogoutRedirectUris", + columns: table => new + { + ClientId = table.Column(nullable: false), + PostLogoutRedirectUri = table.Column(maxLength: 200, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientPostLogoutRedirectUris", x => new { x.ClientId, x.PostLogoutRedirectUri }); + table.ForeignKey( + name: "FK_IdentityServerClientPostLogoutRedirectUris_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientProperties", + columns: table => new + { + ClientId = table.Column(nullable: false), + Key = table.Column(maxLength: 64, nullable: false), + Value = table.Column(maxLength: 128, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientProperties", x => new { x.ClientId, x.Key }); + table.ForeignKey( + name: "FK_IdentityServerClientProperties_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientRedirectUris", + columns: table => new + { + ClientId = table.Column(nullable: false), + RedirectUri = table.Column(maxLength: 200, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientRedirectUris", x => new { x.ClientId, x.RedirectUri }); + table.ForeignKey( + name: "FK_IdentityServerClientRedirectUris_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientScopes", + columns: table => new + { + ClientId = table.Column(nullable: false), + Scope = table.Column(maxLength: 196, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientScopes", x => new { x.ClientId, x.Scope }); + table.ForeignKey( + name: "FK_IdentityServerClientScopes_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerClientSecrets", + columns: table => new + { + Type = table.Column(maxLength: 32, nullable: false), + Value = table.Column(maxLength: 196, nullable: false), + ClientId = table.Column(nullable: false), + Description = table.Column(maxLength: 256, nullable: true), + Expiration = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerClientSecrets", x => new { x.ClientId, x.Type, x.Value }); + table.ForeignKey( + name: "FK_IdentityServerClientSecrets_IdentityServerClients_ClientId", + column: x => x.ClientId, + principalTable: "IdentityServerClients", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerIdentityClaims", + columns: table => new + { + Type = table.Column(maxLength: 196, nullable: false), + IdentityResourceId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerIdentityClaims", x => new { x.IdentityResourceId, x.Type }); + table.ForeignKey( + name: "FK_IdentityServerIdentityClaims_IdentityServerIdentityResources_IdentityResourceId", + column: x => x.IdentityResourceId, + principalTable: "IdentityServerIdentityResources", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AbpEntityPropertyChanges", + columns: table => new + { + Id = table.Column(nullable: false), + TenantId = table.Column(nullable: true), + EntityChangeId = table.Column(nullable: false), + NewValue = table.Column(maxLength: 512, nullable: true), + OriginalValue = table.Column(maxLength: 512, nullable: true), + PropertyName = table.Column(maxLength: 128, nullable: false), + PropertyTypeFullName = table.Column(maxLength: 64, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AbpEntityPropertyChanges", x => x.Id); + table.ForeignKey( + name: "FK_AbpEntityPropertyChanges_AbpEntityChanges_EntityChangeId", + column: x => x.EntityChangeId, + principalTable: "AbpEntityChanges", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "IdentityServerApiScopeClaims", + columns: table => new + { + Type = table.Column(maxLength: 196, nullable: false), + ApiResourceId = table.Column(nullable: false), + Name = table.Column(maxLength: 196, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_IdentityServerApiScopeClaims", x => new { x.ApiResourceId, x.Name, x.Type }); + table.ForeignKey( + name: "FK_IdentityServerApiScopeClaims_IdentityServerApiScopes_ApiResourceId_Name", + columns: x => new { x.ApiResourceId, x.Name }, + principalTable: "IdentityServerApiScopes", + principalColumns: new[] { "ApiResourceId", "Name" }, + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_AuditLogId", + table: "AbpAuditLogActions", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogActions_TenantId_ServiceName_MethodName_ExecutionTime", + table: "AbpAuditLogActions", + columns: new[] { "TenantId", "ServiceName", "MethodName", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpAuditLogs_TenantId_UserId_ExecutionTime", + table: "AbpAuditLogs", + columns: new[] { "TenantId", "UserId", "ExecutionTime" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_AuditLogId", + table: "AbpEntityChanges", + column: "AuditLogId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityChanges_TenantId_EntityTypeFullName_EntityId", + table: "AbpEntityChanges", + columns: new[] { "TenantId", "EntityTypeFullName", "EntityId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpEntityPropertyChanges_EntityChangeId", + table: "AbpEntityPropertyChanges", + column: "EntityChangeId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpPermissionGrants_Name_ProviderName_ProviderKey", + table: "AbpPermissionGrants", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoleClaims_RoleId", + table: "AbpRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpRoles_NormalizedName", + table: "AbpRoles", + column: "NormalizedName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpSettings_Name_ProviderName_ProviderKey", + table: "AbpSettings", + columns: new[] { "Name", "ProviderName", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpTenants_Name", + table: "AbpTenants", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserClaims_UserId", + table: "AbpUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserLogins_LoginProvider_ProviderKey", + table: "AbpUserLogins", + columns: new[] { "LoginProvider", "ProviderKey" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUserRoles_RoleId_UserId", + table: "AbpUserRoles", + columns: new[] { "RoleId", "UserId" }); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_Email", + table: "AbpUsers", + column: "Email"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedEmail", + table: "AbpUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_NormalizedUserName", + table: "AbpUsers", + column: "NormalizedUserName"); + + migrationBuilder.CreateIndex( + name: "IX_AbpUsers_UserName", + table: "AbpUsers", + column: "UserName"); + + migrationBuilder.CreateIndex( + name: "IX_IdentityServerClients_ClientId", + table: "IdentityServerClients", + column: "ClientId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_IdentityServerPersistedGrants_SubjectId_ClientId_Type", + table: "IdentityServerPersistedGrants", + columns: new[] { "SubjectId", "ClientId", "Type" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AbpAuditLogActions"); + + migrationBuilder.DropTable( + name: "AbpClaimTypes"); + + migrationBuilder.DropTable( + name: "AbpEntityPropertyChanges"); + + migrationBuilder.DropTable( + name: "AbpPermissionGrants"); + + migrationBuilder.DropTable( + name: "AbpRoleClaims"); + + migrationBuilder.DropTable( + name: "AbpSettings"); + + migrationBuilder.DropTable( + name: "AbpTenantConnectionStrings"); + + migrationBuilder.DropTable( + name: "AbpUserClaims"); + + migrationBuilder.DropTable( + name: "AbpUserLogins"); + + migrationBuilder.DropTable( + name: "AbpUserRoles"); + + migrationBuilder.DropTable( + name: "AbpUserTokens"); + + migrationBuilder.DropTable( + name: "IdentityServerApiClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerApiScopeClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerApiSecrets"); + + migrationBuilder.DropTable( + name: "IdentityServerClientClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerClientCorsOrigins"); + + migrationBuilder.DropTable( + name: "IdentityServerClientGrantTypes"); + + migrationBuilder.DropTable( + name: "IdentityServerClientIdPRestrictions"); + + migrationBuilder.DropTable( + name: "IdentityServerClientPostLogoutRedirectUris"); + + migrationBuilder.DropTable( + name: "IdentityServerClientProperties"); + + migrationBuilder.DropTable( + name: "IdentityServerClientRedirectUris"); + + migrationBuilder.DropTable( + name: "IdentityServerClientScopes"); + + migrationBuilder.DropTable( + name: "IdentityServerClientSecrets"); + + migrationBuilder.DropTable( + name: "IdentityServerIdentityClaims"); + + migrationBuilder.DropTable( + name: "IdentityServerPersistedGrants"); + + migrationBuilder.DropTable( + name: "AbpEntityChanges"); + + migrationBuilder.DropTable( + name: "AbpTenants"); + + migrationBuilder.DropTable( + name: "AbpRoles"); + + migrationBuilder.DropTable( + name: "AbpUsers"); + + migrationBuilder.DropTable( + name: "IdentityServerApiScopes"); + + migrationBuilder.DropTable( + name: "IdentityServerClients"); + + migrationBuilder.DropTable( + name: "IdentityServerIdentityResources"); + + migrationBuilder.DropTable( + name: "AbpAuditLogs"); + + migrationBuilder.DropTable( + name: "IdentityServerApiResources"); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs new file mode 100644 index 0000000000..8eb804421d --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Migrations/IdentityServerHostMigrationsDbContextModelSnapshot.cs @@ -0,0 +1,1302 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using MyCompanyName.MyProjectName.EntityFrameworkCore; + +namespace MyCompanyName.MyProjectName.Migrations +{ + [DbContext(typeof(IdentityServerHostMigrationsDbContext))] + partial class IdentityServerHostMigrationsDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.4-servicing-10062") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ApplicationName") + .HasColumnName("ApplicationName") + .HasMaxLength(96); + + b.Property("BrowserInfo") + .HasColumnName("BrowserInfo") + .HasMaxLength(512); + + b.Property("ClientId") + .HasColumnName("ClientId") + .HasMaxLength(64); + + b.Property("ClientIpAddress") + .HasColumnName("ClientIpAddress") + .HasMaxLength(64); + + b.Property("ClientName") + .HasColumnName("ClientName") + .HasMaxLength(128); + + b.Property("Comments") + .HasColumnName("Comments") + .HasMaxLength(256); + + b.Property("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnName("CorrelationId") + .HasMaxLength(64); + + b.Property("Exceptions") + .HasColumnName("Exceptions") + .HasMaxLength(4000); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasColumnName("HttpMethod") + .HasMaxLength(16); + + b.Property("HttpStatusCode") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TenantName"); + + b.Property("Url") + .HasColumnName("Url") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasColumnName("UserName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasColumnName("MethodName") + .HasMaxLength(128); + + b.Property("Parameters") + .HasColumnName("Parameters") + .HasMaxLength(2000); + + b.Property("ServiceName") + .HasColumnName("ServiceName") + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditLogId") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasColumnName("EntityId") + .HasMaxLength(128); + + b.Property("EntityTenantId"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasColumnName("EntityTypeFullName") + .HasMaxLength(128); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EntityChangeId"); + + b.Property("NewValue") + .HasColumnName("NewValue") + .HasMaxLength(512); + + b.Property("OriginalValue") + .HasColumnName("OriginalValue") + .HasMaxLength(512); + + b.Property("PropertyName") + .IsRequired() + .HasColumnName("PropertyName") + .HasMaxLength(128); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasColumnName("PropertyTypeFullName") + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("Regex") + .HasMaxLength(512); + + b.Property("RegexDescription") + .HasMaxLength(128); + + b.Property("Required"); + + b.Property("ValueType"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasColumnName("ConcurrencyStamp") + .HasMaxLength(256); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnName("AccessFailedCount") + .HasDefaultValue(0); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .HasColumnName("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("EmailConfirmed") + .HasDefaultValue(false); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("LockoutEnabled") + .HasDefaultValue(false); + + b.Property("LockoutEnd"); + + b.Property("Name") + .HasColumnName("Name") + .HasMaxLength(64); + + b.Property("NormalizedEmail") + .HasColumnName("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .IsRequired() + .HasColumnName("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("PasswordHash") + .HasMaxLength(256); + + b.Property("PhoneNumber") + .HasColumnName("PhoneNumber") + .HasMaxLength(16); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnName("PhoneNumberConfirmed") + .HasDefaultValue(false); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnName("SecurityStamp") + .HasMaxLength(256); + + b.Property("Surname") + .HasColumnName("Surname") + .HasMaxLength(64); + + b.Property("TenantId") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnName("TwoFactorEnabled") + .HasDefaultValue(false); + + b.Property("UserName") + .IsRequired() + .HasColumnName("UserName") + .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") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256); + + b.Property("ClaimValue") + .HasMaxLength(1024); + + b.Property("TenantId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("ProviderDisplayName") + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196); + + b.Property("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.Property("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider") + .HasMaxLength(64); + + b.Property("Name") + .HasMaxLength(128); + + b.Property("TenantId"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("DisplayName") + .HasMaxLength(128); + + b.Property("Emphasize"); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("ApiResourceId", "Name"); + + b.ToTable("IdentityServerApiScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.Property("ApiResourceId"); + + b.Property("Name") + .HasMaxLength(196); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("ApiResourceId", "Name", "Type"); + + b.ToTable("IdentityServerApiScopeClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.Property("ApiResourceId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AbsoluteRefreshTokenLifetime"); + + b.Property("AccessTokenLifetime"); + + b.Property("AccessTokenType"); + + b.Property("AllowAccessTokensViaBrowser"); + + b.Property("AllowOfflineAccess"); + + b.Property("AllowPlainTextPkce"); + + b.Property("AllowRememberConsent"); + + b.Property("AlwaysIncludeUserClaimsInIdToken"); + + b.Property("AlwaysSendClientClaims"); + + b.Property("AuthorizationCodeLifetime"); + + b.Property("BackChannelLogoutSessionRequired"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(300); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ClientName") + .HasMaxLength(200); + + b.Property("ClientUri") + .HasMaxLength(300); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("EnableLocalLogin"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(300); + + b.Property("IdentityTokenLifetime"); + + b.Property("IncludeJwtId"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(300); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200); + + b.Property("RefreshTokenExpiration"); + + b.Property("RefreshTokenUsage"); + + b.Property("RequireClientSecret"); + + b.Property("RequireConsent"); + + b.Property("RequirePkce"); + + b.Property("SlidingRefreshTokenLifetime"); + + b.Property("UpdateAccessTokenClaimsOnRefresh"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("IdentityServerClients"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(250); + + b.Property("Value") + .HasMaxLength(250); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId"); + + b.Property("Origin") + .HasMaxLength(150); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId"); + + b.Property("GrantType") + .HasMaxLength(196); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId"); + + b.Property("Provider") + .HasMaxLength(64); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId"); + + b.Property("Key") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(128); + + b.HasKey("ClientId", "Key"); + + b.ToTable("IdentityServerClientProperties"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId"); + + b.Property("RedirectUri") + .HasMaxLength(200); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId"); + + b.Property("Scope") + .HasMaxLength(196); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId"); + + b.Property("Type") + .HasMaxLength(32); + + b.Property("Value") + .HasMaxLength(196); + + b.Property("Description") + .HasMaxLength(256); + + b.Property("Expiration"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200); + + b.Property("ConcurrencyStamp"); + + b.Property("CreationTime"); + + b.Property("Data") + .IsRequired(); + + b.Property("Expiration"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("Id"); + + b.Property("SubjectId") + .HasMaxLength(200); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50); + + b.HasKey("Key"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.ToTable("IdentityServerPersistedGrants"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.Property("IdentityResourceId"); + + b.Property("Type") + .HasMaxLength(196); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000); + + b.Property("DisplayName") + .HasMaxLength(200); + + b.Property("Emphasize"); + + b.Property("Enabled"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200); + + b.Property("Required"); + + b.Property("ShowInDiscoveryDocument"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources"); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64); + + b.Property("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpPermissionGrants"); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128); + + b.Property("ProviderKey") + .HasMaxLength(64); + + b.Property("ProviderName") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey"); + + b.ToTable("AbpSettings"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnName("IsDeleted") + .HasDefaultValue(false); + + b.Property("LastModificationTime") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("AbpTenants"); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.Property("TenantId"); + + b.Property("Name") + .HasMaxLength(64); + + b.Property("Value") + .IsRequired() + .HasMaxLength(1024); + + b.HasKey("TenantId", "Name"); + + b.ToTable("AbpTenantConnectionStrings"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog") + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange") + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser") + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiScope") + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId", "Name") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource") + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client") + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource") + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Volo.Abp.TenantManagement.TenantConnectionString", b => + { + b.HasOne("Volo.Abp.TenantManagement.Tenant") + .WithMany("ConnectionStrings") + .HasForeignKey("TenantId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj new file mode 100644 index 0000000000..b1d532024c --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/MyCompanyName.MyProjectName.IdentityServer.csproj @@ -0,0 +1,55 @@ + + + + netcoreapp2.2 + MyCompanyName.MyProjectName + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + signed + + + + + diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs new file mode 100644 index 0000000000..065f35e613 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs @@ -0,0 +1,165 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using MyCompanyName.MyProjectName.MultiTenancy; +using StackExchange.Redis; +using Swashbuckle.AspNetCore.Swagger; +using Volo.Abp; +using Volo.Abp.Account.Web; +using Volo.Abp.AspNetCore.Authentication.JwtBearer; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic; +using Volo.Abp.Auditing; +using Volo.Abp.AuditLogging.EntityFrameworkCore; +using Volo.Abp.Autofac; +using Volo.Abp.Data; +using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore.SqlServer; +using Volo.Abp.Identity; +using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.IdentityServer.EntityFrameworkCore; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.PermissionManagement; +using Volo.Abp.PermissionManagement.EntityFrameworkCore; +using Volo.Abp.PermissionManagement.HttpApi; +using Volo.Abp.PermissionManagement.Identity; +using Volo.Abp.SettingManagement.EntityFrameworkCore; +using Volo.Abp.TenantManagement; +using Volo.Abp.TenantManagement.EntityFrameworkCore; +using Volo.Abp.Threading; +using Volo.Abp.Ui.Navigation.Urls; + +namespace MyCompanyName.MyProjectName +{ + [DependsOn( + typeof(AbpAccountWebIdentityServerModule), + typeof(AbpAspNetCoreMultiTenancyModule), + typeof(AbpAspNetCoreMvcModule), + typeof(AbpAspNetCoreMvcUiBasicThemeModule), + typeof(AbpAuditLoggingEntityFrameworkCoreModule), + typeof(AbpAutofacModule), + typeof(AbpEntityFrameworkCoreSqlServerModule), + typeof(AbpIdentityEntityFrameworkCoreModule), + typeof(AbpIdentityApplicationModule), + typeof(AbpIdentityHttpApiModule), + typeof(AbpIdentityServerEntityFrameworkCoreModule), + typeof(AbpPermissionManagementDomainIdentityModule), + typeof(AbpPermissionManagementEntityFrameworkCoreModule), + typeof(AbpPermissionManagementApplicationModule), + typeof(AbpPermissionManagementHttpApiModule), + typeof(AbpSettingManagementEntityFrameworkCoreModule), + typeof(AbpTenantManagementEntityFrameworkCoreModule), + typeof(AbpTenantManagementApplicationModule), + typeof(AbpTenantManagementHttpApiModule), + typeof(AbpAspNetCoreAuthenticationJwtBearerModule), + typeof(MyProjectNameApplicationContractsModule) + )] + public class MyProjectNameIdentityServerModule : AbpModule + { + public override void ConfigureServices(ServiceConfigurationContext context) + { + var hostingEnvironment = context.Services.GetHostingEnvironment(); + var configuration = context.Services.BuildConfiguration(); + + Configure(options => + { + options.UseSqlServer(); + }); + + context.Services.AddSwaggerGen( + options => + { + options.SwaggerDoc("v1", new Info { Title = "MyProjectName API", Version = "v1" }); + options.DocInclusionPredicate((docName, description) => true); + options.CustomSchemaIds(type => type.FullName); + }); + + Configure(options => + { + options.Languages.Add(new LanguageInfo("cs", "cs", "Čeština")); + options.Languages.Add(new LanguageInfo("en", "en", "English")); + options.Languages.Add(new LanguageInfo("pt-BR", "pt-BR", "Português")); + options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe")); + options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文")); + }); + + Configure(options => + { + //options.IsEnabledForGetRequests = true; + options.ApplicationName = "AuthServer"; + }); + + Configure(options => + { + options.Applications["MVC"].RootUrl = configuration["AppSelfUrl"]; + }); + + context.Services.AddAuthentication() + .AddIdentityServerAuthentication(options => + { + options.Authority = configuration["AuthServer:Authority"]; + options.RequireHttpsMetadata = false; + options.ApiName = configuration["AuthServer:ApiName"]; + }); + + context.Services.AddDistributedRedisCache(options => + { + options.Configuration = configuration["Redis:Configuration"]; + }); + + if (!hostingEnvironment.IsDevelopment()) + { + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); + context.Services + .AddDataProtection() + .PersistKeysToStackExchangeRedis(redis, "MyProjectName-Protection-Keys"); + } + } + + public override void OnApplicationInitialization(ApplicationInitializationContext context) + { + var app = context.GetApplicationBuilder(); + + if (!context.GetEnvironment().IsDevelopment()) + { + app.UseHsts(); + } + app.UseHttpsRedirection(); + app.UseCorrelationId(); + app.UseVirtualFiles(); + app.UseAuthentication(); + app.UseJwtTokenMiddleware(); + if (MultiTenancyConsts.IsEnabled) + { + app.UseMultiTenancy(); + } + app.UseIdentityServer(); + app.UseAbpRequestLocalization(); + app.UseSwagger(); + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Support APP API"); + }); + app.UseAuditing(); + app.UseMvcWithDefaultRouteAndArea(); + + SeedData(context); + } + + private void SeedData(ApplicationInitializationContext context) + { + AsyncHelper.RunSync(async () => + { + using (var scope = context.ServiceProvider.CreateScope()) + { + await scope.ServiceProvider + .GetRequiredService() + .SeedAsync(); + } + }); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml new file mode 100644 index 0000000000..5bc466601f --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml @@ -0,0 +1,34 @@ +@page +@using MyCompanyName.MyProjectName.Pages +@using Volo.Abp.Users +@model IndexModel +@inject ICurrentUser CurrentUser +@if (CurrentUser.IsAuthenticated) +{ +
    + + + + Logout + + +

    @CurrentUser.UserName

    +
    @CurrentUser.Email
    +
    + Roles: @CurrentUser.Roles.JoinAsString(", ") +
    + Claims:
    + @Html.Raw(CurrentUser.GetAllClaims().Select(c => $"{c.Type}={c.Value}").JoinAsString("
    ")) +
    +
    +
    +
    +} + +@if (!CurrentUser.IsAuthenticated) +{ +
    +

    + Login +
    +} \ No newline at end of file diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml.cs new file mode 100644 index 0000000000..cfd795c936 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/Index.cshtml.cs @@ -0,0 +1,11 @@ +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace MyCompanyName.MyProjectName.Pages +{ + public class IndexModel : AbpPageModel + { + public void OnGet() + { + } + } +} \ No newline at end of file diff --git a/templates/module/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectName/_ViewImports.cshtml b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/_ViewImports.cshtml similarity index 100% rename from templates/module/src/MyCompanyName.MyProjectName.Web/Pages/MyProjectName/_ViewImports.cshtml rename to templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Pages/_ViewImports.cshtml diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Program.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Program.cs new file mode 100644 index 0000000000..22b560f34f --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Program.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; +using Microsoft.AspNetCore.Hosting; +using Serilog; +using Serilog.Events; + +namespace MyCompanyName.MyProjectName +{ + public class Program + { + public static int Main(string[] args) + { + Log.Logger = new LoggerConfiguration() +#if DEBUG + .MinimumLevel.Debug() +#else + .MinimumLevel.Information() +#endif + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.File("Logs/logs.txt") + .CreateLogger(); + + try + { + Log.Information("Starting web host."); + BuildWebHostInternal(args).Run(); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly!"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IWebHost BuildWebHostInternal(string[] args) => + new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .UseSerilog() + .Build(); + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Properties/launchSettings.json b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Properties/launchSettings.json new file mode 100644 index 0000000000..d1d5c8a40c --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:61517", + "sslPort": 44301 //44300 to 44399 (inclusive) + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IdentityServerHost": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Startup.cs b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Startup.cs new file mode 100644 index 0000000000..ff2a868a60 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/Startup.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Volo.Abp; + +namespace MyCompanyName.MyProjectName +{ + public class Startup + { + public IServiceProvider ConfigureServices(IServiceCollection services) + { + services.AddApplication(options => + { + options.UseAutofac(); + }); + + return services.BuildServiceProviderFromFactory(); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + app.InitializeApplication(); + } + } +} diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/abp.resourcemapping.js b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/abp.resourcemapping.js new file mode 100644 index 0000000000..e2189c3c69 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/abp.resourcemapping.js @@ -0,0 +1,11 @@ +module.exports = { + aliases: { + + }, + clean: [ + + ], + mappings: { + + } +}; \ No newline at end of file diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/appsettings.json b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/appsettings.json new file mode 100644 index 0000000000..84503601a5 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/appsettings.json @@ -0,0 +1,24 @@ +{ + "AppSelfUrl": "https://localhost:44301/", + "ConnectionStrings": { + "Default": "Server=localhost;Database=MyProjectName_Main;Trusted_Connection=True;MultipleActiveResultSets=true" + }, + "Redis": { + "Configuration": "127.0.0.1" + }, + "AuthServer": { + "Authority": "https://localhost:44301/", + "ApiName": "MyProjectName" + }, + "IdentityServer": { + "Clients": { + "MyProjectName_Web": { + "ClientId": "MyProjectName_Web", + "RootUrl": "https://localhost:44302/" + }, + "MyProjectName_ConsoleTestApp": { + "ClientId": "MyProjectName_ConsoleTestApp" + } + } + } +} \ No newline at end of file diff --git a/templates/module/app/MyCompanyName.MyProjectName.DemoApp/gulpfile.js b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/gulpfile.js similarity index 100% rename from templates/module/app/MyCompanyName.MyProjectName.DemoApp/gulpfile.js rename to templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/gulpfile.js diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/package.json b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/package.json new file mode 100644 index 0000000000..826f8ef849 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/package.json @@ -0,0 +1,8 @@ +{ + "version": "0.1.0", + "name": "my-app-identityserver", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^0.4.9" + } +} diff --git a/templates/service/host/IdentityServerHost/tempkey.rsa b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/tempkey.rsa similarity index 100% rename from templates/service/host/IdentityServerHost/tempkey.rsa rename to templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/tempkey.rsa diff --git a/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/abp/core/abp.js b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/abp/core/abp.js new file mode 100644 index 0000000000..4c87489e22 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/abp/core/abp.js @@ -0,0 +1,546 @@ +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) { + sourceName = sourceName || abp.localization.defaultResourceName; + + 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.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(); + } + + /* 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, not implements it + + abp.ui.block = function (elm) { + abp.log.warn('abp.ui.block is not implemented!'); + }; + + abp.ui.unblock = function (elm) { + abp.log.warn('abp.ui.unblock is not implemented!'); + }; + + /* UI BUSY */ + //Defines UI Busy API, not implements it + + abp.ui.setBusy = function (elm, optionsOrPromise) { + abp.log.warn('abp.ui.setBusy is not implemented!'); + }; + + abp.ui.clearBusy = function (elm) { + abp.log.warn('abp.ui.clearBusy is not implemented!'); + }; + + /* 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/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/abp/jquery/abp.jquery.js b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/abp/jquery/abp.jquery.js new file mode 100644 index 0000000000..6a84bfbb61 --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/abp/jquery/abp.jquery.js @@ -0,0 +1,389 @@ +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) { + $.getScript(url) + .done(function () { + 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/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/bootstrap/css/bootstrap.css b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/bootstrap/css/bootstrap.css new file mode 100644 index 0000000000..8f4758923a --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/bootstrap/css/bootstrap.css @@ -0,0 +1,10038 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 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 { + 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]):not([tabindex]) { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):focus { + outline: 0; +} + +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; +} + +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; +} + +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; +} + +input[type="date"], +input[type="time"], +input[type="datetime-local"], +input[type="month"] { + -webkit-appearance: listbox; +} + +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-break: 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 { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.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; + max-width: 100%; +} + +.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; + max-width: 100%; + } + .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; + max-width: 100%; + } + .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; + max-width: 100%; + } + .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; + max-width: 100%; + } + .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: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; +} + +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-top: 0.375rem; + padding-bottom: 0.375rem; + margin-bottom: 0; + 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 { + 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 .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' 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: center right calc(0.375em + 0.1875rem); + 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 .form-control:valid ~ .valid-feedback, +.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, +.form-control.is-valid ~ .valid-tooltip { + display: block; +} + +.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((1em + 0.75rem) * 3 / 4 + 1.75rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' 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' 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 .custom-select:valid ~ .valid-feedback, +.was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, +.custom-select.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-control-file:valid ~ .valid-feedback, +.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, +.form-control-file.is-valid ~ .valid-tooltip { + display: block; +} + +.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 ~ .valid-feedback, +.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, +.custom-control-input.is-valid ~ .valid-tooltip { + display: block; +} + +.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 ~ .valid-feedback, +.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, +.custom-file-input.is-valid ~ .valid-tooltip { + display: block; +} + +.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 .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' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); + background-repeat: no-repeat; + background-position: center right calc(0.375em + 0.1875rem); + 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 .form-control:invalid ~ .invalid-feedback, +.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, +.form-control.is-invalid ~ .invalid-tooltip { + display: block; +} + +.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((1em + 0.75rem) * 3 / 4 + 1.75rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' 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' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%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 .custom-select:invalid ~ .invalid-feedback, +.was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, +.custom-select.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-control-file:invalid ~ .invalid-feedback, +.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, +.form-control-file.is-invalid ~ .invalid-tooltip { + display: block; +} + +.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 ~ .invalid-feedback, +.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, +.custom-control-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.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 ~ .invalid-feedback, +.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, +.custom-file-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.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; +} + +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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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; + box-shadow: none; +} + +.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%; + 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; + z-index: -1; + 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 { + color: #6c757d; +} + +.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' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 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' 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' 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: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' 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; + background-color: #fff; + 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-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 { + 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: calc(1rem + 0.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; + 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 { + 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; + 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 { + 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; + 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 { + 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 { + 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 { + 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 { + -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 { + 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 { + -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 { + 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 { + -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 { + 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 { + -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 { + 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 { + -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 viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' 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 viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' 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:first-child .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.card > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.card-body { + -ms-flex: 1 1 auto; + flex: 1 1 auto; + 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 { + width: 100%; + border-radius: calc(0.25rem - 1px); +} + +.card-img-top { + width: 100%; + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); +} + +.card-img-bottom { + width: 100%; + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); +} + +.card-deck { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; +} + +.card-deck .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-deck { + -ms-flex-flow: row wrap; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; + } + .card-deck .card { + display: -ms-flexbox; + display: flex; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + -ms-flex-direction: column; + flex-direction: column; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; + } +} + +.card-group { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; +} + +.card-group > .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-group { + -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(:first-of-type) .card-header:first-child { + border-radius: 0; +} + +.accordion > .card:not(:first-of-type):not(:last-of-type) { + border-bottom: 0; + border-radius: 0; +} + +.accordion > .card:first-of-type { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.accordion > .card:last-of-type { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.accordion > .card .card-header { + 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 + .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: 2; + 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: 1; + 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; + 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; + 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; +} + +.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; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); +} + +.list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.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-horizontal { + -ms-flex-direction: row; + flex-direction: row; +} + +.list-group-horizontal .list-group-item { + margin-right: -1px; + margin-bottom: 0; +} + +.list-group-horizontal .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; +} + +.list-group-horizontal .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; +} + +@media (min-width: 576px) { + .list-group-horizontal-sm { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-sm .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-sm .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-sm .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +@media (min-width: 768px) { + .list-group-horizontal-md { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-md .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-md .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-md .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +@media (min-width: 992px) { + .list-group-horizontal-lg { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-lg .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-lg .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-lg .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +@media (min-width: 1200px) { + .list-group-horizontal-xl { + -ms-flex-direction: row; + flex-direction: row; + } + .list-group-horizontal-xl .list-group-item { + margin-right: -1px; + margin-bottom: 0; + } + .list-group-horizontal-xl .list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; + } + .list-group-horizontal-xl .list-group-item:last-child { + margin-right: 0; + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0; + } +} + +.list-group-flush .list-group-item { + border-right: 0; + border-left: 0; + border-radius: 0; +} + +.list-group-flush .list-group-item:last-child { + margin-bottom: -1px; +} + +.list-group-flush:first-child .list-group-item:first-child { + border-top: 0; +} + +.list-group-flush:last-child .list-group-item:last-child { + margin-bottom: 0; + border-bottom: 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; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +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-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); + 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: 0.3rem; + border-top-right-radius: 0.3rem; +} + +.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-align: center; + align-items: center; + -ms-flex-pack: end; + justify-content: flex-end; + padding: 1rem; + border-top: 1px solid #dee2e6; + border-bottom-right-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} + +.modal-footer > :not(:first-child) { + margin-left: .25rem; +} + +.modal-footer > :not(:last-child) { + margin-right: .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); + } + .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) * -1); +} + +.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) * -1); + 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) * -1); +} + +.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) * -1); + 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: 0s 0.6s opacity; +} + +@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' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%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' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-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; + } +} + +@keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } + 50% { + opacity: 1; + } +} + +.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; + } +} + +.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; + 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; +} + +.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); +} + +.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; + } +} + +.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-break: break-word !important; + overflow-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/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/bootstrap/js/bootstrap.bundle.js b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/bootstrap/js/bootstrap.bundle.js new file mode 100644 index 0000000000..f4f23ead2c --- /dev/null +++ b/templates/mvc-module/host/MyCompanyName.MyProjectName.IdentityServer/wwwroot/libs/bootstrap/js/bootstrap.bundle.js @@ -0,0 +1,7013 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 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'; + + $ = $ && $.hasOwnProperty('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 _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + + if (typeof Object.getOwnPropertySymbols === 'function') { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + + ownKeys.forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } + + return target; + } + + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + subClass.__proto__ = superClass; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v4.3.1): 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) { + 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; // eslint-disable-line no-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); + } + }; + setTransitionEndSupport(); + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME = 'alert'; + var VERSION = '4.3.1'; + 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, + CLOSED: "closed" + EVENT_KEY, + CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY + }; + var ClassName = { + ALERT: 'alert', + FADE: 'fade', + 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("." + ClassName.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(ClassName.SHOW); + + if (!$(element).hasClass(ClassName.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.3.1'; + 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 ClassName$1 = { + ACTIVE: 'active', + BUTTON: 'btn', + FOCUS: 'focus' + }; + var Selector$1 = { + DATA_TOGGLE_CARROT: '[data-toggle^="button"]', + DATA_TOGGLE: '[data-toggle="buttons"]', + INPUT: 'input:not([type="hidden"])', + ACTIVE: '.active', + BUTTON: '.btn' + }; + var Event$1 = { + CLICK_DATA_API: "click" + EVENT_KEY$1 + DATA_API_KEY$1, + FOCUS_BLUR_DATA_API: "focus" + EVENT_KEY$1 + DATA_API_KEY$1 + " " + ("blur" + 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$1.DATA_TOGGLE)[0]; + + if (rootElement) { + var input = this._element.querySelector(Selector$1.INPUT); + + if (input) { + if (input.type === 'radio') { + if (input.checked && this._element.classList.contains(ClassName$1.ACTIVE)) { + triggerChangeEvent = false; + } else { + var activeElement = rootElement.querySelector(Selector$1.ACTIVE); + + if (activeElement) { + $(activeElement).removeClass(ClassName$1.ACTIVE); + } + } + } + + if (triggerChangeEvent) { + if (input.hasAttribute('disabled') || rootElement.hasAttribute('disabled') || input.classList.contains('disabled') || rootElement.classList.contains('disabled')) { + return; + } + + input.checked = !this._element.classList.contains(ClassName$1.ACTIVE); + $(input).trigger('change'); + } + + input.focus(); + addAriaPressed = false; + } + } + + if (addAriaPressed) { + this._element.setAttribute('aria-pressed', !this._element.classList.contains(ClassName$1.ACTIVE)); + } + + if (triggerChangeEvent) { + $(this._element).toggleClass(ClassName$1.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$1.CLICK_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) { + event.preventDefault(); + var button = event.target; + + if (!$(button).hasClass(ClassName$1.BUTTON)) { + button = $(button).closest(Selector$1.BUTTON); + } + + Button._jQueryInterface.call($(button), 'toggle'); + }).on(Event$1.FOCUS_BLUR_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) { + var button = $(event.target).closest(Selector$1.BUTTON)[0]; + $(button).toggleClass(ClassName$1.FOCUS, /^focus(in)?$/.test(event.type)); + }); + /** + * ------------------------------------------------------------------------ + * 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.3.1'; + 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', + PREV: 'prev', + LEFT: 'left', + RIGHT: 'right' + }; + var Event$2 = { + SLIDE: "slide" + EVENT_KEY$2, + SLID: "slid" + EVENT_KEY$2, + KEYDOWN: "keydown" + EVENT_KEY$2, + MOUSEENTER: "mouseenter" + EVENT_KEY$2, + MOUSELEAVE: "mouseleave" + EVENT_KEY$2, + TOUCHSTART: "touchstart" + EVENT_KEY$2, + TOUCHMOVE: "touchmove" + EVENT_KEY$2, + TOUCHEND: "touchend" + EVENT_KEY$2, + POINTERDOWN: "pointerdown" + EVENT_KEY$2, + POINTERUP: "pointerup" + EVENT_KEY$2, + DRAG_START: "dragstart" + EVENT_KEY$2, + LOAD_DATA_API: "load" + EVENT_KEY$2 + DATA_API_KEY$2, + CLICK_DATA_API: "click" + EVENT_KEY$2 + DATA_API_KEY$2 + }; + var ClassName$2 = { + CAROUSEL: 'carousel', + ACTIVE: 'active', + SLIDE: 'slide', + RIGHT: 'carousel-item-right', + LEFT: 'carousel-item-left', + NEXT: 'carousel-item-next', + PREV: 'carousel-item-prev', + ITEM: 'carousel-item', + POINTER_EVENT: 'pointer-event' + }; + var Selector$2 = { + ACTIVE: '.active', + ACTIVE_ITEM: '.active.carousel-item', + ITEM: '.carousel-item', + ITEM_IMG: '.carousel-item img', + NEXT_PREV: '.carousel-item-next, .carousel-item-prev', + INDICATORS: '.carousel-indicators', + DATA_SLIDE: '[data-slide], [data-slide-to]', + 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$2.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$2.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$2.ACTIVE_ITEM); + + var activeIndex = this._getItemIndex(this._activeElement); + + if (index > this._items.length - 1 || index < 0) { + return; + } + + if (this._isSliding) { + $(this._element).one(Event$2.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 = _objectSpread({}, 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; // 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$2.KEYDOWN, function (event) { + return _this2._keydown(event); + }); + } + + if (this._config.pause === 'hover') { + $(this._element).on(Event$2.MOUSEENTER, function (event) { + return _this2.pause(event); + }).on(Event$2.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$2.ITEM_IMG)).on(Event$2.DRAG_START, function (e) { + return e.preventDefault(); + }); + + if (this._pointerEvent) { + $(this._element).on(Event$2.POINTERDOWN, function (event) { + return start(event); + }); + $(this._element).on(Event$2.POINTERUP, function (event) { + return end(event); + }); + + this._element.classList.add(ClassName$2.POINTER_EVENT); + } else { + $(this._element).on(Event$2.TOUCHSTART, function (event) { + return start(event); + }); + $(this._element).on(Event$2.TOUCHMOVE, function (event) { + return move(event); + }); + $(this._element).on(Event$2.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; + + default: + } + }; + + _proto._getItemIndex = function _getItemIndex(element) { + this._items = element && element.parentNode ? [].slice.call(element.parentNode.querySelectorAll(Selector$2.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$2.ACTIVE_ITEM)); + + var slideEvent = $.Event(Event$2.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$2.ACTIVE)); + $(indicators).removeClass(ClassName$2.ACTIVE); + + var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]; + + if (nextIndicator) { + $(nextIndicator).addClass(ClassName$2.ACTIVE); + } + } + }; + + _proto._slide = function _slide(direction, element) { + var _this4 = this; + + var activeElement = this._element.querySelector(Selector$2.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 = ClassName$2.LEFT; + orderClassName = ClassName$2.NEXT; + eventDirectionName = Direction.LEFT; + } else { + directionalClassName = ClassName$2.RIGHT; + orderClassName = ClassName$2.PREV; + eventDirectionName = Direction.RIGHT; + } + + if (nextElement && $(nextElement).hasClass(ClassName$2.ACTIVE)) { + 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$2.SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }); + + if ($(this._element).hasClass(ClassName$2.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(ClassName$2.ACTIVE); + $(activeElement).removeClass(ClassName$2.ACTIVE + " " + orderClassName + " " + directionalClassName); + _this4._isSliding = false; + setTimeout(function () { + return $(_this4._element).trigger(slidEvent); + }, 0); + }).emulateTransitionEnd(transitionDuration); + } else { + $(activeElement).removeClass(ClassName$2.ACTIVE); + $(nextElement).addClass(ClassName$2.ACTIVE); + 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 = _objectSpread({}, Default, $(this).data()); + + if (typeof config === 'object') { + _config = _objectSpread({}, _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(ClassName$2.CAROUSEL)) { + return; + } + + var config = _objectSpread({}, $(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$2.CLICK_DATA_API, Selector$2.DATA_SLIDE, Carousel._dataApiClickHandler); + $(window).on(Event$2.LOAD_DATA_API, function () { + var carousels = [].slice.call(document.querySelectorAll(Selector$2.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.3.1'; + 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$3 = { + SHOW: "show" + EVENT_KEY$3, + SHOWN: "shown" + EVENT_KEY$3, + HIDE: "hide" + EVENT_KEY$3, + HIDDEN: "hidden" + EVENT_KEY$3, + CLICK_DATA_API: "click" + EVENT_KEY$3 + DATA_API_KEY$3 + }; + var ClassName$3 = { + SHOW: 'show', + COLLAPSE: 'collapse', + COLLAPSING: 'collapsing', + COLLAPSED: 'collapsed' + }; + var Dimension = { + WIDTH: 'width', + HEIGHT: 'height' + }; + var Selector$3 = { + ACTIVES: '.show, .collapsing', + DATA_TOGGLE: '[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$3.DATA_TOGGLE)); + + 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(ClassName$3.SHOW)) { + this.hide(); + } else { + this.show(); + } + }; + + _proto.show = function show() { + var _this = this; + + if (this._isTransitioning || $(this._element).hasClass(ClassName$3.SHOW)) { + return; + } + + var actives; + var activesData; + + if (this._parent) { + actives = [].slice.call(this._parent.querySelectorAll(Selector$3.ACTIVES)).filter(function (elem) { + if (typeof _this._config.parent === 'string') { + return elem.getAttribute('data-parent') === _this._config.parent; + } + + return elem.classList.contains(ClassName$3.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$3.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(ClassName$3.COLLAPSE).addClass(ClassName$3.COLLAPSING); + this._element.style[dimension] = 0; + + if (this._triggerArray.length) { + $(this._triggerArray).removeClass(ClassName$3.COLLAPSED).attr('aria-expanded', true); + } + + this.setTransitioning(true); + + var complete = function complete() { + $(_this._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).addClass(ClassName$3.SHOW); + _this._element.style[dimension] = ''; + + _this.setTransitioning(false); + + $(_this._element).trigger(Event$3.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(ClassName$3.SHOW)) { + return; + } + + var startEvent = $.Event(Event$3.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(ClassName$3.COLLAPSING).removeClass(ClassName$3.COLLAPSE).removeClass(ClassName$3.SHOW); + 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(ClassName$3.SHOW)) { + $(trigger).addClass(ClassName$3.COLLAPSED).attr('aria-expanded', false); + } + } + } + } + + this.setTransitioning(true); + + var complete = function complete() { + _this2.setTransitioning(false); + + $(_this2._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).trigger(Event$3.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 = _objectSpread({}, 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(ClassName$3.SHOW); + + if (triggerArray.length) { + $(triggerArray).toggleClass(ClassName$3.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 = _objectSpread({}, Default$1, $this.data(), typeof config === 'object' && config ? config : {}); + + if (!data && _config.toggle && /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$3.CLICK_DATA_API, Selector$3.DATA_TOGGLE, 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.14.7 + * @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'; + + var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox']; + var timeoutDuration = 0; + for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) { + if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) { + timeoutDuration = 1; + break; + } + } + + 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)); + } + + 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.right - result.left; + var height = sizes.height || element.clientHeight || result.bottom - result.top; + + 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, 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, 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 explicity 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; + var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom); + + 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' + }, + + /** + * 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 {HTMLElement|referenceObject} reference - The reference element used to position the popper + * @param {HTMLElement} popper - The HTML 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.3.1'; + 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$4 = { + HIDE: "hide" + EVENT_KEY$4, + HIDDEN: "hidden" + EVENT_KEY$4, + SHOW: "show" + EVENT_KEY$4, + SHOWN: "shown" + EVENT_KEY$4, + CLICK: "click" + EVENT_KEY$4, + CLICK_DATA_API: "click" + EVENT_KEY$4 + DATA_API_KEY$4, + KEYDOWN_DATA_API: "keydown" + EVENT_KEY$4 + DATA_API_KEY$4, + KEYUP_DATA_API: "keyup" + EVENT_KEY$4 + DATA_API_KEY$4 + }; + var ClassName$4 = { + DISABLED: 'disabled', + SHOW: 'show', + DROPUP: 'dropup', + DROPRIGHT: 'dropright', + DROPLEFT: 'dropleft', + MENURIGHT: 'dropdown-menu-right', + MENULEFT: 'dropdown-menu-left', + POSITION_STATIC: 'position-static' + }; + var Selector$4 = { + DATA_TOGGLE: '[data-toggle="dropdown"]', + FORM_CHILD: '.dropdown form', + MENU: '.dropdown-menu', + NAVBAR_NAV: '.navbar-nav', + VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)' + }; + var AttachmentMap = { + TOP: 'top-start', + TOPEND: 'top-end', + BOTTOM: 'bottom-start', + BOTTOMEND: 'bottom-end', + RIGHT: 'right-start', + RIGHTEND: 'right-end', + LEFT: 'left-start', + LEFTEND: 'left-end' + }; + var Default$2 = { + offset: 0, + flip: true, + boundary: 'scrollParent', + reference: 'toggle', + display: 'dynamic' + }; + var DefaultType$2 = { + offset: '(number|string|function)', + flip: 'boolean', + boundary: '(string|element)', + reference: '(string|element)', + display: 'string' + /** + * ------------------------------------------------------------------------ + * 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(ClassName$4.DISABLED)) { + return; + } + + var parent = Dropdown._getParentFromElement(this._element); + + var isActive = $(this._menu).hasClass(ClassName$4.SHOW); + + Dropdown._clearMenus(); + + if (isActive) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var showEvent = $.Event(Event$4.SHOW, relatedTarget); + $(parent).trigger(showEvent); + + if (showEvent.isDefaultPrevented()) { + return; + } // Disable totally Popper.js for Dropdown in Navbar + + + if (!this._inNavbar) { + /** + * 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(ClassName$4.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$4.NAVBAR_NAV).length === 0) { + $(document.body).children().on('mouseover', null, $.noop); + } + + this._element.focus(); + + this._element.setAttribute('aria-expanded', true); + + $(this._menu).toggleClass(ClassName$4.SHOW); + $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.SHOWN, relatedTarget)); + }; + + _proto.show = function show() { + if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || $(this._menu).hasClass(ClassName$4.SHOW)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var showEvent = $.Event(Event$4.SHOW, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(showEvent); + + if (showEvent.isDefaultPrevented()) { + return; + } + + $(this._menu).toggleClass(ClassName$4.SHOW); + $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.SHOWN, relatedTarget)); + }; + + _proto.hide = function hide() { + if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || !$(this._menu).hasClass(ClassName$4.SHOW)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var hideEvent = $.Event(Event$4.HIDE, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + return; + } + + $(this._menu).toggleClass(ClassName$4.SHOW); + $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, 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$4.CLICK, function (event) { + event.preventDefault(); + event.stopPropagation(); + + _this.toggle(); + }); + }; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread({}, 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$4.MENU); + } + } + + return this._menu; + }; + + _proto._getPlacement = function _getPlacement() { + var $parentDropdown = $(this._element.parentNode); + var placement = AttachmentMap.BOTTOM; // Handle dropup + + if ($parentDropdown.hasClass(ClassName$4.DROPUP)) { + placement = AttachmentMap.TOP; + + if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) { + placement = AttachmentMap.TOPEND; + } + } else if ($parentDropdown.hasClass(ClassName$4.DROPRIGHT)) { + placement = AttachmentMap.RIGHT; + } else if ($parentDropdown.hasClass(ClassName$4.DROPLEFT)) { + placement = AttachmentMap.LEFT; + } else if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) { + placement = AttachmentMap.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 = _objectSpread({}, 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 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$4.DATA_TOGGLE)); + + 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(ClassName$4.SHOW)) { + 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$4.HIDE, 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'); + $(dropdownMenu).removeClass(ClassName$4.SHOW); + $(parent).removeClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, 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$4.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + if (this.disabled || $(this).hasClass(ClassName$4.DISABLED)) { + return; + } + + var parent = Dropdown._getParentFromElement(this); + + var isActive = $(parent).hasClass(ClassName$4.SHOW); + + if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { + if (event.which === ESCAPE_KEYCODE) { + var toggle = parent.querySelector(Selector$4.DATA_TOGGLE); + $(toggle).trigger('focus'); + } + + $(this).trigger('click'); + return; + } + + var items = [].slice.call(parent.querySelectorAll(Selector$4.VISIBLE_ITEMS)); + + 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$4.KEYDOWN_DATA_API, Selector$4.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event$4.KEYDOWN_DATA_API, Selector$4.MENU, Dropdown._dataApiKeydownHandler).on(Event$4.CLICK_DATA_API + " " + Event$4.KEYUP_DATA_API, Dropdown._clearMenus).on(Event$4.CLICK_DATA_API, Selector$4.DATA_TOGGLE, function (event) { + event.preventDefault(); + event.stopPropagation(); + + Dropdown._jQueryInterface.call($(this), 'toggle'); + }).on(Event$4.CLICK_DATA_API, Selector$4.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.3.1'; + 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$5 = { + HIDE: "hide" + EVENT_KEY$5, + HIDDEN: "hidden" + EVENT_KEY$5, + SHOW: "show" + EVENT_KEY$5, + SHOWN: "shown" + EVENT_KEY$5, + FOCUSIN: "focusin" + EVENT_KEY$5, + RESIZE: "resize" + EVENT_KEY$5, + CLICK_DISMISS: "click.dismiss" + EVENT_KEY$5, + KEYDOWN_DISMISS: "keydown.dismiss" + EVENT_KEY$5, + MOUSEUP_DISMISS: "mouseup.dismiss" + EVENT_KEY$5, + MOUSEDOWN_DISMISS: "mousedown.dismiss" + EVENT_KEY$5, + CLICK_DATA_API: "click" + EVENT_KEY$5 + DATA_API_KEY$5 + }; + var ClassName$5 = { + SCROLLABLE: 'modal-dialog-scrollable', + SCROLLBAR_MEASURER: 'modal-scrollbar-measure', + BACKDROP: 'modal-backdrop', + OPEN: 'modal-open', + FADE: 'fade', + SHOW: 'show' + }; + var Selector$5 = { + DIALOG: '.modal-dialog', + MODAL_BODY: '.modal-body', + DATA_TOGGLE: '[data-toggle="modal"]', + DATA_DISMISS: '[data-dismiss="modal"]', + FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top', + 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$5.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(ClassName$5.FADE)) { + this._isTransitioning = true; + } + + var showEvent = $.Event(Event$5.SHOW, { + 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$5.CLICK_DISMISS, Selector$5.DATA_DISMISS, function (event) { + return _this.hide(event); + }); + $(this._dialog).on(Event$5.MOUSEDOWN_DISMISS, function () { + $(_this._element).one(Event$5.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$5.HIDE); + $(this._element).trigger(hideEvent); + + if (!this._isShown || hideEvent.isDefaultPrevented()) { + return; + } + + this._isShown = false; + var transition = $(this._element).hasClass(ClassName$5.FADE); + + if (transition) { + this._isTransitioning = true; + } + + this._setEscapeEvent(); + + this._setResizeEvent(); + + $(document).off(Event$5.FOCUSIN); + $(this._element).removeClass(ClassName$5.SHOW); + $(this._element).off(Event$5.CLICK_DISMISS); + $(this._dialog).off(Event$5.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$5.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 = _objectSpread({}, Default$3, config); + Util.typeCheckConfig(NAME$5, config, DefaultType$3); + return config; + }; + + _proto._showElement = function _showElement(relatedTarget) { + var _this3 = this; + + var transition = $(this._element).hasClass(ClassName$5.FADE); + + 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(ClassName$5.SCROLLABLE)) { + this._dialog.querySelector(Selector$5.MODAL_BODY).scrollTop = 0; + } else { + this._element.scrollTop = 0; + } + + if (transition) { + Util.reflow(this._element); + } + + $(this._element).addClass(ClassName$5.SHOW); + + if (this._config.focus) { + this._enforceFocus(); + } + + var shownEvent = $.Event(Event$5.SHOWN, { + relatedTarget: relatedTarget + }); + + var transitionComplete = function transitionComplete() { + if (_this3._config.focus) { + _this3._element.focus(); + } + + _this3._isTransitioning = false; + $(_this3._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 _this4 = this; + + $(document).off(Event$5.FOCUSIN) // Guard against infinite focus loop + .on(Event$5.FOCUSIN, function (event) { + if (document !== event.target && _this4._element !== event.target && $(_this4._element).has(event.target).length === 0) { + _this4._element.focus(); + } + }); + }; + + _proto._setEscapeEvent = function _setEscapeEvent() { + var _this5 = this; + + if (this._isShown && this._config.keyboard) { + $(this._element).on(Event$5.KEYDOWN_DISMISS, function (event) { + if (event.which === ESCAPE_KEYCODE$1) { + event.preventDefault(); + + _this5.hide(); + } + }); + } else if (!this._isShown) { + $(this._element).off(Event$5.KEYDOWN_DISMISS); + } + }; + + _proto._setResizeEvent = function _setResizeEvent() { + var _this6 = this; + + if (this._isShown) { + $(window).on(Event$5.RESIZE, function (event) { + return _this6.handleUpdate(event); + }); + } else { + $(window).off(Event$5.RESIZE); + } + }; + + _proto._hideModal = function _hideModal() { + var _this7 = 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(ClassName$5.OPEN); + + _this7._resetAdjustments(); + + _this7._resetScrollbar(); + + $(_this7._element).trigger(Event$5.HIDDEN); + }); + }; + + _proto._removeBackdrop = function _removeBackdrop() { + if (this._backdrop) { + $(this._backdrop).remove(); + this._backdrop = null; + } + }; + + _proto._showBackdrop = function _showBackdrop(callback) { + var _this8 = this; + + var animate = $(this._element).hasClass(ClassName$5.FADE) ? ClassName$5.FADE : ''; + + if (this._isShown && this._config.backdrop) { + this._backdrop = document.createElement('div'); + this._backdrop.className = ClassName$5.BACKDROP; + + if (animate) { + this._backdrop.classList.add(animate); + } + + $(this._backdrop).appendTo(document.body); + $(this._element).on(Event$5.CLICK_DISMISS, function (event) { + if (_this8._ignoreBackdropClick) { + _this8._ignoreBackdropClick = false; + return; + } + + if (event.target !== event.currentTarget) { + return; + } + + if (_this8._config.backdrop === 'static') { + _this8._element.focus(); + } else { + _this8.hide(); + } + }); + + if (animate) { + Util.reflow(this._backdrop); + } + + $(this._backdrop).addClass(ClassName$5.SHOW); + + 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(ClassName$5.SHOW); + + var callbackRemove = function callbackRemove() { + _this8._removeBackdrop(); + + if (callback) { + callback(); + } + }; + + if ($(this._element).hasClass(ClassName$5.FADE)) { + 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 = rect.left + rect.right < window.innerWidth; + this._scrollbarWidth = this._getScrollbarWidth(); + }; + + _proto._setScrollbar = function _setScrollbar() { + var _this9 = 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$5.FIXED_CONTENT)); + var stickyContent = [].slice.call(document.querySelectorAll(Selector$5.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) + _this9._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) - _this9._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(ClassName$5.OPEN); + }; + + _proto._resetScrollbar = function _resetScrollbar() { + // Restore fixed content padding + var fixedContent = [].slice.call(document.querySelectorAll(Selector$5.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$5.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 = ClassName$5.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 = _objectSpread({}, 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$5.CLICK_DATA_API, Selector$5.DATA_TOGGLE, function (event) { + var _this10 = this; + + var target; + var selector = Util.getSelectorFromElement(this); + + if (selector) { + target = document.querySelector(selector); + } + + var config = $(target).data(DATA_KEY$5) ? 'toggle' : _objectSpread({}, $(target).data(), $(this).data()); + + if (this.tagName === 'A' || this.tagName === 'AREA') { + event.preventDefault(); + } + + var $target = $(target).one(Event$5.SHOW, function (showEvent) { + if (showEvent.isDefaultPrevented()) { + // Only register focus restorer if modal will actually get shown + return; + } + + $target.one(Event$5.HIDDEN, function () { + if ($(_this10).is(':visible')) { + _this10.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.3.1): 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', '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,[a-z0-9+/]+=*$/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, l = regExp.length; i < l; 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, len); + + if (_ret === "continue") continue; + } + + return createdDocument.body.innerHTML; + } + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$6 = 'tooltip'; + var VERSION$6 = '4.3.1'; + 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' + }; + var AttachmentMap$1 = { + 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 + }; + var HoverState = { + SHOW: 'show', + OUT: 'out' + }; + var Event$6 = { + 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 ClassName$6 = { + FADE: 'fade', + SHOW: 'show' + }; + var Selector$6 = { + TOOLTIP: '.tooltip', + TOOLTIP_INNER: '.tooltip-inner', + ARROW: '.arrow' + }; + var Trigger = { + HOVER: 'hover', + FOCUS: 'focus', + CLICK: 'click', + MANUAL: 'manual' + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + }; + + var Tooltip = + /*#__PURE__*/ + function () { + function Tooltip(element, config) { + /** + * Check for Popper dependency + * Popper - https://popper.js.org + */ + 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(ClassName$6.SHOW)) { + 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'); + + if (this.tip) { + $(this.tip).remove(); + } + + this._isEnabled = null; + this._timeout = null; + this._hoverState = null; + this._activeTrigger = null; + + if (this._popper !== null) { + 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(ClassName$6.FADE); + } + + 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, { + placement: attachment, + modifiers: { + offset: this._getOffset(), + flip: { + behavior: this.config.fallbackPlacement + }, + arrow: { + element: Selector$6.ARROW + }, + preventOverflow: { + boundariesElement: this.config.boundary + } + }, + onCreate: function onCreate(data) { + if (data.originalPlacement !== data.placement) { + _this._handlePopperPlacementChange(data); + } + }, + onUpdate: function onUpdate(data) { + return _this._handlePopperPlacementChange(data); + } + }); + $(tip).addClass(ClassName$6.SHOW); // 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 === HoverState.OUT) { + _this._leave(null, _this); + } + }; + + if ($(this.tip).hasClass(ClassName$6.FADE)) { + 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 !== HoverState.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(ClassName$6.SHOW); // 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(ClassName$6.FADE)) { + 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$6.TOOLTIP_INNER)), this.getTitle()); + $(tip).removeClass(ClassName$6.FADE + " " + ClassName$6.SHOW); + }; + + _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._getOffset = function _getOffset() { + var _this3 = this; + + var offset = {}; + + if (typeof this.config.offset === 'function') { + offset.fn = function (data) { + data.offsets = _objectSpread({}, data.offsets, _this3.config.offset(data.offsets, _this3.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$1[placement.toUpperCase()]; + }; + + _proto._setListeners = function _setListeners() { + var _this4 = this; + + var triggers = this.config.trigger.split(' '); + triggers.forEach(function (trigger) { + if (trigger === 'click') { + $(_this4.element).on(_this4.constructor.Event.CLICK, _this4.config.selector, function (event) { + return _this4.toggle(event); + }); + } else if (trigger !== Trigger.MANUAL) { + var eventIn = trigger === Trigger.HOVER ? _this4.constructor.Event.MOUSEENTER : _this4.constructor.Event.FOCUSIN; + var eventOut = trigger === Trigger.HOVER ? _this4.constructor.Event.MOUSELEAVE : _this4.constructor.Event.FOCUSOUT; + $(_this4.element).on(eventIn, _this4.config.selector, function (event) { + return _this4._enter(event); + }).on(eventOut, _this4.config.selector, function (event) { + return _this4._leave(event); + }); + } + }); + $(this.element).closest('.modal').on('hide.bs.modal', function () { + if (_this4.element) { + _this4.hide(); + } + }); + + if (this.config.selector) { + this.config = _objectSpread({}, 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(ClassName$6.SHOW) || context._hoverState === HoverState.SHOW) { + context._hoverState = HoverState.SHOW; + return; + } + + clearTimeout(context._timeout); + context._hoverState = HoverState.SHOW; + + if (!context.config.delay || !context.config.delay.show) { + context.show(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HoverState.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 = HoverState.OUT; + + if (!context.config.delay || !context.config.delay.hide) { + context.hide(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HoverState.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 = _objectSpread({}, 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) { + var popperInstance = popperData.instance; + this.tip = popperInstance.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(ClassName$6.FADE); + 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$6; + } + }, { + 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.3.1'; + 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 = _objectSpread({}, Tooltip.Default, { + placement: 'right', + trigger: 'click', + content: '', + template: '' + }); + + var DefaultType$5 = _objectSpread({}, Tooltip.DefaultType, { + content: '(string|element|function)' + }); + + var ClassName$7 = { + FADE: 'fade', + SHOW: 'show' + }; + var Selector$7 = { + TITLE: '.popover-header', + CONTENT: '.popover-body' + }; + var Event$7 = { + 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$7.TITLE), this.getTitle()); + + var content = this._getContent(); + + if (typeof content === 'function') { + content = content.call(this.element); + } + + this.setElementContent($tip.find(Selector$7.CONTENT), content); + $tip.removeClass(ClassName$7.FADE + " " + ClassName$7.SHOW); + } // 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$7; + } + }, { + 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.3.1'; + 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$8 = { + ACTIVATE: "activate" + EVENT_KEY$8, + SCROLL: "scroll" + EVENT_KEY$8, + LOAD_DATA_API: "load" + EVENT_KEY$8 + DATA_API_KEY$6 + }; + var ClassName$8 = { + DROPDOWN_ITEM: 'dropdown-item', + DROPDOWN_MENU: 'dropdown-menu', + ACTIVE: 'active' + }; + var Selector$8 = { + DATA_SPY: '[data-spy="scroll"]', + ACTIVE: '.active', + NAV_LIST_GROUP: '.nav, .list-group', + NAV_LINKS: '.nav-link', + NAV_ITEMS: '.nav-item', + LIST_ITEMS: '.list-group-item', + DROPDOWN: '.dropdown', + DROPDOWN_ITEMS: '.dropdown-item', + DROPDOWN_TOGGLE: '.dropdown-toggle' + }; + var OffsetMethod = { + OFFSET: 'offset', + 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$8.NAV_LINKS + "," + (this._config.target + " " + Selector$8.LIST_ITEMS + ",") + (this._config.target + " " + Selector$8.DROPDOWN_ITEMS); + this._offsets = []; + this._targets = []; + this._activeTarget = null; + this._scrollHeight = 0; + $(this._scrollElement).on(Event$8.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 ? OffsetMethod.OFFSET : OffsetMethod.POSITION; + var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method; + var offsetBase = offsetMethod === OffsetMethod.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 = _objectSpread({}, Default$6, typeof config === 'object' && config ? config : {}); + + if (typeof config.target !== 'string') { + 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; + } + + var offsetLength = this._offsets.length; + + for (var i = offsetLength; 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(ClassName$8.DROPDOWN_ITEM)) { + $link.closest(Selector$8.DROPDOWN).find(Selector$8.DROPDOWN_TOGGLE).addClass(ClassName$8.ACTIVE); + $link.addClass(ClassName$8.ACTIVE); + } else { + // Set triggered link as active + $link.addClass(ClassName$8.ACTIVE); // Set triggered links parents as active + // With both