diff --git a/docs/en/API/Application-Configuration.md b/docs/en/API/Application-Configuration.md new file mode 100644 index 0000000000..db624987c5 --- /dev/null +++ b/docs/en/API/Application-Configuration.md @@ -0,0 +1,23 @@ +# Application Configuration Endpoint + +ABP Framework provides a pre-built and standard endpoint that contains some useful information about the application/service. Here, the list of some fundamental information at this endpoint: + +* [Localization](Localization.md) values, supported and the current language of the application. +* Available and granted [policies](Authorization.md) (permissions) for the current user. +* [Setting](Settings.md) values for the current user. +* Info about the [current user](CurrentUser.md) (like id and user name). +* Info about the current [tenant](Multi-Tenancy.md) (like id and name). +* [Time zone](Timing.md) information for the current user and the [clock](Timing.md) type of the application. + +## HTTP API + +If you navigate to the `/api/abp/application-configuration` URL of an ABP Framework based web application or HTTP Service, you can access the configuration as a JSON object. This endpoint is useful to create the client of your application. + +## Script + +For ASP.NET Core MVC (Razor Pages) applications, the same configuration values are also available on the JavaScript side. `/Abp/ApplicationConfigurationScript` is the URL of the script that is auto-generated based on the HTTP API above. + +See the [JavaScript API document](../UI/AspNetCore/JavaScript-API/Index.md) for the ASP.NET Core UI. + +Other UI types provide services native to the related platform. For example, see the [Angular UI localization documentation](../UI/Angular/Localization.md) to learn how to use the localization values exposes by this endpoint. + diff --git a/docs/en/API/JavaScript-API/Auth.md b/docs/en/API/JavaScript-API/Auth.md deleted file mode 100644 index 60c9eb8866..0000000000 --- a/docs/en/API/JavaScript-API/Auth.md +++ /dev/null @@ -1,3 +0,0 @@ -# abp.auth JavaScript API - -TODO \ No newline at end of file diff --git a/docs/en/Application-Services.md b/docs/en/Application-Services.md index f7f7675f46..d28a7290fb 100644 --- a/docs/en/Application-Services.md +++ b/docs/en/Application-Services.md @@ -2,7 +2,7 @@ Application services are used to implement the **use cases** of an application. They are used to **expose domain logic to the presentation layer**. -An Application Service is called from the presentation layer (optionally) with a **DTO (Data Transfer Object)** as the parameter. It uses domain objects to **perform some specific business logic** and (optionally) returns a DTO back to the presentation layer. Thus, the presentation layer is completely **isolated** from domain layer. +An Application Service is called from the presentation layer (optionally) with a **DTO ([Data Transfer Object](Data-Transfer-Objects.md))** as the parameter. It uses domain objects to **perform some specific business logic** and (optionally) returns a DTO back to the presentation layer. Thus, the presentation layer is completely **isolated** from domain layer. ## Example @@ -205,7 +205,7 @@ See the [object to object mapping document](Object-To-Object-Mapping.md) for mor ## Validation -Inputs of application service methods are automatically validated (like ASP.NET Core controller actions). You can use the standard data annotation attributes or custom validation method to perform the validation. ABP also ensures that the input is not null. +Inputs of application service methods are automatically validated (like ASP.NET Core controller actions). You can use the standard data annotation attributes or a custom validation method to perform the validation. ABP also ensures that the input is not null. See the [validation document](Validation.md) for more. diff --git a/docs/en/AspNet-Boilerplate-Migration-Guide.md b/docs/en/AspNet-Boilerplate-Migration-Guide.md index 39dd01eaa4..9929b6d24d 100644 --- a/docs/en/AspNet-Boilerplate-Migration-Guide.md +++ b/docs/en/AspNet-Boilerplate-Migration-Guide.md @@ -595,7 +595,7 @@ ABP Framework separates it and provides the setting management module (pre-added ASP.NET Boilerplate has a static `Clock` service ([see](https://aspnetboilerplate.com/Pages/Documents/Timing)) which is used to abstract the `DateTime` kind, so you can easily switch between Local and UTC times. You don't inject it, but just use the `Clock.Now` static method to obtain the current time. -ABP Framework has the `IClock` service ([see](Clock.md)) which has a similar goal, but now you need to inject it whenever you need it. +ABP Framework has the `IClock` service ([see](Timing.md)) which has a similar goal, but now you need to inject it whenever you need it. ### Event Bus diff --git a/docs/en/AspNetCore/JavaScript-API/Auth.md b/docs/en/AspNetCore/JavaScript-API/Auth.md deleted file mode 100644 index fb83bcfbff..0000000000 --- a/docs/en/AspNetCore/JavaScript-API/Auth.md +++ /dev/null @@ -1,3 +0,0 @@ -This document has moved. - -[Click to navigate to JavaScript Auth document](../../API/JavaScript-API/Auth.md) \ No newline at end of file diff --git a/docs/en/AspNetCore/JavaScript-API/Index.md b/docs/en/AspNetCore/JavaScript-API/Index.md deleted file mode 100644 index 1ed7f0285c..0000000000 --- a/docs/en/AspNetCore/JavaScript-API/Index.md +++ /dev/null @@ -1,3 +0,0 @@ -This document has moved. - -[Click to navigate to JavaScript API document](../../API/JavaScript-API/Index.md) \ No newline at end of file diff --git a/docs/en/Blob-Storing-Azure.md b/docs/en/Blob-Storing-Azure.md index 9bf912c0d2..d43f8b3a43 100644 --- a/docs/en/Blob-Storing-Azure.md +++ b/docs/en/Blob-Storing-Azure.md @@ -1,3 +1,59 @@ # BLOB Storing Azure Provider -This feature will be available with v3.0! \ No newline at end of file +BLOB Storing Azure Provider can store BLOBs in [Azure Blob storage](https://azure.microsoft.com/en-us/services/storage/blobs/). + +> Read the [BLOB Storing document](Blob-Storing.md) to understand how to use the BLOB storing system. This document only covers how to configure containers to use a Azure BLOB as the storage provider. + +## Installation + +Use the ABP CLI to add [Volo.Abp.BlobStoring.Azure](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Azure) NuGet package to your project: + +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.BlobStoring.Azure` package. +* Run `abp add-package Volo.Abp.BlobStoring.Azure` command. + +If you want to do it manually, install the [Volo.Abp.BlobStoring.Azure](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Azure) NuGet package to your project and add `[DependsOn(typeof(AbpBlobStoringAzureModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. + +## Configuration + +Configuration is done in the `ConfigureServices` method of your [module](Module-Development-Basics.md) class, as explained in the [BLOB Storing document](Blob-Storing.md). + +**Example: Configure to use the azure storage provider by default** + +````csharp +Configure(options => +{ + options.Containerscontainer.UseAzure(azure => + { + azure.ConnectionString = "your azure connection string"; + azure.ContainerName = "your azure container name"; + azure.CreateContainerIfNotExists = false; + }); +}); +```` + +> See the [BLOB Storing document](Blob-Storing.md) to learn how to configure this provider for a specific container. + +### Options + +* **ConnectionString** (string): A connection string includes the authorization information required for your application to access data in an Azure Storage account at runtime using Shared Key authorization. Please refer to Azure documentation: https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string +* **ContainerName** (string): You can specify the container name in azure. If this is not specified, it uses the name of the BLOB container defined with the `BlogContainerName` attribute (see the [BLOB storing document](Blob-Storing.md)). Please note that Azure has some **rules for naming containers**. A container name must be a valid DNS name, conforming to the [following naming rules](https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names): + * Container names must start or end with a letter or number, and can contain only letters, numbers, and the dash (-) character. + * Every dash (-) character must be immediately preceded and followed by a letter or number; consecutive dashes are not permitted in container names. + * All letters in a container name must be **lowercase**. + * Container names must be from **3** through **63** characters long. +* **CreateContainerIfNotExists** (bool): Default value is `false`, If a container does not exist in azure, `AzureBlobProvider` will try to create it. + + +## Azure Blob Name Calculator + +Azure Blob Provider organizes BLOB name and implements some conventions. The full name of a BLOB is determined by the following rules by default: + +* Appends `host` string if [current tenant](Multi-Tenancy.md) is `null` (or multi-tenancy is disabled for the container - see the [BLOB Storing document](Blob-Storing.md) to learn how to disable multi-tenancy for a container). +* Appends `tenants/` string if current tenant is not `null`. +* Appends the BLOB name. + +## Other Services + +* `AzureBlobProvider` is the main service that implements the Azure BLOB storage provider, if you want to override/replace it via [dependency injection](Dependency-Injection.md) (don't replace `IBlobProvider` interface, but replace `AzureBlobProvider` class). +* `IAzureBlobNameCalculator` is used to calculate the full BLOB name (that is explained above). It is implemented by the `DefaultAzureBlobNameCalculator` by default. diff --git a/docs/en/Blob-Storing-Custom-Provider.md b/docs/en/Blob-Storing-Custom-Provider.md index cb3364cd80..0b1255e0b7 100644 --- a/docs/en/Blob-Storing-Custom-Provider.md +++ b/docs/en/Blob-Storing-Custom-Provider.md @@ -174,4 +174,4 @@ public class MyCustomBlobProvider : BlobProviderBase, ITransientDependency ## Contribute? -If you create a new provider and you think it can be useful for other developers, please consider to [contribute](Contribution/Index.md) to the ABP Framework on GitHub. \ No newline at end of file +If you create a new provider and you think it can be useful for other developers, please consider to [contribute](Contribution/Index.md) to the ABP Framework on GitHub. diff --git a/docs/en/Blob-Storing-Database.md b/docs/en/Blob-Storing-Database.md index b413882227..69b9232ee8 100644 --- a/docs/en/Blob-Storing-Database.md +++ b/docs/en/Blob-Storing-Database.md @@ -55,6 +55,8 @@ If you want to use a separate database for BLOB storage, use the `AbpBlobStoring ### Configuring the Containers +If you are using only the database storage provider, you don't need to manually configure it, since it is automatically done. If you are using multiple storage providers, you may want to configure it. + Configuration is done in the `ConfigureServices` method of your [module](Module-Development-Basics.md) class, as explained in the [BLOB Storing document](Blob-Storing.md). **Example: Configure to use the database storage provider by default** diff --git a/docs/en/Clock.md b/docs/en/Clock.md deleted file mode 100644 index 46ef1235e0..0000000000 --- a/docs/en/Clock.md +++ /dev/null @@ -1,3 +0,0 @@ -# Clock - -TODO \ No newline at end of file diff --git a/docs/en/Data-Seeding.md b/docs/en/Data-Seeding.md index 0b424e7e0d..d6236da9d9 100644 --- a/docs/en/Data-Seeding.md +++ b/docs/en/Data-Seeding.md @@ -1,3 +1,160 @@ # Data Seeding -TODO \ No newline at end of file +## Introduction + +Some applications (or modules) using a database may need to have some **initial data** to be able to properly start and run. For example, an **admin user** & roles must be available at the beginning. Otherwise you can not **login** to the application to create new users and roles. + +Data seeding is also useful for [testing](Testing.md) purpose, so your automatic tests can assume some initial data available in the database. + +### Why a Data Seed System? + +While EF Core Data Seeding system provides a way, it is very limited and doesn't cover production scenarios. Also, it is only for EF Core. + +ABP Framework provides a data seed system that is; + +* **Modular**: Any [module](Module-Development-Basics.md) can silently contribute to the data seeding process without knowing and effecting each other. In this way, a module seeds its own initial data. +* **Database Independent**: It is not only for EF Core, it also works for other database providers (like [MongoDB](MongoDB.md)). +* **Production Ready**: It solves the problems on production environments. See the "*On Production*" section below. +* **Dependency Injection**: It takes the full advantage of dependency injection, so you can use any internal or external service while seeding the initial data. Actually, you can do much more than data seeding. + +## IDataSeedContributor + +`IDataSeedContributor` is the interface that should be implemented in order to seed data to the database. + +**Example: Seed one initial book to the database if there is no book** + +````csharp +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 BookStoreDataSeedContributor + : IDataSeedContributor, ITransientDependency + { + private readonly IRepository _bookRepository; + private readonly IGuidGenerator _guidGenerator; + + public BookStoreDataSeedContributor( + IRepository bookRepository, + IGuidGenerator guidGenerator) + { + _bookRepository = bookRepository; + _guidGenerator = guidGenerator; + } + + public async Task SeedAsync(DataSeedContext context) + { + if (await _bookRepository.GetCountAsync() > 0) + { + return; + } + + var book = new Book( + id: _guidGenerator.Create(), + name: "The Hitchhiker's Guide to the Galaxy", + type: BookType.ScienceFiction, + publishDate: new DateTime(1979, 10, 12), + price: 42 + ); + + await _bookRepository.InsertAsync(book); + } + } +} +```` + +* `IDataSeedContributor` defines the `SeedAsync` method to execute the **data seed logic**. +* It is typical to **check database** if the seeding data is already present. +* You can **inject** and service and perform any logic needed to seed the data. + +> Data seed contributors are automatically discovered by the ABP Framework and executed as a part of the data seed process. + +### DataSeedContext + +`DataSeedContext` contains `TenantId` if your application is [multi-tenant](Multi-Tenancy.md), so you can use this value while inserting data or performing custom logic based on the tenant. + +`DataSeedContext` also contains name-value style configuration parameters for passing to the seeder contributors from the `IDataSeeder`. + +## Modularity + +An application can have multiple data seed contributor (`IDataSeedContributor`) class. So, any reusable module can also implement this interface to seed its own initial data. + +For example, the [Identity Module](Modules/Identity.md) has a data seed contributor that creates an admin role and admin user and assign all the permissions. + +## IDataSeeder + +> You typically never need to directly use the `IDataSeeder` service since it is already done if you've started with the [application startup template](Startup-Templates/Application.md). But its suggested to read it to understand the design behind the data seed system. + +`IDataSeeder` is the main service that is used to seed initial data. It is pretty easy to use; + +````csharp +public class MyService : ITransientDependency +{ + private readonly IDataSeeder _dataSeeder; + + public MyService(IDataSeeder dataSeeder) + { + _dataSeeder = dataSeeder; + } + + public async Task FooAsync() + { + await _dataSeeder.SeedAsync(); + } +} +```` + +You can [inject](Dependency-Injection.md) the `IDataSeeder` and use it to seed the initial data when you need. It internally calls all the `IDataSeedContributor` implementations to complete the data seeding. + +It is possible to send named configuration parameters to the `SeedAsync` method as shown below: + +````csharp +await _dataSeeder.SeedAsync( + new DataSeedContext() + .WithProperty("MyProperty1", "MyValue1") + .WithProperty("MyProperty2", 42) +); +```` + +Then the data seed contributors can access to these properties via the `DataSeedContext` explained before. + +If a module needs to a parameter, it should be declared on the [module documentation](Modules/Index.md). For example, the [Identity Module](Modules/Identity.md) can use `AdminEmail` and `AdminPassword` parameters if you provide (otherwise uses the default values). + +### Where & How to Seed Data? + +It is important to understand where & how to execute the `IDataSeeder.SeedAsync()`? + +#### On Production + +The [application startup template](Startup-Templates/Application.md) comes with a *YourProjectName***.DbMigrator** project (Acme.BookStore.DbMigrator on the picture below), which is a **console application** that is responsible to **migrate** the database schema (for relational databases) and **seed** the initial data: + +![bookstore-visual-studio-solution-v3](images/bookstore-visual-studio-solution-v3.png) + +This console application is properly configured for you. It even supports **multi-tenant** scenarios where each tenant has its own database (migrates & seeds all necessary databases). + +It is expected to run this DbMigrator application whenever you **deploy a new version** of your solution to the server. It will migrate your **database schema** (create new tables/fields... etc.) and **seed new initial data** needed to properly run the new version of your solution. Then you can deploy/start your actual application. + +Even if you are using MongoDB or another NoSQL database (that doesn't need to schema migrations), it is recommended to use the DbMigrator application to seed your data or perform your data migration. + +Having such a separate console application has several advantages; + +* You can **run it before** updating your application, so your application will run on the ready database. +* Your application **starts faster** compared to if it seeds the initial data itself. +* Your application can properly run on a **clustered environment** (where multiple instances of your application run concurrently). If you seed data on application startup you would have conflicts in this case. + +#### On Development + +We suggest the same way on development. Run the DbMigrator console application whenever you [create a database migration](https://docs.microsoft.com/en-us/ef/ef6/modeling/code-first/migrations/) (using EF Core `Add-Migration` command, for example) or change the data seed code (will be explained later). + +> You can continue to use the standard `Update-Database` command for EF Core, but it will not seed if you've created a new seed data. + +#### On Testing + +You probably want to seed the data also for automated [testing](Testing.md), so want to use the `IDataSeeder.SeedAsync()`. In the [application startup template](Startup-Templates/Application.md), it is done in the [OnApplicationInitialization](Module-Development-Basics.md) method of the *YourProjectName*TestBaseModule class of the TestBase project. + +In addition to the standard seed data (that is also used on production), you may want to seed additional data unique to the automated tests. If so, you can create a new data seed contributor in the test project to have more data to work on. \ No newline at end of file diff --git a/docs/en/Data-Transfer-Objects.md b/docs/en/Data-Transfer-Objects.md index b0d80246f7..bc81205b66 100644 --- a/docs/en/Data-Transfer-Objects.md +++ b/docs/en/Data-Transfer-Objects.md @@ -1,3 +1,280 @@ -## Data Transfer Objects +# Data Transfer Objects -TODO \ No newline at end of file +## Introduction + +**Data Transfer Objects** (DTO) are used to transfer data between the **Application Layer** and the **Presentation Layer** or other type of clients. + +Typically, an [application service](Application-Services.md) is called from the presentation layer (optionally) with a **DTO** as the parameter. It uses domain objects to **perform some specific business logic** and (optionally) returns a DTO back to the presentation layer. Thus, the presentation layer is completely **isolated** from domain layer. + +### The Need for DTOs + +> **You can skip this section** if you feel that you know and confirm the benefits of using DTOs. + +At first, creating a DTO class for each application service method can be seen as tedious and time-consuming work. However, they can save your application if you correctly use them. Why & how? + +#### Abstraction of the Domain Layer + +DTOs provide an efficient way of **abstracting domain objects** from the presentation layer. In effect, your **layers** are correctly separated. If you want to change the presentation layer completely, you can continue with the existing application and domain layers. Alternatively, you can re-write your domain layer, completely change the database schema, entities and O/RM framework, all without changing the presentation layer. This, of course, is as long as the contracts (method signatures and DTOs) of your application services remain unchanged. + +#### Data Hiding + +Say you have a `User` entity with the properties Id, Name, EmailAddress and Password. If a `GetAllUsers()` method of a `UserAppService` returns a `List`, anyone can access the passwords of all your users, even if you do not show it on the screen. It's not just about security, it's about data hiding. Application services should return only what it needs by the presentation layer (or client). Not more, not less. + +#### Serialization & Lazy Load Problems + +When you return data (an object) to the presentation layer, it's most likely serialized. For example, in a REST API that returns JSON, your object will be serialized to JSON and sent to the client. Returning an Entity to the presentation layer can be problematic in that regard, especially if you are using a relational database and an ORM provider like Entity Framework Core. How? + +In a real-world application, your entities may have references to each other. The `User` entity can have a reference to it's `Role`s. If you want to serialize `User`, its `Role`s are also serialized. The `Role` class may have a `List` and the `Permission` class can has a reference to a `PermissionGroup` class and so on... Imagine all of these objects being serialized at once. You could easily and accidentally serialize your whole database! Also, if your objects have circular references, they may **not** be serialized at all. + +What's the solution? Marking properties as `NonSerialized`? No, you can not know when it should be serialized and when it shouldn't be. It may be needed in one application service method, and not needed in another. Returning safe, serializable, and specially designed DTOs is a good choice in this situation. + +Almost all O/RM frameworks support lazy-loading. It's a feature that loads entities from the database when they're needed. Say a `User` class has a reference to a `Role` class. When you get a `User` from the database, the `Role` property (or collection) is not filled. When you first read the `Role` property, it's loaded from the database. So, if you return such an Entity to the presentation layer, it will cause it to retrieve additional entities from the database by executing additional queries. If a serialization tool reads the entity, it reads all properties recursively and again your whole database can be retrieved (if there are relations between entities). + +More problems can arise if you use Entities in the presentation layer. **It's best not to reference the domain/business layer assembly in the presentation layer.** + +If you are convinced about using DTOs, we can continue to what ABP Framework provides and suggests about DTOs. + +> ABP doesn't force you to use DTOs, however using DTOs is **strongly suggested as a best practice**. + +## Standard Interfaces & Base Classes + +A DTO is a simple class that has no dependency and you can design it in any way. However, ABP introduces some **interfaces** to determine the **conventions** for naming **standard properties** and **base classes** to **don't repeat yourself** while declaring **common properties**. + +**None of them are required**, but using them **simplifies and standardizes** your application code. + +### Entity Related DTOs + +You typically create DTOs corresponding to your entities, which results similar classes to your entities. ABP Framework provides some base classes to simplify while creating such DTOs. + +#### EntityDto + +`IEntityDto` is a simple interface that only defines an `Id` property. You can implement it or inherit from the `EntityDto` for your DTOs that matches to an [entity](Entities.md). + +**Example:** + +````csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace AbpDemo +{ + public class ProductDto : EntityDto + { + public string Name { get; set; } + //... + } +} +```` + +#### Audited DTOs + +If your entity inherits from audited entity classes (or implements auditing interfaces), you can use the following base classes to create your DTOs: + +* `CreationAuditedEntityDto` +* `CreationAuditedEntityWithUserDto` +* `AuditedEntityDto` +* `AuditedEntityWithUserDto` +* `FullAuditedEntityDto` +* `FullAuditedEntityWithUserDto` + +#### Extensible DTOs + +If you want to use the [object extension system](Object-Extensions.md) for your DTOs, you can use or inherit from the following DTO classes: + +* `ExtensibleObject` implements the `IHasExtraProperties` (other classes inherits this class). +* `ExtensibleEntityDto` +* `ExtensibleCreationAuditedEntityDto` +* `ExtensibleCreationAuditedEntityWithUserDto` +* `ExtensibleAuditedEntityDto` +* `ExtensibleAuditedEntityWithUserDto` +* `ExtensibleFullAuditedEntityDto` +* `ExtensibleFullAuditedEntityWithUserDto` + +### List Results + +It is common to return a list of DTOs to the client. `IListResult` interface and `ListResultDto` class is used to make it standard. + +The definition of the `IListResult` interface: + +````csharp +public interface IListResult +{ + IReadOnlyList Items { get; set; } +} +```` + +**Example: Return a list of products** + +````csharp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace AbpDemo +{ + public class ProductAppService : ApplicationService, IProductAppService + { + private readonly IRepository _productRepository; + + public ProductAppService(IRepository productRepository) + { + _productRepository = productRepository; + } + + public async Task> GetListAsync() + { + //Get entities from the repository + List products = await _productRepository.GetListAsync(); + + //Map entities to DTOs + List productDtos = + ObjectMapper.Map, List>(products); + + //Return the result + return new ListResultDto(productDtos); + } + } +} +```` + +You could simply return the `productDtos` object (and change the method return type) and it has nothing wrong. Returning a `ListResultDto` makes your `List` wrapped into another object as an `Items` property. This has one advantage: You can later add more properties to your return value without breaking your remote clients (when they get the value as a JSON result). So, it is especially suggested when you are developing reusable application modules. + +### Paged & Sorted List Results + +It is more common to request a paged list from server and return a paged list to the client. ABP defines a few interface and classes to standardize it: + +#### Input (Request) Types + +The following interfaces and classes is to standardize the input sent by the clients. + +* `ILimitedResultRequest`: Defines a `MaxResultCount` (`int`) property to request a limited result from the server. +* `IPagedResultRequest`: Inherits from the `ILimitedResultRequest` (so it inherently has the `MaxResultCount` property) and defines a `SkipCount` (`int`) to declare the skip count while requesting a paged result from the server. +* `ISortedResultRequest`: Defines a `Sorting` (`string`) property to request a sorted result from the server. Sorting value can be "*Name*", "*Name DESC*", "*Name ASC, Age DESC*"... etc. +* `IPagedAndSortedResultRequest` inherits from both of the `IPagedResultRequest` and `ISortedResultRequest`, so has `MaxResultCount`, `SkipCount` and `Sorting` properties. + +Instead of implementing the interfaces manually, it is suggested to inherit one of the following base DTO classes: + +* `LimitedResultRequestDto` implements `ILimitedResultRequest`. +* `PagedResultRequestDto` implements `IPagedResultRequest` (and inherits from the `LimitedResultRequestDto`). +* `PagedAndSortedResultRequestDto` implements `IPagedAndSortedResultRequest` (and inherit from the `PagedResultRequestDto`). + +##### Max Result Count + +`LimitedResultRequestDto` (and inherently the others) limits and validates the `MaxResultCount` by the following rules; + +* If the client doesn't set `MaxResultCount`, it is assumed as **10** (the default page size). This value can be changed by setting the `LimitedResultRequestDto.DefaultMaxResultCount` static property. +* If the client sends `MaxResultCount` greater than **1,000**, it produces a **validation error**. It is important to protect the server from abuse of the service. If you want, you can change this value by setting the `LimitedResultRequestDto.MaxMaxResultCount` static property. + +Static properties suggested to be set on application startup since they are static (global). + +#### Output (Response) Types + +The following interfaces and classes is to standardize the output sent to the clients. + +* `IHasTotalCount` defines a `TotalCount` (`long`) property to return the total count of the records in case of paging. +* `IPagedResult` inherits from the `IListResult` and `IHasTotalCount`, so it has the `Items` and `TotalCount` properties. + +Instead of implementing the interfaces manually, it is suggested to inherit one of the following base DTO classes: + +* `PagedResultDto` inherits from the `ListResultDto` and also implements the `IPagedResult`. + +**Example: Request a paged & sorted result from server and return a paged list** + +````csharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; +using Volo.Abp.Domain.Repositories; + +namespace AbpDemo +{ + public class ProductAppService : ApplicationService, IProductAppService + { + private readonly IRepository _productRepository; + + public ProductAppService(IRepository productRepository) + { + _productRepository = productRepository; + } + + public async Task> GetListAsync( + PagedAndSortedResultRequestDto input) + { + //Create the query + var query = _productRepository + .OrderBy(input.Sorting) + .Skip(input.SkipCount) + .Take(input.MaxResultCount); + + //Get total count from the repository + var totalCount = await query.CountAsync(); + + //Get entities from the repository + List products = await query.ToListAsync(); + + //Map entities to DTOs + List productDtos = + ObjectMapper.Map, List>(products); + + //Return the result + return new PagedResultDto(totalCount, productDtos); + } + } +} +```` + +ABP Framework also defines a `PageBy` extension method (that is compatible with the `IPagedResultRequest`) that can be used instead of `Skip` + `Take` calls: + +````csharp +var query = _productRepository + .OrderBy(input.Sorting) + .PageBy(input); +```` + +> Notice that we added `Volo.Abp.EntityFrameworkCore` package to the project to be able to use the `ToListAsync` and `CountAsync` methods since they are not included in the standard LINQ, but defined by the Entity Framework Core. + +See also the [repository documentation](Repositories.md) to if you haven't understood the example code. + +## Related Topics + +### Validation + +Inputs of [application service](Application-Services.md) methods, controller actions, page model inputs... are automatically validated. You can use the standard data annotation attributes or a custom validation method to perform the validation. + +See the [validation document](Validation.md) for more. + +### Object to Object Mapping + +When you create a DTO that is related to an entity, you generally need to map these objects. ABP provides an object to object mapping system to simplify the mapping process. See the following documents: + +* [Object to Object Mapping document](Object-To-Object-Mapping.md) covers all the features. +* [Application Services document](Application-Services.md) provides a full example. + +## Best Practices + +You are free to design your DTO classes. However, there are some best practices & suggestions that you may want to follow. + +### Common Principles + +* DTOs should be **well serializable** since they are generally serialized and deserialized (to JSON or other format). It is suggested to have an empty (parameterless) public constructor if you have another constructor with parameter(s). +* DTOs **should not contain any business logic**, except some formal [validation](Validation.md) code. +* Do not inherit DTOs from entities and **do not reference to entities**. The [application startup template](Startup-Templates/Application.md) already prevents it by separating the projects. +* If you use an auto [object to object mapping](Object-To-Object-Mapping.md) library, like AutoMapper, enable the **mapping configuration validation** to prevent potential bugs. + +### Input DTO Principles + +* Define only the **properties needed** for the use case. Do not include properties not used for the use case, which confuses developers if you do so. + +* **Don't reuse** input DTOs among different application service methods. Because, different use cases will need to and use different properties of the DTO which results some properties are not used in some cases and that makes harder to understand and use the services and causes potential bugs in the future. + +### Output DTO Principles + +* You can **reuse output DTOs** if you **fill all the properties** on all the cases. \ No newline at end of file diff --git a/docs/en/Domain-Driven-Design.md b/docs/en/Domain-Driven-Design.md index 4f2d06be7d..ce33295492 100644 --- a/docs/en/Domain-Driven-Design.md +++ b/docs/en/Domain-Driven-Design.md @@ -2,7 +2,7 @@ ## What is DDD? -ABP framework provides an **infrastructure** to make **DDD** based development easier to implement. DDD is [defined in the Wikipedia](https://en.wikipedia.org/wiki/Domain-driven_design) as below: +ABP framework provides an **infrastructure** to make **Domain Driven Design** based development easier to implement. DDD is [defined in the Wikipedia](https://en.wikipedia.org/wiki/Domain-driven_design) as below: > **Domain-driven design** (**DDD**) is an approach to software development for complex needs by connecting the implementation to an evolving model. The premise of domain-driven design is the following: > @@ -16,7 +16,7 @@ ABP follows DDD principles and patterns to achieve a layered application model w - **Presentation Layer**: Provides an interface to the user. Uses the *Application Layer* to achieve user interactions. - **Application Layer**: Mediates between the Presentation and Domain Layers. Orchestrates business objects to perform specific application tasks. Implements use cases as the application logic. -- **Domain Layer**: Includes business objects and their business rules. This is the heart of the application. +- **Domain Layer**: Includes business objects and the core (domain) business rules. This is the heart of the application. - **Infrastructure Layer**: Provides generic technical capabilities that support higher layers mostly using 3rd-party libraries. ## Contents diff --git a/docs/en/Entities.md b/docs/en/Entities.md index 8a234b94a5..538d298461 100644 --- a/docs/en/Entities.md +++ b/docs/en/Entities.md @@ -26,9 +26,9 @@ public class Book : Entity If your entity's Id type is `Guid`, there are some good practices to implement: * Create a constructor that gets the Id as a parameter and passes to the base class. - * If you don't set a GUID Id, ABP Framework sets it on save, but it is good to have a valid Id on the entity even before saving it to the database. -* If you create an entity with a constructor that takes parameters, also create a `protected` empty constructor. This is used while your database provider reads your entity from the database (on deserialization). -* Don't use the `Guid.NewGuid()` to set the Id! Use [the `IGuidGenerator` service](Guid-Generation.md) while passing the Id from the code that creates the entity. `IGuidGenerator` optimized to generate sequential GUIDs, which is critical for clustered indexes in the relational databases. + * If you don't set a GUID Id, **ABP Framework sets it on save**, but it is good to have a valid Id on the entity even before saving it to the database. +* If you create an entity with a constructor that takes parameters, also create a `private` or `protected` empty constructor. This is used while your database provider reads your entity from the database (on deserialization). +* Don't use the `Guid.NewGuid()` to set the Id! **Use [the `IGuidGenerator` service](Guid-Generation.md)** while passing the Id from the code that creates the entity. `IGuidGenerator` optimized to generate sequential GUIDs, which is critical for clustered indexes in the relational databases. An example entity: @@ -382,7 +382,7 @@ The way to store this dictionary in the database depends on the database provide Extra Properties system is especially useful if you are using a **re-usable module** that defines an entity inside and you want to get/set some data related to this entity in an easy way. -You normally **don't need** to this system for your own entities, because it has the following drawbacks: +You typically **don't need** to use this system for your own entities, because it has the following drawbacks: * It is **not fully type safe** since it works with strings as property names. * It is **not easy to [auto map](Object-To-Object-Mapping.md)** these properties from/to other objects. diff --git a/docs/en/Guid-Generation.md b/docs/en/Guid-Generation.md index 1cd2d6c218..0990f5b6dd 100644 --- a/docs/en/Guid-Generation.md +++ b/docs/en/Guid-Generation.md @@ -106,4 +106,6 @@ Configure(options => { options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary; }); -```` \ No newline at end of file +```` + +> EF Core [integration packages](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Other-DBMS) sets this option to a proper value for the related DBMS. So, most of the times, you don't need to set this option if you are using these integration packages. \ No newline at end of file diff --git a/docs/en/Json.md b/docs/en/Json.md new file mode 100644 index 0000000000..f0082ea9d6 --- /dev/null +++ b/docs/en/Json.md @@ -0,0 +1,3 @@ +# JSON + +TODO \ No newline at end of file diff --git a/docs/en/Modules/Docs.md b/docs/en/Modules/Docs.md index bbed28e8fe..b8f678dc48 100644 --- a/docs/en/Modules/Docs.md +++ b/docs/en/Modules/Docs.md @@ -58,14 +58,18 @@ Now an empty ABP project has been created! You can now run your project and see To login your website enter `admin` as the username and `1q2w3E*` as the password. -### 2- Referencing Docs Module Packages +### 3- Installation Module Docs module packages are hosted on NuGet. There are 4 packages that needs be to installed to your application. Each package has to be installed to the relevant project. +#### 3.1- Use ABP CLI + It is recommended to use the ABP CLI to install the module, open the CMD window in the solution file (`.sln`) directory, and run the following command: `abp add-module Volo.Docs` +#### 3.2- Manually install + Or you can also manually install nuget package to each project: * Install [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget package to `Acme.MyProject.Domain` project. @@ -84,7 +88,7 @@ Or you can also manually install nuget package to each project: `Install-Package Volo.Docs.Web` -### 3- Adding Module Dependencies +##### 3.2.1- Adding Module Dependencies An ABP module must declare `[DependsOn]` attribute if it has a dependency upon another module. Each module has to be added in`[DependsOn]` attribute to the relevant project. @@ -165,6 +169,27 @@ An ABP module must declare `[DependsOn]` attribute if it has a dependency upon a } ``` +##### 3.2.2- Adding NPM Package + +Open `package.json` and add `@abp/docs": "^2.9.0` as shown below: + + ```json + { + "version": "1.0.0", + "name": "my-app", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.9.0", + "@abp/docs": "^2.9.0" + } + } + ``` + + Then open the command line terminal in the `Acme.MyProject.Web` project folder and run the following command: + + 1. `yarn` + 2. `gulp` + ### 4- Database Integration #### 4.1- Entity Framework Integration diff --git a/docs/en/Modules/Virtual-File-Explorer.md b/docs/en/Modules/Virtual-File-Explorer.md index 81059ff870..bae87ba95a 100644 --- a/docs/en/Modules/Virtual-File-Explorer.md +++ b/docs/en/Modules/Virtual-File-Explorer.md @@ -8,19 +8,21 @@ Virtual File Explorer Module provided a simple UI to view all files in [virtual ### Installation -#### 1- Referencing Virtual File Explorer Module Packages +#### 1- Use ABP CLI It is recommended to use the ABP CLI to install the module, open the CMD window in the solution file (`.sln`) directory, and run the following command: `abp add-module Volo.VirtualFileExplorer` +#### 2- Manually install + Or you can also manually install nuget package to `Acme.MyProject.Web` project: * Install [Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget package to `Acme.MyProject.Web` project. `Install-Package Volo.Abp.VirtualFileExplorer.Web` -#### 2- Adding Module Dependencies +##### 2.1- Adding Module Dependencies * Open `MyProjectWebModule.cs`and add `typeof(AbpVirtualFileExplorerWebModule)` as shown below; @@ -40,7 +42,7 @@ Or you can also manually install nuget package to `Acme.MyProject.Web` project: } ``` -#### 3- Adding NPM Package +##### 2.2- Adding NPM Package * Open `package.json` and add `@abp/virtual-file-explorer": "^2.9.0` as shown below: diff --git a/docs/en/Timing.md b/docs/en/Timing.md new file mode 100644 index 0000000000..4d48fa6d51 --- /dev/null +++ b/docs/en/Timing.md @@ -0,0 +1,113 @@ +# Timing + +Working with times & [time zones](https://en.wikipedia.org/wiki/Time_zone) is always tricky, especially if you need to build a **global system** that is used by users in **different time zones**. + +ABP provides a basic infrastructure to make it easy and handle automatically wherever possible. This document covers the ABP Framework services and systems related to time and time zones. + +> If you are creating a local application that runs in a single time zone region, you may not need all these systems. But even in this case, it is suggested to use the `IClock` service introduced in this document. + +## IClock + +`DateTime.Now` returns a `DateTime` object with the **local date & time of the server**. A `DateTime` object **doesn't store the time zone information**. So, you can not know the **absolute date & time** stored in this object. You can only make **assumptions**, like assuming that it was created in UTC+05 time zone. The things especially gets complicated when you save this value to a database and read later, or send it to a client in a **different time zone**. + +One solution to this problem is always use `DateTime.UtcNow` and assume all `DateTime` objects as UTC time. In this was, you can convert it to the time zone of the target client when needed. + +`IClock` provides an abstraction while getting the current time, so you can control the kind of the date time (UTC or local) in a single point in your application. + +**Example: Getting the current time** + +````csharp +using Volo.Abp.DependencyInjection; +using Volo.Abp.Timing; + +namespace AbpDemo +{ + public class MyService : ITransientDependency + { + private readonly IClock _clock; + + public MyService(IClock clock) + { + _clock = clock; + } + + public void Foo() + { + //Get the current time! + var now = _clock.Now; + } + } +} +```` + +* Inject the `IClock` service when you need to get the current time. Common base classes (like ApplicationService) already injects it and provides as a base property - so, you can directly use as `Clock`. +* Use the `Now` property to get the current time. + +> Most of the times, `IClock` is the only service you need to know and use in your application. + +### Clock Options + +`AbpClockOptions` is the [options](Options.md) class that used to set the clock kind. + +**Example: Use UTC Clock** + +````csharp +Configure(options => +{ + options.Kind = DateTimeKind.Utc; +}); +```` + +Write this inside the `ConfigureServices` method of your [module](Module-Development-Basics.md). + +> Default `Kind` is `Unspecified`, that actually make the Clock as it doesn't exists at all. Either make it `Utc` or `Local` if you want to get benefit of the Clock system. + +### DateTime Normalization + +Other important function of the `IClock` is to normalize `DateTime` objects. + +**Example usage:** + +````csharp +DateTime dateTime = ...; //Get from somewhere +var normalizedDateTime = Clock.Normalize(dateTime) +```` + +`Normalize` method works as described below: + +* Converts the given `DateTime` to the UTC (by using the `DateTime.ToUniversalTime()` method) if current Clock is UTC and given `DateTime` is local. +* Converts the given `DateTime` to the local (by using the `DateTime.ToLocalTime()` method) if current Clock is local and given `DateTime` is UTC. +* Sets `Kind` of the given `DateTime` (using the `DateTime.SpecifyKind(...)` method) to the `Kind` of the current Clock if given `DateTime`'s `Kind` is `Unspecified`. + +`Normalize` method is used by the ABP Framework when the it gets a `DateTime` that is not created by `IClock.Now` and may not be compatible with the current Clock type. Examples; + +* `DateTime` type binding in the ASP.NET Core MVC model binding. +* Saving data to and reading data from database via [Entity Framework Core](Entity-Framework-Core.md). +* Working with `DateTime` objects on [JSON deserialization](Json.md). + +#### DisableDateTimeNormalization Attribute + +`DisableDateTimeNormalization` attribute can be used to disable the normalization operation for desired classes or properties. + +### Other IClock Properties + +In addition to the `Now`, `IClock` service has the following properties: + +* `Kind`: Returns a `DateTimeKind` for the currently used clock type (`DateTimeKind.Utc`, `DateTimeKind.Local` or `DateTimeKind.Unspecified`). +* `SupportsMultipleTimezone`: Returns `true` if currently used clock is UTC. + +## Time Zones + +This section covers the ABP Framework infrastructure related to managing time zones. + +### TimeZone Setting + +ABP Framework defines **a setting**, named `Abp.Timing.Timezone`, that can be used to set and get the time zone for a user, [tenant](Multi-Tenancy.md) or globally for the application. The default value is `UTC`. + +See the [setting documentation](Settings.md) to learn more about the setting system. + +### ITimezoneProvider + +`ITimezoneProvider` is a service to simple convert [Windows Time Zone Id](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values) values to [Iana Time Zone Name](https://www.iana.org/time-zones) values and vice verse. It also provides methods to get list of these time zones and get a `TimeZoneInfo` with a given name. + +It has been implemented using the [TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter) library. \ No newline at end of file diff --git a/docs/en/API/JavaScript-API/Index.md b/docs/en/UI/AspNetCore/JavaScript-API/Index.md similarity index 92% rename from docs/en/API/JavaScript-API/Index.md rename to docs/en/UI/AspNetCore/JavaScript-API/Index.md index ca9afca97f..9f584eacfb 100644 --- a/docs/en/API/JavaScript-API/Index.md +++ b/docs/en/UI/AspNetCore/JavaScript-API/Index.md @@ -5,7 +5,7 @@ ABP provides some JavaScript APIs for ASP.NET Core MVC / Razor Pages application ## APIs * abp.ajax -* [abp.auth](Auth.md) +* abp.auth * abp.currentUser * abp.dom * abp.event @@ -20,5 +20,4 @@ ABP provides some JavaScript APIs for ASP.NET Core MVC / Razor Pages application * abp.utils * abp.ResourceLoader * abp.WidgetManager -* Other APIs - +* Other APIs \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Button-groups.md b/docs/en/UI/AspNetCore/Tag-Helpers/Button-groups.md new file mode 100644 index 0000000000..93865e0d74 --- /dev/null +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Button-groups.md @@ -0,0 +1,37 @@ +# Button groups + +## Introduction + +`abp-button-group` is the main container for grouped button elements. + +Basic usage: + +````csharp + + Left + Middle + Right + +```` + +## Demo + +See the [button groups demo page](https://bootstrap-taghelpers.abp.io/Components/Button-groups) to see it in action. + +## Attributes + +### direction + +A value indicates the direction of the buttons. Should be one of the following values: + +* `Horizontal` (default value) +* `Vertical` + +### size + +A value indicates the size of the buttons in the group. Should be one of the following values: + +* `Default` (default value) +* `Small` +* `Medium` +* `Large` diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Carousel.md b/docs/en/UI/AspNetCore/Tag-Helpers/Carousel.md new file mode 100644 index 0000000000..6088977068 --- /dev/null +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Carousel.md @@ -0,0 +1,74 @@ +# Carousel + +## Introduction + +`abp-carousel` is a the abp tag for carousel element. + +Basic usage: + +````csharp + + + + + +```` + + + +## Demo + +See the [carousel_demo page](https://bootstrap-taghelpers.abp.io/Components/Carousel) to see it in action. + +## Attributes + +### id + +A value sets the id of the carousel. If not set, generated id will be set whenever the tag is created. + +### controls + +A value to enable the controls (previous and next buttons) on carousel. Should be one of the following values: + +* `false` +* `true` + +### indicators + +A value to enables the indicators on carousel. Should be one of the following values: + +* `false` +* `true` + +### crossfade + +A value to enables the fade animation instead of slide on carousel. Should be one of the following values: + +* `false` +* `true` + +## abp-carousel-item Attributes + +### caption-title + +A value sets the caption title of the carousel item. + +### caption + +A value sets the caption of the carousel item. + +### src + +A link value sets the source of the image displayed on carousel item. + +### active + +A value to set the active carousel item. Should be one of the following values: + +* `false` +* `true` + +### alt + +A value sets the alternate text for the carousel item image when the image can not be displayed. + diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 6c36d4d7e1..38cbe0d5a4 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -216,6 +216,10 @@ { "text": "GUID Generation", "path": "Guid-Generation.md" + }, + { + "text": "Timing", + "path": "Timing.md" } ] }, @@ -277,7 +281,8 @@ "path": "Application-Services.md" }, { - "text": "Data Transfer Objects" + "text": "Data Transfer Objects", + "path": "Data-Transfer-Objects.md" }, { "text": "Unit Of Work" @@ -296,6 +301,15 @@ { "text": "Dynamic C# API Clients", "path": "API/Dynamic-CSharp-API-Clients.md" + }, + { + "text": "ABP Endpoints", + "items": [ + { + "text": "Application Configuration", + "path": "API/Application-Configuration.md" + } + ] } ] }, @@ -477,6 +491,10 @@ "path": "Dapper.md" } ] + }, + { + "text": "Data Seeding", + "path": "Data-Seeding.md" } ] }, diff --git a/docs/zh-Hans/Modules/Docs.md b/docs/zh-Hans/Modules/Docs.md index 96452f2bd5..63759d1462 100644 --- a/docs/zh-Hans/Modules/Docs.md +++ b/docs/zh-Hans/Modules/Docs.md @@ -58,14 +58,18 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块. 输入用户名 `admin` 密码 `1q2w3E*` 登录到网站. -### 2- 引用文档模块包 +### 3- 安装模块 文档模块包托管在Nuget上面. 需要有四个包安装到你的应用程序中. 每个包必须安装到相关的项目. +#### 3.1- 使用ABP CLI + 建议使用ABP CLI安装模块,在解决方案文件 (`.sln`) 目录打开 `CMD` 窗口,运行以下命令: `abp add-module Volo.Docs` +#### 3.2- 手动安装 + 或者你也可以手动安装nuget包到每个项目: * 安装[Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget包到 `Acme.MyProject.Domain` 项目. @@ -83,7 +87,7 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块. * 安装[Volo.Docs.Web](https://www.nuget.org/packages/Volo.Docs.Domain/) nuget包到 `Acme.MyProject.Web` 项目. `Install-Package Volo.Docs.Web` -### 3- 添加模块依赖 +##### 3.2.1- 添加模块依赖 一个ABP模块必须声明 `[DependsOn]` attribute 如果它依赖于另一个模块. 每个模块都必须在相关的项目的`[DependsOn]`Attribute 中添加. @@ -122,7 +126,6 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块. } ``` - * 打开 `MyProjectApplicationModule.cs`并且添加 `typeof(DocsApplicationModule)` 如下所示; ```csharp @@ -165,6 +168,27 @@ ABP框架的[文档](docs.abp.io)也是使用的此模块. } ``` +##### 3.2.2- 添加NPM包 + +打开 `package.json` 添加 `@abp/docs` 如下所示: + + ```json + { + "version": "1.0.0", + "name": "my-app", + "private": true, + "dependencies": { + "@abp/aspnetcore.mvc.ui.theme.basic": "^2.9.0", + "@abp/docs": "^2.9.0" + } + } + ``` + + 然后在 `Acme.MyProject.Web` 项目目录打开命令行终端运行以下命令: + + 1. `yarn` + 2. `gulp` + ### 4- 数据库集成 #### 4.1- Entity Framework 集成 diff --git a/docs/zh-Hans/Modules/Virtual-File-Explorer.md b/docs/zh-Hans/Modules/Virtual-File-Explorer.md index 375165d221..024b3d23df 100644 --- a/docs/zh-Hans/Modules/Virtual-File-Explorer.md +++ b/docs/zh-Hans/Modules/Virtual-File-Explorer.md @@ -8,19 +8,21 @@ ### 安装 -#### 1- 引用虚拟文件浏览器模块包 +#### 1- 使用ABP CLI 建议使用ABP CLI安装模块,在解决方案文件 (`.sln`) 目录打开 `CMD` 窗口,运行以下命令: `abp add-module Volo.VirtualFileExplorer` +#### 2- 手动安装 + 或者你也可以手动安装nuget包到 `Acme.MyProject.Web` 项目: * 安装[Volo.Abp.VirtualFileExplorer.Web](https://www.nuget.org/packages/Volo.Abp.VirtualFileExplorer.Web/) nuget包到 `Acme.MyProject.Web` 项目. `Install-Package Volo.Abp.VirtualFileExplorer.Web` -#### 2- 添加模块依赖 +##### 2.1- 添加模块依赖 * 打开 `MyProjectWebModule.cs` 并且添加 `typeof(AbpVirtualFileExplorerWebModule)` 如下所示; @@ -40,7 +42,7 @@ } ``` -#### 3- 添加NPM包 +##### 2.2- 添加NPM包 * 打开 `package.json` 添加 `@abp/virtual-file-explorer": "^2.9.0` 如下所示: diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 135afaf4d9..e02d40b1e0 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -301,6 +301,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSy EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle.Devart", "src\Volo.Abp.EntityFrameworkCore.Oracle.Devart\Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj", "{75E5C841-5F36-4C44-A532-57CB8E7FFE15}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure", "src\Volo.Abp.BlobStoring.Azure\Volo.Abp.BlobStoring.Azure.csproj", "{C44242F7-D55D-4867-AAF4-A786E404312E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure.Tests", "test\Volo.Abp.BlobStoring.Azure.Tests\Volo.Abp.BlobStoring.Azure.Tests.csproj", "{A80E9A0B-8932-4B5D-83FB-6751708FD484}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -895,6 +899,14 @@ Global {75E5C841-5F36-4C44-A532-57CB8E7FFE15}.Debug|Any CPU.Build.0 = Debug|Any CPU {75E5C841-5F36-4C44-A532-57CB8E7FFE15}.Release|Any CPU.ActiveCfg = Release|Any CPU {75E5C841-5F36-4C44-A532-57CB8E7FFE15}.Release|Any CPU.Build.0 = Release|Any CPU + {C44242F7-D55D-4867-AAF4-A786E404312E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C44242F7-D55D-4867-AAF4-A786E404312E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C44242F7-D55D-4867-AAF4-A786E404312E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C44242F7-D55D-4867-AAF4-A786E404312E}.Release|Any CPU.Build.0 = Release|Any CPU + {A80E9A0B-8932-4B5D-83FB-6751708FD484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A80E9A0B-8932-4B5D-83FB-6751708FD484}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A80E9A0B-8932-4B5D-83FB-6751708FD484}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A80E9A0B-8932-4B5D-83FB-6751708FD484}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1047,6 +1059,8 @@ Global {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {68443D4A-1608-4039-B995-7AF4CF82E9F8} = {447C8A77-E5F0-4538-8687-7383196D04EA} {75E5C841-5F36-4C44-A532-57CB8E7FFE15} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {C44242F7-D55D-4867-AAF4-A786E404312E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {A80E9A0B-8932-4B5D-83FB-6751708FD484} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs index ffba80fb6f..c92e08365e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs @@ -38,6 +38,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel protected virtual void SetItems(TagHelperContext context, TagHelperOutput output, List itemList) { var itemsHtml = new StringBuilder(""); + itemsHtml.Append("
"); foreach (var carouselItem in itemList) { @@ -46,6 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel itemsHtml.AppendLine(carouselItem.Html); } + itemsHtml.Append("
"); output.Content.SetHtmlContent(itemsHtml.ToString()); } @@ -137,4 +139,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs index f68d4d1450..6ad5b3276e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs @@ -188,7 +188,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations ThreeLetterIsoLanguageName = CultureInfo.CurrentUICulture.ThreeLetterISOLanguageName, DateTimeFormat = new DateTimeFormatDto { - CalendarAlgorithmType = CultureInfo.CurrentUICulture.DateTimeFormat.Calendar.AlgorithmType.ToString(), + CalendarAlgorithmType = + CultureInfo.CurrentUICulture.DateTimeFormat.Calendar.AlgorithmType.ToString(), DateTimeFormatLong = CultureInfo.CurrentUICulture.DateTimeFormat.LongDatePattern, ShortDatePattern = CultureInfo.CurrentUICulture.DateTimeFormat.ShortDatePattern, FullDateTimePattern = CultureInfo.CurrentUICulture.DateTimeFormat.FullDateTimePattern, @@ -238,25 +239,24 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations protected virtual async Task GetTimingConfigAsync() { - var result = new TimingDto(); - var windowsTimeZoneId = await _settingProvider.GetOrNullAsync(TimingSettingNames.TimeZone); - if (!windowsTimeZoneId.IsNullOrWhiteSpace()) + + return new TimingDto { - result.TimeZone = new TimeZone + TimeZone = new TimeZone { Windows = new WindowsTimeZone { TimeZoneId = windowsTimeZoneId }, - Iana = new IanaTimeZone() + Iana = new IanaTimeZone { - TimeZoneName = _timezoneProvider.WindowsToIana(windowsTimeZoneId) + TimeZoneName = windowsTimeZoneId.IsNullOrWhiteSpace() + ? null + : _timezoneProvider.WindowsToIana(windowsTimeZoneId) } - }; - } - - return result; + } + }; } protected virtual ClockDto GetClockConfig() @@ -267,4 +267,4 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations }; } } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xml new file mode 100644 index 0000000000..bc5a74a236 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xsd b/framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj b/framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj new file mode 100644 index 0000000000..1e7c51f4fc --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj @@ -0,0 +1,22 @@ + + + + + + + netstandard2.0 + Volo.Abp.BlobStoring.Azure + Volo.Abp.BlobStoring.Azure + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureModule.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureModule.cs new file mode 100644 index 0000000000..0b386fbec1 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureModule.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Azure +{ + [DependsOn(typeof(AbpBlobStoringModule))] + public class AbpBlobStoringAzureModule : AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..945c930175 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs @@ -0,0 +1,24 @@ +using System; + +namespace Volo.Abp.BlobStoring.Azure +{ + public static class AzureBlobContainerConfigurationExtensions + { + public static AzureBlobProviderConfiguration GetAzureConfiguration( + this BlobContainerConfiguration containerConfiguration) + { + return new AzureBlobProviderConfiguration(containerConfiguration); + } + + public static BlobContainerConfiguration UseAzure( + this BlobContainerConfiguration containerConfiguration, + Action azureConfigureAction) + { + containerConfiguration.ProviderType = typeof(AzureBlobProvider); + + azureConfigureAction(new AzureBlobProviderConfiguration(containerConfiguration)); + + return containerConfiguration; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs new file mode 100644 index 0000000000..1afc3b6a53 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs @@ -0,0 +1,110 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Azure.Storage.Blobs; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class AzureBlobProvider : BlobProviderBase, ITransientDependency + { + protected IAzureBlobNameCalculator AzureBlobNameCalculator { get; } + + public AzureBlobProvider(IAzureBlobNameCalculator azureBlobNameCalculator) + { + AzureBlobNameCalculator = azureBlobNameCalculator; + } + + public override async Task SaveAsync(BlobProviderSaveArgs args) + { + var blobName = AzureBlobNameCalculator.Calculate(args); + var configuration = args.Configuration.GetAzureConfiguration(); + + if (!args.OverrideExisting && await BlobExistsAsync(args, blobName)) + { + throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{GetContainerName(args)}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); + } + + if (configuration.CreateContainerIfNotExists) + { + await CreateContainerIfNotExists(args); + } + + await GetBlobClient(args, blobName).UploadAsync(args.BlobStream, true); + } + + public override async Task DeleteAsync(BlobProviderDeleteArgs args) + { + var blobName = AzureBlobNameCalculator.Calculate(args); + + if (await BlobExistsAsync(args, blobName)) + { + return await GetBlobClient(args, blobName).DeleteIfExistsAsync(); + } + + return false; + } + + public override async Task ExistsAsync(BlobProviderExistsArgs args) + { + var blobName = AzureBlobNameCalculator.Calculate(args); + + return await BlobExistsAsync(args, blobName); + } + + public override async Task GetOrNullAsync(BlobProviderGetArgs args) + { + var blobName = AzureBlobNameCalculator.Calculate(args); + + if (!await BlobExistsAsync(args, blobName)) + { + return null; + } + + var blobClient = GetBlobClient(args, blobName); + var download = await blobClient.DownloadAsync(); + var memoryStream = new MemoryStream(); + await download.Value.Content.CopyToAsync(memoryStream); + return memoryStream; + } + + protected virtual BlobClient GetBlobClient(BlobProviderArgs args, string blobName) + { + var blobContainerClient = GetBlobContainerClient(args); + return blobContainerClient.GetBlobClient(blobName); + } + + protected virtual BlobContainerClient GetBlobContainerClient(BlobProviderArgs args) + { + var configuration = args.Configuration.GetAzureConfiguration(); + var blobServiceClient = new BlobServiceClient(configuration.ConnectionString); + return blobServiceClient.GetBlobContainerClient(GetContainerName(args)); + } + + protected virtual async Task CreateContainerIfNotExists(BlobProviderArgs args) + { + var blobContainerClient = GetBlobContainerClient(args); + await blobContainerClient.CreateIfNotExistsAsync(); + } + + private async Task BlobExistsAsync(BlobProviderArgs args, string blobName) + { + // Make sure Blob Container exists. + return await ContainerExistsAsync(GetBlobContainerClient(args)) && + (await GetBlobClient(args, blobName).ExistsAsync()).Value; + } + + private static string GetContainerName(BlobProviderArgs args) + { + var configuration = args.Configuration.GetAzureConfiguration(); + return configuration.ContainerName.IsNullOrWhiteSpace() + ? args.ContainerName + : configuration.ContainerName; + } + + private static async Task ContainerExistsAsync(BlobContainerClient blobContainerClient) + { + return (await blobContainerClient.ExistsAsync()).Value; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfiguration.cs new file mode 100644 index 0000000000..7ab338794b --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfiguration.cs @@ -0,0 +1,39 @@ +namespace Volo.Abp.BlobStoring.Azure +{ + public class AzureBlobProviderConfiguration + { + public string ConnectionString + { + get => _containerConfiguration.GetConfiguration(AzureBlobProviderConfigurationNames.ConnectionString); + set => _containerConfiguration.SetConfiguration(AzureBlobProviderConfigurationNames.ConnectionString, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + /// + /// This name may only contain lowercase letters, numbers, and hyphens, and must begin with a letter or a number. + /// Each hyphen must be preceded and followed by a non-hyphen character. + /// The name must also be between 3 and 63 characters long. + /// If this parameter is not specified, the ContainerName of the will be used. + /// + public string ContainerName + { + get => _containerConfiguration.GetConfiguration(AzureBlobProviderConfigurationNames.ContainerName); + set => _containerConfiguration.SetConfiguration(AzureBlobProviderConfigurationNames.ContainerName, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + /// + /// Default value: false. + /// + public bool CreateContainerIfNotExists + { + get => _containerConfiguration.GetConfigurationOrDefault(AzureBlobProviderConfigurationNames.CreateContainerIfNotExists, false); + set => _containerConfiguration.SetConfiguration(AzureBlobProviderConfigurationNames.CreateContainerIfNotExists, value); + } + + private readonly BlobContainerConfiguration _containerConfiguration; + + public AzureBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) + { + _containerConfiguration = containerConfiguration; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfigurationNames.cs new file mode 100644 index 0000000000..b8fbff19d2 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfigurationNames.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.BlobStoring.Azure +{ + public static class AzureBlobProviderConfigurationNames + { + public const string ConnectionString = "Azure.ConnectionString"; + public const string ContainerName = "Azure.ContainerName"; + public const string CreateContainerIfNotExists = "Azure.CreateContainerIfNotExists"; + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNameCalculator.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNameCalculator.cs new file mode 100644 index 0000000000..0c6cd481c9 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNameCalculator.cs @@ -0,0 +1,22 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class DefaultAzureBlobNameCalculator : IAzureBlobNameCalculator, ITransientDependency + { + protected ICurrentTenant CurrentTenant { get; } + + public DefaultAzureBlobNameCalculator(ICurrentTenant currentTenant) + { + CurrentTenant = currentTenant; + } + + public virtual string Calculate(BlobProviderArgs args) + { + return CurrentTenant.Id == null + ? $"host/{args.BlobName}" + : $"tenants/{CurrentTenant.Id.Value.ToString("D")}/{args.BlobName}"; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/IAzureBlobNameCalculator.cs b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/IAzureBlobNameCalculator.cs new file mode 100644 index 0000000000..5985a9278d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/IAzureBlobNameCalculator.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Azure +{ + public interface IAzureBlobNameCalculator + { + string Calculate(BlobProviderArgs args); + } +} diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs index fcc264977a..0170409a16 100644 --- a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs @@ -28,4 +28,4 @@ namespace Volo.Abp.BlobStoring CancellationToken = cancellationToken; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs index 7c42014fc4..6cf424f422 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Guids; +using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.MySQL { @@ -7,6 +8,15 @@ namespace Volo.Abp.EntityFrameworkCore.MySQL )] public class AbpEntityFrameworkCoreMySQLModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + if (options.DefaultSequentialGuidType == null) + { + options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString; + } + }); + } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs index ec28190702..7b5970bb59 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Guids; +using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.Oracle.Devart { @@ -7,6 +8,15 @@ namespace Volo.Abp.EntityFrameworkCore.Oracle.Devart )] public class AbpEntityFrameworkCoreOracleDevartModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + if (options.DefaultSequentialGuidType == null) + { + options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary; + } + }); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs index 4973af3966..4e76b89113 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Guids; +using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.PostgreSql { @@ -7,6 +8,15 @@ namespace Volo.Abp.EntityFrameworkCore.PostgreSql )] public class AbpEntityFrameworkCorePostgreSqlModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + if (options.DefaultSequentialGuidType == null) + { + options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString; + } + }); + } } } diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs index 762e8ce355..ae54df624f 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs @@ -1,4 +1,5 @@ -using Volo.Abp.Modularity; +using Volo.Abp.Guids; +using Volo.Abp.Modularity; namespace Volo.Abp.EntityFrameworkCore.SqlServer { @@ -7,6 +8,15 @@ namespace Volo.Abp.EntityFrameworkCore.SqlServer )] public class AbpEntityFrameworkCoreSqlServerModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + if (options.DefaultSequentialGuidType == null) + { + options.DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd; + } + }); + } } } diff --git a/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/AbpSequentialGuidGeneratorOptions.cs b/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/AbpSequentialGuidGeneratorOptions.cs index c712f8084b..83695b6a30 100644 --- a/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/AbpSequentialGuidGeneratorOptions.cs +++ b/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/AbpSequentialGuidGeneratorOptions.cs @@ -3,13 +3,21 @@ public class AbpSequentialGuidGeneratorOptions { /// - /// Default value: . + /// Default value: null (unspecified). + /// Use method + /// to get the value on use, since it fall backs to a default value. /// - public SequentialGuidType DefaultSequentialGuidType { get; set; } - - public AbpSequentialGuidGeneratorOptions() + public SequentialGuidType? DefaultSequentialGuidType { get; set; } + + /// + /// Get the value + /// or returns + /// if was null. + /// + public SequentialGuidType GetDefaultSequentialGuidType() { - DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd; + return DefaultSequentialGuidType ?? + SequentialGuidType.SequentialAtEnd; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/SequentialGuidGenerator.cs b/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/SequentialGuidGenerator.cs index 331ed066a4..a57e21f657 100644 --- a/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/SequentialGuidGenerator.cs +++ b/framework/src/Volo.Abp.Guids/Volo/Abp/Guids/SequentialGuidGenerator.cs @@ -24,7 +24,7 @@ namespace Volo.Abp.Guids public Guid Create() { - return Create(Options.DefaultSequentialGuidType); + return Create(Options.GetDefaultSequentialGuidType()); } public Guid Create(SequentialGuidType guidType) diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json index 2bd4bc7352..a4fdf7afbd 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json @@ -47,6 +47,8 @@ "PagerInfoEmpty": "Showing 0 to 0 of 0 entries", "PagerInfoFiltered": "(filtered from _MAX_ total entries)", "NoDataAvailableInDatatable": "No data available", + "Total": "total", + "Selected": "selected", "PagerShowMenuEntries": "Show _MENU_ entries", "DatatableActionDropdownDefaultText": "Actions", "ChangePassword": "Change password", @@ -59,6 +61,6 @@ "500Message": "Internal Server Error", "GoHomePage": "Go to the homepage", "GoBack": "Go back", - "Search" : "Search" + "Search": "Search" } } diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json index a7c511fe02..69ac5dfc56 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json @@ -13,11 +13,13 @@ "DefaultErrorMessage404": "No se encontró el recurso!", "DefaultErrorMessage404Detail": "El recurso solicitado no se encontró en el servidor!", "EntityNotFoundErrorMessage": "No hay una entidad {0} con el id = {1}!", + "Languages": "Lenguajes", "Error": "Error", "AreYouSure": "¿Está seguro?", "Cancel": "Cancelar", "Yes": "Si", "No": "No", + "Ok": "Aceptar", "Close": "Cerrar", "Save": "Guardar", "SavingWithThreeDot": "Guardando...", @@ -25,6 +27,8 @@ "Delete": "Eliminar", "Edit": "Editar", "Refresh": "Refrescar", + "Language": "Lenguaje", + "LoadMore": "Cargar más", "ProcessingWithThreeDot": "Procesando...", "LoadingWithThreeDot": "Cargando...", "Welcome": "Bienvenido", @@ -57,4 +61,4 @@ "GoBack": "Atras", "Search": "Buscar" } -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json index ddf8760fa9..bf558bb763 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json @@ -47,6 +47,8 @@ "PagerInfoEmpty": "0 kayıttan 0 ile 0 arası gösteriliyor.", "PagerInfoFiltered": "(_MAX_ kayıt arasından filtrelendi)", "NoDataAvailableInDatatable": "Tabloda kayıt mevcut değil.", + "Total": "toplam", + "Selected": "seçilen", "PagerShowMenuEntries": "Sayfada _MENU_ kayıt göster.", "DatatableActionDropdownDefaultText": "İşlemler", "ChangePassword": "Şifre değiştir", @@ -61,4 +63,4 @@ "GoBack": "Geri dön", "Search": "Arama" } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj new file mode 100644 index 0000000000..ff97ea97d3 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj @@ -0,0 +1,19 @@ + + + + + + netcoreapp3.1 + + 9f0d2c00-80c1-435b-bfab-2c39c8249091 + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestBase.cs b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestBase.cs new file mode 100644 index 0000000000..8941234cbf --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestBase.cs @@ -0,0 +1,20 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class AbpBlobStoringAzureTestCommonBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } + + public class AbpBlobStoringAzureTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestModule.cs new file mode 100644 index 0000000000..5e66be4e19 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestModule.cs @@ -0,0 +1,64 @@ +using System; +using Azure.Storage.Blobs; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Azure +{ + + /// + /// This module will not try to connect to azure. + /// + [DependsOn( + typeof(AbpBlobStoringAzureModule), + typeof(AbpBlobStoringTestModule) + )] + public class AbpBlobStoringAzureTestCommonModule : AbpModule + { + + } + + [DependsOn( + typeof(AbpBlobStoringAzureTestCommonModule) + )] + public class AbpBlobStoringAzureTestModule : AbpModule + { + private const string UserSecretsId = "9f0d2c00-80c1-435b-bfab-2c39c8249091"; + + private string _connectionString; + + private readonly string _randomContainerName = "abp-azure-test-container-" + Guid.NewGuid().ToString("N"); + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(builderAction: builder => + { + builder.AddUserSecrets(UserSecretsId); + })); + + var configuration = context.Services.GetConfiguration(); + _connectionString = configuration["Azure:ConnectionString"]; + + Configure(options => + { + options.Containers.ConfigureAll((containerName, containerConfiguration) => + { + containerConfiguration.UseAzure(azure => + { + azure.ConnectionString = _connectionString; + azure.ContainerName = _randomContainerName; + azure.CreateContainerIfNotExists = true; + }); + }); + }); + } + + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + var blobServiceClient = new BlobServiceClient(_connectionString); + blobServiceClient.GetBlobContainerClient(_randomContainerName).DeleteIfExists(); + } + } + +} diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobContainer_Tests.cs new file mode 100644 index 0000000000..751918be85 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobContainer_Tests.cs @@ -0,0 +1,16 @@ +using Xunit; + +namespace Volo.Abp.BlobStoring.Azure +{ + /* + //Please set the correct connection string in secrets.json and continue the test. + + public class AzureBlobContainer_Tests : BlobContainer_Tests + { + public AzureBlobContainer_Tests() + { + + } + } + */ +} diff --git a/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobNameCalculator_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobNameCalculator_Tests.cs new file mode 100644 index 0000000000..4540d208ae --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobNameCalculator_Tests.cs @@ -0,0 +1,57 @@ +using System; +using Shouldly; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.BlobStoring.Azure +{ + public class AzureBlobNameCalculator_Tests : AbpBlobStoringAzureTestCommonBase + { + private readonly IAzureBlobNameCalculator _calculator; + private readonly ICurrentTenant _currentTenant; + + private const string AzureContainerName = "/"; + private const string AzureSeparator = "/"; + + public AzureBlobNameCalculator_Tests() + { + _calculator = GetRequiredService(); + _currentTenant = GetRequiredService(); + } + + [Fact] + public void Default_Settings() + { + _calculator.Calculate( + GetArgs("my-container", "my-blob") + ).ShouldBe($"host{AzureSeparator}my-blob"); + } + + [Fact] + public void Default_Settings_With_TenantId() + { + var tenantId = Guid.NewGuid(); + + using (_currentTenant.Change(tenantId)) + { + _calculator.Calculate( + GetArgs("my-container", "my-blob") + ).ShouldBe($"tenants{AzureSeparator}{tenantId:D}{AzureSeparator}my-blob"); + } + } + + private static BlobProviderArgs GetArgs( + string containerName, + string blobName) + { + return new BlobProviderGetArgs( + containerName, + new BlobContainerConfiguration().UseAzure(x => + { + x.ContainerName = containerName; + }), + blobName + ); + } + } +} diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs index e49f9ce63b..316659af67 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs @@ -10,6 +10,18 @@ namespace Volo.Abp.BlobStoring.Database )] public class BlobStoringDatabaseDomainModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Containers.ConfigureDefault(container => + { + if (container.ProviderType == null) + { + container.UseDatabase(); + } + }); + }); + } } } diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml index 43c7ca2ce8..db9644a9ed 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml @@ -92,7 +92,7 @@ }
- @for (var index = 0; index < Model.Posts.Count && index < 3; index++) + @for (var index = 1; index < Model.Posts.Count && index < 4; index++) { var post = Model.Posts[index];
@@ -140,16 +140,16 @@
- @if (Model.Posts.Count > 3) + @if (Model.Posts.Count > 4) {

@L["LastPosts"]

- @for (var index = 3; index < Model.Posts.Count; index++) + @for (var index = 4; index < Model.Posts.Count; index++) { var post = Model.Posts[index]; - var oddPost = index % 2 == 1; + var oddPost = index % 2 == 0;
diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs index cef7983235..d904ef124a 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs @@ -26,7 +26,7 @@ namespace Volo.Abp.FeatureManagement return providerKey; } - return PrincipalAccessor.Principal?.FindEditionId()?.ToString(); + return PrincipalAccessor.Principal?.FindEditionId()?.ToString("N"); } } } \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs index 8f3574d4a2..b35225edb2 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs @@ -44,8 +44,8 @@ namespace Volo.Abp.FeatureManagement public virtual async Task DeleteAsync(string name, string providerName, string providerKey) { - var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey); - if (featureValue != null) + var featureValues = await FeatureValueRepository.FindAllAsync(name, providerName, providerKey); + foreach (var featureValue in featureValues) { await FeatureValueRepository.DeleteAsync(featureValue); } @@ -78,4 +78,4 @@ namespace Volo.Abp.FeatureManagement return FeatureValueCacheItem.CalculateCacheKey(name, providerName, providerKey); } } -} \ No newline at end of file +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs index 06ca345bf6..04b7ab6682 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs @@ -183,4 +183,4 @@ namespace Volo.Abp.FeatureManagement return value; } } -} \ No newline at end of file +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs index c630d045a1..f5b5c85da4 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs @@ -9,6 +9,8 @@ namespace Volo.Abp.FeatureManagement { Task FindAsync(string name, string providerName, string providerKey); + Task> FindAllAsync(string name, string providerName, string providerKey); + Task> GetListAsync(string providerName, string providerKey); } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs index 34a6c1bbb3..22e7b2775d 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs @@ -23,6 +23,14 @@ namespace Volo.Abp.FeatureManagement.EntityFrameworkCore ); } + public async Task> FindAllAsync(string name, string providerName, string providerKey) + { + return await DbSet + .Where( + s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey + ).ToListAsync(); + } + public virtual async Task> GetListAsync(string providerName, string providerKey) { return await DbSet @@ -31,4 +39,4 @@ namespace Volo.Abp.FeatureManagement.EntityFrameworkCore ).ToListAsync(); } } -} \ No newline at end of file +} diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs index 7db01d5288..72e5ac4b3b 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs @@ -22,6 +22,12 @@ namespace Volo.Abp.FeatureManagement.MongoDB .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey); } + public async Task> FindAllAsync(string name, string providerName, string providerKey) + { + return await GetMongoQueryable() + .Where(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey).ToListAsync(); + } + public virtual async Task> GetListAsync(string providerName, string providerKey) { return await GetMongoQueryable() @@ -29,4 +35,4 @@ namespace Volo.Abp.FeatureManagement.MongoDB .ToListAsync(); } } -} \ No newline at end of file +} diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs index c7cbb0d8ae..db606b2582 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs @@ -40,6 +40,18 @@ namespace Volo.Abp.FeatureManagement featureValue.ShouldBeNull(); } + [Fact] + public async Task FindAAllsync() + { + var featureValues = await Repository.FindAllAsync( + TestFeatureDefinitionProvider.ProjectCount, + EditionFeatureValueProvider.ProviderName, + TestEditionIds.Enterprise.ToString() + ); + + featureValues.Count.ShouldBe(1); + } + [Fact] public async Task GetListAsync() { diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs index bf698ca54f..6650f62c82 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs @@ -67,6 +67,7 @@ namespace Volo.Abp.Identity Task GetMembersCountAsync( OrganizationUnit organizationUnit, + string filter = null, CancellationToken cancellationToken = default ); diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs index 06b2084707..7bdb7d6837 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs @@ -121,33 +121,19 @@ namespace Volo.Abp.Identity.EntityFrameworkCore CancellationToken cancellationToken = default ) { - var query = from userOu in DbContext.Set() - join user in DbContext.Users.IncludeDetails(includeDetails) on userOu.UserId equals user.Id - where userOu.OrganizationUnitId == organizationUnit.Id - select user; + var query = CreateGetMembersFilteredQuery(organizationUnit, filter); - if (!filter.IsNullOrWhiteSpace()) - { - query = query.Where(u => - u.UserName.Contains(filter) || - u.Email.Contains(filter) || - (u.PhoneNumber != null && u.PhoneNumber.Contains(filter)) - ); - } - - return await query.OrderBy(sorting ?? nameof(IdentityUser.UserName)) + return await query.IncludeDetails(includeDetails).OrderBy(sorting ?? nameof(IdentityUser.UserName)) .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } public virtual async Task GetMembersCountAsync( OrganizationUnit organizationUnit, + string filter = null, CancellationToken cancellationToken = default) { - var query = from userOu in DbContext.Set() - join user in DbContext.Users on userOu.UserId equals user.Id - where userOu.OrganizationUnitId == organizationUnit.Id - select user; + var query = CreateGetMembersFilteredQuery(organizationUnit, filter); return await query.CountAsync(GetCancellationToken(cancellationToken)); } @@ -175,5 +161,24 @@ namespace Volo.Abp.Identity.EntityFrameworkCore DbContext.Set().RemoveRange(ouMembersQuery); } + + protected virtual IQueryable CreateGetMembersFilteredQuery(OrganizationUnit organizationUnit, string filter = null) + { + var query = from userOu in DbContext.Set() + join user in DbContext.Users on userOu.UserId equals user.Id + where userOu.OrganizationUnitId == organizationUnit.Id + select user; + + if (!filter.IsNullOrWhiteSpace()) + { + query = query.Where(u => + u.UserName.Contains(filter) || + u.Email.Contains(filter) || + (u.PhoneNumber != null && u.PhoneNumber.Contains(filter)) + ); + } + + return query; + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs index 943d570153..8c163d4126 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs @@ -116,14 +116,9 @@ namespace Volo.Abp.Identity.MongoDB bool includeDetails = false, CancellationToken cancellationToken = default) { - return await DbContext.Users.AsQueryable() - .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnit.Id)) - .WhereIf>( - !filter.IsNullOrWhiteSpace(), - u => - u.UserName.Contains(filter) || - u.Email.Contains(filter) - ) + var query = CreateGetMembersFilteredQuery(organizationUnit, filter); + + return await query .OrderBy(sorting ?? nameof(IdentityUser.UserName)) .As>() .PageBy>(skipCount, maxResultCount) @@ -132,12 +127,12 @@ namespace Volo.Abp.Identity.MongoDB public virtual async Task GetMembersCountAsync( OrganizationUnit organizationUnit, + string filter = null, CancellationToken cancellationToken = default) { - return await DbContext.Users.AsQueryable() - .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnit.Id)) - .As>() - .CountAsync(GetCancellationToken(cancellationToken)); + var query = CreateGetMembersFilteredQuery(organizationUnit, filter); + + return await query.CountAsync(GetCancellationToken(cancellationToken)); } public virtual Task RemoveAllRolesAsync(OrganizationUnit organizationUnit, CancellationToken cancellationToken = default) @@ -159,5 +154,18 @@ namespace Volo.Abp.Identity.MongoDB DbContext.Users.ReplaceOne(u => u.Id == user.Id, user); } } + + protected virtual IMongoQueryable CreateGetMembersFilteredQuery(OrganizationUnit organizationUnit, string filter = null) + { + return DbContext.Users.AsQueryable() + .Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnit.Id)) + .WhereIf>( + !filter.IsNullOrWhiteSpace(), + u => + u.UserName.Contains(filter) || + u.Email.Contains(filter) || + (u.PhoneNumber != null && u.PhoneNumber.Contains(filter)) + ); + } } } diff --git a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs index 5ff0284194..0e6717f29c 100644 --- a/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs @@ -143,6 +143,19 @@ namespace Volo.Abp.Identity usersCount.ShouldBeGreaterThan(1); } + [Fact] + public async Task GetMembersCountOfOrganizationUnitWithParamsAsync() + { + OrganizationUnit ou = await _organizationUnitRepository.GetAsync("OU111", true); + var usersCount = await _organizationUnitRepository.GetMembersCountAsync(ou,"n"); + + usersCount.ShouldBeGreaterThan(1); + usersCount.ShouldBeLessThanOrEqualTo(5); + + usersCount = await _organizationUnitRepository.GetMembersCountAsync(ou,"undefined-username"); + usersCount.ShouldBe(0); + } + [Fact] public async Task GetRolesCountOfOrganizationUnit() { diff --git a/npm/ng-packs/angular.json b/npm/ng-packs/angular.json index 57fc306525..87948c6f14 100644 --- a/npm/ng-packs/angular.json +++ b/npm/ng-packs/angular.json @@ -493,7 +493,7 @@ "index": "apps/dev-app/src/index.html", "main": "apps/dev-app/src/main.ts", "polyfills": "apps/dev-app/src/polyfills.ts", - "tsConfig": "apps/dev-app/tsconfig.prod.json", + "tsConfig": "apps/dev-app/tsconfig.dev.json", "aot": true, "extractCss": true, "assets": ["apps/dev-app/src/favicon.ico", "apps/dev-app/src/assets"], @@ -518,11 +518,27 @@ "inject": true, "bundleName": "fontawesome-v4-shims.min" }, + { + "input": "node_modules/@swimlane/ngx-datatable/index.css", + "inject": true, + "bundleName": "ngx-datatable-index" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/assets/icons.css", + "inject": true, + "bundleName": "ngx-datatable-icons" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/themes/material.css", + "inject": true, + "bundleName": "ngx-datatable-material" + }, "apps/dev-app/src/styles.scss" ], "scripts": [] }, "configurations": { + "tsConfig": "apps/dev-app/tsconfig.prod.json", "production": { "fileReplacements": [ { diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 4049f471fb..e42cf9ecc2 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -14,13 +14,13 @@ "commit": "git-cz", "lint": "ng lint", "scripts:build": "cd scripts && yarn && yarn build", - "prepare:workspace": "yarn scripts:build", + "prepare:workspace": "yarn scripts:build --noInstall", "ci": "yarn ng lint && yarn prepare:workspace && yarn ci:test && yarn ci:build", "ci:test": "ng test --coverage=false", "ci:build": "cd scripts && yarn build:prod", "lerna": "lerna", - "postinstall": "cd scripts && npm install && npm run post-install", - "generate:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" + "compile:ivy": "ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points --tsconfig './tsconfig.prod.json'", + "postinstall": "npm run compile:ivy" }, "devDependencies": { "@abp/ng.account": "~2.9.0", @@ -61,6 +61,7 @@ "@ngxs/router-plugin": "^3.6.2", "@ngxs/storage-plugin": "^3.6.2", "@ngxs/store": "^3.6.2", + "@swimlane/ngx-datatable": "^16.0.0", "@types/jest": "^25.2.3", "@types/node": "^12.11.1", "angular-oauth2-oidc": "^8.0.4", diff --git a/npm/ng-packs/packages/core/src/lib/services/list.service.ts b/npm/ng-packs/packages/core/src/lib/services/list.service.ts index 944368ad88..3aff601f7a 100644 --- a/npm/ng-packs/packages/core/src/lib/services/list.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/list.service.ts @@ -7,7 +7,7 @@ import { LIST_QUERY_DEBOUNCE_TIME } from '../tokens/list.token'; import { takeUntilDestroy } from '../utils/rxjs-utils'; @Injectable() -export class ListService implements OnDestroy { +export class ListService implements OnDestroy { private _filter = ''; set filter(value: string) { this._filter = value; @@ -26,8 +26,10 @@ export class ListService implements OnDestroy { return this._maxResultCount; } - private _page = 1; + private _page = 0; set page(value: number) { + if (value === this._page) return; + this._page = value; this.get(); } @@ -53,9 +55,9 @@ export class ListService implements OnDestroy { return this._sortOrder; } - private _query$ = new ReplaySubject(1); + private _query$ = new ReplaySubject(1); - get query$(): Observable { + get query$(): Observable { return this._query$ .asObservable() .pipe(debounceTime(this.delay || 300), shareReplay({ bufferSize: 1, refCount: true })); @@ -68,12 +70,12 @@ export class ListService implements OnDestroy { } get = () => { - this._query$.next({ + this._query$.next(({ filter: this._filter || undefined, maxResultCount: this._maxResultCount, - skipCount: (this._page - 1) * this._maxResultCount, + skipCount: this._page * this._maxResultCount, sorting: this._sortOrder ? `${this._sortKey} ${this._sortOrder}` : undefined, - }); + } as any) as QueryParamsType); }; constructor(@Optional() @Inject(LIST_QUERY_DEBOUNCE_TIME) private delay: number) { @@ -81,7 +83,7 @@ export class ListService implements OnDestroy { } hookToQuery( - streamCreatorCallback: QueryStreamCreatorCallback, + streamCreatorCallback: QueryStreamCreatorCallback, ): Observable> { this._isLoading$.next(true); @@ -96,6 +98,6 @@ export class ListService implements OnDestroy { ngOnDestroy() {} } -export type QueryStreamCreatorCallback = ( - query: ABP.PageQueryParams, +export type QueryStreamCreatorCallback = ( + query: QueryParamsType, ) => Observable>; diff --git a/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts index c2118d7f3a..f26c39d4ad 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts @@ -49,8 +49,8 @@ describe('ListService', () => { }); describe('#page', () => { - it('should initially be 1', () => { - expect(service.page).toBe(1); + it('should initially be 0', () => { + expect(service.page).toBe(0); }); it('should be changed', () => { @@ -110,7 +110,7 @@ describe('ListService', () => { filter: 'foo', sorting: 'bar baz', maxResultCount: 20, - skipCount: 160, + skipCount: 180, }); done(); diff --git a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html index 9962d8dbb4..802e272dce 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html +++ b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html @@ -20,89 +20,54 @@
- - - - - - - - - {{ 'AbpIdentity::Actions' | abpLocalization }} - - {{ 'AbpIdentity::RoleName' | abpLocalization }} - - - - - - - -
+ + + +
+ +
+ + -
- - - -
- - - {{ data.name - }}{{ - 'AbpIdentity::DisplayName:IsDefault' | abpLocalization - }} - {{ - 'AbpIdentity::DisplayName:IsPublic' | abpLocalization - }} - - - - +
+
+
+ +
diff --git a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts index 5e0f64e811..73a9a64dfb 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts @@ -1,5 +1,6 @@ -import { ABP } from '@abp/ng.core'; -import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; +import { ListService } from '@abp/ng.core'; +import { ePermissionManagementComponents } from '@abp/ng.permission-management'; +import { Confirmation, ConfirmationService } from '@abp/ng.theme.shared'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { Select, Store } from '@ngxs/store'; @@ -14,11 +15,11 @@ import { } from '../../actions/identity.actions'; import { Identity } from '../../models/identity'; import { IdentityState } from '../../states/identity.state'; -import { ePermissionManagementComponents } from '@abp/ng.permission-management'; @Component({ selector: 'abp-roles', templateUrl: './roles.component.html', + providers: [ListService], }) export class RolesComponent implements OnInit { @Select(IdentityState.getRoles) @@ -37,16 +38,8 @@ export class RolesComponent implements OnInit { providerKey: string; - pageQuery: ABP.PageQueryParams = { maxResultCount: 10 }; - - loading = false; - modalBusy = false; - sortOrder = ''; - - sortKey = ''; - permissionManagementKey = ePermissionManagementComponents.PermissionManagement; @ViewChild('formRef', { static: false, read: ElementRef }) @@ -57,13 +50,14 @@ export class RolesComponent implements OnInit { }; constructor( + public readonly list: ListService, private confirmationService: ConfirmationService, private fb: FormBuilder, private store: Store, ) {} ngOnInit() { - this.get(); + this.hookToQuery(); } buildForm() { @@ -110,7 +104,7 @@ export class RolesComponent implements OnInit { .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.isModalVisible = false; - this.get(); + this.list.get(); }); } @@ -121,23 +115,13 @@ export class RolesComponent implements OnInit { }) .subscribe((status: Confirmation.Status) => { if (status === Confirmation.Status.confirm) { - this.store.dispatch(new DeleteRole(id)).subscribe(() => this.get()); + this.store.dispatch(new DeleteRole(id)).subscribe(() => this.list.get()); } }); } - onPageChange(page: number) { - this.pageQuery.skipCount = (page - 1) * this.pageQuery.maxResultCount; - - this.get(); - } - - get() { - this.loading = true; - this.store - .dispatch(new GetRoles(this.pageQuery)) - .pipe(finalize(() => (this.loading = false))) - .subscribe(); + private hookToQuery() { + this.list.hookToQuery(query => this.store.dispatch(new GetRoles(query))).subscribe(); } onClickSaveButton() { @@ -152,4 +136,10 @@ export class RolesComponent implements OnInit { this.visiblePermissions = true; }, 0); } + + sort(data) { + const { prop, dir } = data.sorts[0]; + this.list.sortKey = prop; + this.list.sortOrder = dir; + } } diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html index e00dbfe866..648b1024d0 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.html @@ -20,108 +20,74 @@
- + [(ngModel)]="list.filter" + /> +
- - - - - - - - - {{ 'AbpIdentity::Actions' | abpLocalization }} - - {{ 'AbpIdentity::UserName' | abpLocalization }} - - - - - {{ 'AbpIdentity::EmailAddress' | abpLocalization }} - - - - {{ 'AbpIdentity::PhoneNumber' | abpLocalization }} - + + +
+ +
+ + -
- - - -
- - {{ data.userName }} - {{ data.email }} - {{ data.phoneNumber }} - - - +
+
+
+ + + +
diff --git a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts index 8b35e9ab77..aeeb250126 100644 --- a/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts +++ b/npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts @@ -1,13 +1,27 @@ -import { ABP } from '@abp/ng.core'; +import { ListService } from '@abp/ng.core'; import { ePermissionManagementComponents } from '@abp/ng.permission-management'; import { Confirmation, ConfirmationService, getPasswordValidators } from '@abp/ng.theme.shared'; import { Component, OnInit, TemplateRef, TrackByFunction, ViewChild } from '@angular/core'; -import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { + AbstractControl, + FormArray, + FormBuilder, + FormControl, + FormGroup, + Validators, +} from '@angular/forms'; import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; import { finalize, pluck, switchMap, take } from 'rxjs/operators'; import snq from 'snq'; -import { CreateUser, DeleteUser, GetUserById, GetUserRoles, GetUsers, UpdateUser } from '../../actions/identity.actions'; +import { + CreateUser, + DeleteUser, + GetUserById, + GetUserRoles, + GetUsers, + UpdateUser, +} from '../../actions/identity.actions'; import { Identity } from '../../models/identity'; import { IdentityService } from '../../services/identity.service'; import { IdentityState } from '../../states/identity.state'; @@ -15,6 +29,7 @@ import { IdentityState } from '../../states/identity.state'; @Component({ selector: 'abp-users', templateUrl: './users.component.html', + providers: [ListService], }) export class UsersComponent implements OnInit { @Select(IdentityState.getUsers) @@ -23,7 +38,7 @@ export class UsersComponent implements OnInit { @Select(IdentityState.getUsersTotalCount) totalCount$: Observable; - @ViewChild('modalContent', {static: false}) + @ViewChild('modalContent', { static: false }) modalContent: TemplateRef; form: FormGroup; @@ -38,18 +53,10 @@ export class UsersComponent implements OnInit { providerKey: string; - pageQuery: ABP.PageQueryParams = { maxResultCount: 10 }; - isModalVisible: boolean; - loading = false; - modalBusy = false; - sortOrder = ''; - - sortKey = ''; - permissionManagementKey = ePermissionManagementComponents.PermissionManagement; trackByFn: TrackByFunction = (index, item) => Object.keys(item)[0] || index; @@ -63,6 +70,7 @@ export class UsersComponent implements OnInit { } constructor( + public readonly list: ListService, private confirmationService: ConfirmationService, private fb: FormBuilder, private store: Store, @@ -70,12 +78,7 @@ export class UsersComponent implements OnInit { ) {} ngOnInit() { - this.get(); - } - - onSearch(value: string) { - this.pageQuery.filter = value; - this.get(); + this.hookToQuery(); } buildForm() { @@ -170,7 +173,7 @@ export class UsersComponent implements OnInit { .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.isModalVisible = false; - this.get(); + this.list.get(); }); } @@ -181,23 +184,19 @@ export class UsersComponent implements OnInit { }) .subscribe((status: Confirmation.Status) => { if (status === Confirmation.Status.confirm) { - this.store.dispatch(new DeleteUser(id)).subscribe(() => this.get()); + this.store.dispatch(new DeleteUser(id)).subscribe(() => this.list.get()); } }); } - onPageChange(page: number) { - this.pageQuery.skipCount = (page - 1) * this.pageQuery.maxResultCount; - - this.get(); + sort(data) { + const { prop, dir } = data.sorts[0]; + this.list.sortKey = prop; + this.list.sortOrder = dir; } - get() { - this.loading = true; - this.store - .dispatch(new GetUsers(this.pageQuery)) - .pipe(finalize(() => (this.loading = false))) - .subscribe(); + private hookToQuery() { + this.list.hookToQuery(query => this.store.dispatch(new GetUsers(query))).subscribe(); } openPermissionsModal(providerKey: string) { diff --git a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html index 10761a3618..c966b2c4af 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html +++ b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html @@ -18,99 +18,73 @@
+
- + [(ngModel)]="list.filter" + /> +
- - - - - - - - - {{ 'AbpTenantManagement::Actions' | abpLocalization }} - - {{ 'AbpTenantManagement::TenantName' | abpLocalization }} - + + +
+ +
+ + + -
- - - - -
- - {{ data.name }} - - - +
+
+
+ +
@@ -128,7 +102,7 @@ {{ 'AbpTenantManagement::Cancel' | abpLocalization }} {{ - 'AbpIdentity::Save' | abpLocalization + 'AbpTenantManagement::Save' | abpLocalization }} diff --git a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts index 2e1e5a1b5b..ec50b97c6b 100644 --- a/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts +++ b/npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts @@ -1,4 +1,4 @@ -import { ABP } from '@abp/ng.core'; +import { ABP, ListService } from '@abp/ng.core'; import { eFeatureManagementComponents } from '@abp/ng.feature-management'; import { Confirmation, ConfirmationService, getPasswordValidators } from '@abp/ng.theme.shared'; import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; @@ -6,7 +6,13 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Select, Store } from '@ngxs/store'; import { Observable } from 'rxjs'; import { finalize, pluck, switchMap, take } from 'rxjs/operators'; -import { CreateTenant, DeleteTenant, GetTenantById, GetTenants, UpdateTenant } from '../../actions/tenant-management.actions'; +import { + CreateTenant, + DeleteTenant, + GetTenantById, + GetTenants, + UpdateTenant, +} from '../../actions/tenant-management.actions'; import { TenantManagementService } from '../../services/tenant-management.service'; import { TenantManagementState } from '../../states/tenant-management.state'; @@ -19,6 +25,7 @@ interface SelectedModalContent { @Component({ selector: 'abp-tenants', templateUrl: './tenants.component.html', + providers: [ListService], }) export class TenantsComponent implements OnInit { @Select(TenantManagementState.get) @@ -45,16 +52,8 @@ export class TenantsComponent implements OnInit { _useSharedDatabase: boolean; - pageQuery: ABP.PageQueryParams = { maxResultCount: 10 }; - - loading = false; - modalBusy = false; - sortOrder = ''; - - sortKey = ''; - featureManagementKey = eFeatureManagementComponents.FeatureManagement; get hasSelectedTenant(): boolean { @@ -100,6 +99,7 @@ export class TenantsComponent implements OnInit { }; constructor( + public readonly list: ListService, private confirmationService: ConfirmationService, private tenantService: TenantManagementService, private fb: FormBuilder, @@ -107,12 +107,7 @@ export class TenantsComponent implements OnInit { ) {} ngOnInit() { - this.get(); - } - - onSearch(value: string) { - this.pageQuery.filter = value; - this.get(); + this.hookToQuery(); } private createTenantForm() { @@ -236,7 +231,7 @@ export class TenantsComponent implements OnInit { .pipe(finalize(() => (this.modalBusy = false))) .subscribe(() => { this.isModalVisible = false; - this.get(); + this.list.get(); }); } @@ -251,23 +246,13 @@ export class TenantsComponent implements OnInit { ) .subscribe((status: Confirmation.Status) => { if (status === Confirmation.Status.confirm) { - this.store.dispatch(new DeleteTenant(id)).subscribe(() => this.get()); + this.store.dispatch(new DeleteTenant(id)).subscribe(() => this.list.get()); } }); } - onPageChange(page: number) { - this.pageQuery.skipCount = (page - 1) * this.pageQuery.maxResultCount; - - this.get(); - } - - get() { - this.loading = true; - this.store - .dispatch(new GetTenants(this.pageQuery)) - .pipe(finalize(() => (this.loading = false))) - .subscribe(); + hookToQuery() { + this.list.hookToQuery(query => this.store.dispatch(new GetTenants(query))).subscribe(); } onSharedDatabaseChange(value: boolean) { @@ -289,4 +274,10 @@ export class TenantsComponent implements OnInit { this.visibleFeatures = true; }, 0); } + + sort(data) { + const { prop, dir } = data.sorts[0]; + this.list.sortKey = prop; + this.list.sortOrder = dir; + } } diff --git a/npm/ng-packs/packages/theme-shared/ng-package.json b/npm/ng-packs/packages/theme-shared/ng-package.json index 58d74cba48..0c3b5e300f 100644 --- a/npm/ng-packs/packages/theme-shared/ng-package.json +++ b/npm/ng-packs/packages/theme-shared/ng-package.json @@ -10,6 +10,7 @@ "@fortawesome/fontawesome-free", "@ng-bootstrap/ng-bootstrap", "@ngx-validate/core", + "@swimlane/ngx-datatable", "bootstrap", "font-awesome", "chart.js" diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index b76f5df213..2eb08a7e60 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -11,6 +11,7 @@ "@fortawesome/fontawesome-free": "^5.13.0", "@ng-bootstrap/ng-bootstrap": "^6.1.0", "@ngx-validate/core": "^0.0.8", + "@swimlane/ngx-datatable": "^16.0.0", "bootstrap": "^4.5.0", "chart.js": "^2.9.3" }, diff --git a/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts b/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts index a691f96ead..f997eb6e5c 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts @@ -45,6 +45,11 @@ export default ` background: #8a8686; } +.bordered .datatable-body-row { + border-top: 1px solid #eee; + margin-top: -1px; +} + .abp-ellipsis-inline { display: inline-block; overflow: hidden; @@ -136,6 +141,9 @@ export default ` .sorting_asc:after { opacity: .3; } +.ngx-datatable.material { + box-shadow: none; +} @keyframes fadeInTop { from { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts index aa2725e328..a6b666be3b 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts @@ -1,2 +1,4 @@ export * from './loading.directive'; +export * from './ngx-datatable-default.directive'; +export * from './ngx-datatable-list.directive'; export * from './table-sort.directive'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts new file mode 100644 index 0000000000..3672bde3b3 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts @@ -0,0 +1,23 @@ +import { Directive, HostBinding, Input } from '@angular/core'; +import { ColumnMode, DatatableComponent } from '@swimlane/ngx-datatable'; + +@Directive({ + // tslint:disable-next-line + selector: 'ngx-datatable[default]', + exportAs: 'ngxDatatableDefault', +}) +export class NgxDatatableDefaultDirective { + @Input() class = 'material bordered'; + + @HostBinding('class') + get classes(): string { + return `ngx-datatable ${this.class}`; + } + + constructor(private table: DatatableComponent) { + this.table.columnMode = ColumnMode.force; + this.table.footerHeight = 50; + this.table.headerHeight = 50; + this.table.rowHeight = 'auto'; + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts new file mode 100644 index 0000000000..85e574d63d --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts @@ -0,0 +1,70 @@ +import { ListService } from '@abp/ng.core'; +import { + ChangeDetectorRef, + Directive, + Input, + OnChanges, + OnDestroy, + OnInit, + SimpleChanges, +} from '@angular/core'; +import { DatatableComponent } from '@swimlane/ngx-datatable'; +import { Subscription } from 'rxjs'; + +@Directive({ + // tslint:disable-next-line + selector: 'ngx-datatable[list]', + exportAs: 'ngxDatatableList', +}) +export class NgxDatatableListDirective implements OnChanges, OnDestroy, OnInit { + private subscription = new Subscription(); + + @Input() list: ListService; + + constructor(private table: DatatableComponent, private cdRef: ChangeDetectorRef) { + this.table.externalPaging = true; + this.table.externalSorting = true; + } + + private subscribeToPage() { + const sub = this.table.page.subscribe(({ offset }) => { + this.list.page = offset; + this.table.offset = offset; + }); + this.subscription.add(sub); + } + + private subscribeToSort() { + const sub = this.table.sort.subscribe(({ sorts: [{ prop, dir }] }) => { + this.list.sortKey = prop; + this.list.sortOrder = dir; + }); + this.subscription.add(sub); + } + + private subscribeToIsLoading() { + const sub = this.list.isLoading$.subscribe(loading => { + this.table.loadingIndicator = loading; + this.cdRef.markForCheck(); + }); + this.subscription.add(sub); + } + + ngOnChanges({ list }: SimpleChanges) { + if (!list.firstChange) return; + + const { maxResultCount, page } = list.currentValue; + this.table.limit = maxResultCount; + this.table.offset = page; + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } + + ngOnInit() { + this.subscribeToPage(); + this.subscribeToSort(); + this.subscribeToIsLoading(); + } +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 41d8e3706a..6afd044725 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -1,8 +1,10 @@ -import { CoreModule, LazyLoadService, noop } from '@abp/ng.core'; +import { ConfigState, CoreModule, noop } from '@abp/ng.core'; import { DatePipe } from '@angular/common'; import { APP_INITIALIZER, Injector, ModuleWithProviders, NgModule } from '@angular/core'; import { NgbDateParserFormatter, NgbPaginationModule } from '@ng-bootstrap/ng-bootstrap'; import { NgxValidateCoreModule } from '@ngx-validate/core'; +import { Store } from '@ngxs/store'; +import { INgxDatatableConfig, NgxDatatableModule } from '@swimlane/ngx-datatable'; import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component'; import { ButtonComponent } from './components/button/button.component'; import { ChartComponent } from './components/chart/chart.component'; @@ -18,8 +20,9 @@ import { TableEmptyMessageComponent } from './components/table-empty-message/tab import { TableComponent } from './components/table/table.component'; import { ToastContainerComponent } from './components/toast-container/toast-container.component'; import { ToastComponent } from './components/toast/toast.component'; -import styles from './constants/styles'; import { LoadingDirective } from './directives/loading.directive'; +import { NgxDatatableDefaultDirective } from './directives/ngx-datatable-default.directive'; +import { NgxDatatableListDirective } from './directives/ngx-datatable-list.directive'; import { TableSortDirective } from './directives/table-sort.directive'; import { ErrorHandler } from './handlers/error.handler'; import { initLazyStyleHandler } from './handlers/lazy-style.handler'; @@ -27,26 +30,25 @@ import { RootParams } from './models/common'; import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token'; import { httpErrorConfigFactory, HTTP_ERROR_CONFIG } from './tokens/http-error.token'; import { DateParserFormatter } from './utils/date-parser-formatter'; -import { chartJsLoaded$ } from './utils/widget-utils'; -/** - * - * @deprecated To be deleted in v2.6 - * - */ -export function appendScript(injector: Injector) { - const fn = () => { - import('chart.js').then(() => chartJsLoaded$.next(true)); +export function ngxDatatableMessageFactory(store: Store) { + const emptyMessage = store.selectSnapshot( + ConfigState.getLocalization('AbpUi::NoDataAvailableInDatatable'), + ); + const totalMessage = store.selectSnapshot(ConfigState.getLocalization('AbpUi::Total')); + const selectedMessage = store.selectSnapshot(ConfigState.getLocalization('AbpUi::Selected')); - const lazyLoadService: LazyLoadService = injector.get(LazyLoadService); - return lazyLoadService.load(null, 'style', styles, 'head', 'beforeend').toPromise(); - }; - - return fn; + return { + messages: { + emptyMessage, + totalMessage, + selectedMessage, + }, + } as INgxDatatableConfig; } @NgModule({ - imports: [CoreModule, NgxValidateCoreModule, NgbPaginationModule], + imports: [CoreModule, NgxDatatableModule, NgxValidateCoreModule, NgbPaginationModule], declarations: [ BreadcrumbComponent, ButtonComponent, @@ -63,11 +65,14 @@ export function appendScript(injector: Injector) { ToastComponent, ToastContainerComponent, SortOrderIconComponent, + NgxDatatableDefaultDirective, + NgxDatatableListDirective, LoadingDirective, TableSortDirective, ToastContainerComponent, ], exports: [ + NgxDatatableModule, BreadcrumbComponent, ButtonComponent, ChartComponent, @@ -81,6 +86,8 @@ export function appendScript(injector: Injector) { ToastComponent, ToastContainerComponent, SortOrderIconComponent, + NgxDatatableDefaultDirective, + NgxDatatableListDirective, LoadingDirective, TableSortDirective, ToastContainerComponent, @@ -120,6 +127,11 @@ export class ThemeSharedModule { deps: [HTTP_ERROR_CONFIG], }, { provide: NgbDateParserFormatter, useClass: DateParserFormatter }, + { + provide: 'configuration', + useFactory: ngxDatatableMessageFactory, + deps: [Store], + }, ], }; } diff --git a/npm/ng-packs/scripts/build.ts b/npm/ng-packs/scripts/build.ts index 5e72245bd6..a69f1c768a 100644 --- a/npm/ng-packs/scripts/build.ts +++ b/npm/ng-packs/scripts/build.ts @@ -3,12 +3,13 @@ import program from 'commander'; (async () => { program.option('-i, --noInstall', 'skip updating package.json and installation', false); + program.option('-c, --skipNgcc', 'skip ngcc', false); program.parse(process.argv); try { if (!program.noInstall) { - await execa('yarn', ['install-new-dependencies'], { stdout: 'inherit' }); + await execa('yarn', ['install'], { stdout: 'inherit', cwd: '../' }); } await execa( @@ -54,6 +55,8 @@ import program from 'commander'; ], { stdout: 'inherit', cwd: '../' }, ); + + if (!program.skipNgcc) await execa('yarn', ['compile:ivy'], { stdout: 'inherit', cwd: '../' }); } catch (error) { console.error(error.stderr); process.exit(1); diff --git a/npm/ng-packs/scripts/install-new-dependencies.ts b/npm/ng-packs/scripts/install-new-dependencies.ts deleted file mode 100644 index 50dd0133c0..0000000000 --- a/npm/ng-packs/scripts/install-new-dependencies.ts +++ /dev/null @@ -1,59 +0,0 @@ -import execa from 'execa'; -import fse from 'fs-extra'; - -const updateAndInstall = async () => { - const { projects } = await fse.readJSON('../angular.json'); - const projectNames = Object.keys(projects).filter(project => project !== 'dev-app'); - - const packageJson = await fse.readJSON('../package.json'); - - projectNames.forEach(project => { - // do not convert to async - const { dependencies = {}, peerDependencies = {}, name, version } = fse.readJSONSync( - `../packages/${project}/package.json`, - ); - - packageJson.devDependencies = { - ...packageJson.devDependencies, - ...dependencies, - ...peerDependencies, - [name]: `~${version}`, - }; - - packageJson.devDependencies = Object.keys(packageJson.devDependencies) - .sort() - .reduce((acc, key) => ({ ...acc, [key]: packageJson.devDependencies[key] }), {}); - }); - - // tslint:disable-next-line: no-console - console.log('Searching the packages on NPM to check if it is exist. It takes a while.'); - Object.keys(packageJson.devDependencies).forEach(pkg => { - const isPackageExistOnNPM = !( - execa.sync('npm', ['search', pkg]).stdout.indexOf('No matches found for') > -1 - ); - - if (!isPackageExistOnNPM) delete packageJson.devDependencies[pkg]; - }); - - await fse.writeJSON('../package.json', packageJson, { spaces: 2 }); - - try { - await execa('yarn', ['install', '--ignore-scripts'], { - stdout: 'inherit', - cwd: '../', - }); - - await execa('yarn', ['post-install'], { - stdout: 'inherit', - }); - } catch (error) { - console.error(error.stderr); - process.exit(1); - } - - process.exit(0); -}; - -updateAndInstall(); - -export default updateAndInstall; diff --git a/npm/ng-packs/scripts/package.json b/npm/ng-packs/scripts/package.json index 9f2e047b7f..932abe4f64 100644 --- a/npm/ng-packs/scripts/package.json +++ b/npm/ng-packs/scripts/package.json @@ -7,10 +7,7 @@ "build": "ts-node -r tsconfig-paths/register build.ts", "build:prod": "ts-node -r tsconfig-paths/register prod-build.ts", "publish-packages": "ts-node -r tsconfig-paths/register publish.ts", - "install-new-dependencies": "ts-node -r tsconfig-paths/register install-new-dependencies.ts", - "post-install": "ts-node -r tsconfig-paths/register post-install.ts", - "replace-with-tilde": "ts-node -r tsconfig-paths/register replace-with-tilde.ts", - "sync": "ts-node -r tsconfig-paths/register sync.ts" + "replace-with-tilde": "ts-node -r tsconfig-paths/register replace-with-tilde.ts" }, "author": "", "dependencies": { diff --git a/npm/ng-packs/scripts/post-install.ts b/npm/ng-packs/scripts/post-install.ts deleted file mode 100644 index 1d3e6a9f50..0000000000 --- a/npm/ng-packs/scripts/post-install.ts +++ /dev/null @@ -1,29 +0,0 @@ -import execa from 'execa'; -import fse from 'fs-extra'; - -(async () => { - rename(true); - - try { - await execa('yarn', ['ngcc'], { - stdout: 'inherit', - cwd: '../', - }); - } catch (error) { - rename(false); - console.error(error); - process.exit(1); - } - - rename(false); -})(); - -async function rename(prodToMain: boolean) { - try { - await fse.rename('../tsconfig.json', `../tsconfig.${prodToMain ? 'temp' : 'prod'}.json`); - await fse.rename(`../tsconfig.${prodToMain ? 'prod' : 'temp'}.json`, '../tsconfig.json'); - } catch (error) { - console.error(error); - process.exit(1); - } -} diff --git a/npm/ng-packs/scripts/prod-build.ts b/npm/ng-packs/scripts/prod-build.ts index 03a1432b99..33f2aa2c44 100644 --- a/npm/ng-packs/scripts/prod-build.ts +++ b/npm/ng-packs/scripts/prod-build.ts @@ -13,11 +13,19 @@ import fse from 'fs-extra'; }); await fse.remove('../../../templates/app/angular/node_modules/@abp'); - await fse.copy('../node_modules/@abp', '../../../templates/app/angular/node_modules/@abp', { overwrite: true, }); + // TODO: Will be removed in v3.1, it is added to fix the prod build error + await fse.copy( + '../node_modules/@swimlane', + '../../../templates/app/angular/node_modules/@swimlane', + { + overwrite: true, + }, + ); + await execa('yarn', ['ng', 'build', '--prod'], { stdout: 'inherit', cwd: '../../../templates/app/angular', diff --git a/npm/ng-packs/scripts/publish.ts b/npm/ng-packs/scripts/publish.ts index 21f750b27e..f32afce7f7 100644 --- a/npm/ng-packs/scripts/publish.ts +++ b/npm/ng-packs/scripts/publish.ts @@ -22,7 +22,7 @@ const publish = async () => { const registry = program.preview ? 'http://localhost:4873' : 'https://registry.npmjs.org'; try { - await execa('yarn', ['install-new-dependencies'], { stdout: 'inherit' }); + await execa('yarn', ['install'], { stdout: 'inherit', cwd: '../' }); await fse.rename('../lerna.version.json', '../lerna.json'); diff --git a/npm/ng-packs/scripts/push.ts b/npm/ng-packs/scripts/push.ts deleted file mode 100644 index 6c41b0b697..0000000000 --- a/npm/ng-packs/scripts/push.ts +++ /dev/null @@ -1,19 +0,0 @@ -const Confirm = require('prompt-confirm'); -const execa = require('execa'); - -(async () => { - const answer = await new Confirm('Would you like to push?').run().then(answer => answer); - - const remote = execa.sync('git', ['remote']).stdout; - const branch = execa.sync('git', ['rev-parse', '--abbrev-ref', 'HEAD']).stdout; - - if (answer) { - try { - await execa('git', ['push', remote, branch], { stdout: 'inherit' }); - await execa('git', ['fetch', remote], { stdout: 'inherit' }); - console.log('Successfully!'); - } catch (error) { - console.log('An error occured.' + error); - } - } -})(); diff --git a/npm/ng-packs/scripts/sync.ts b/npm/ng-packs/scripts/sync.ts deleted file mode 100644 index 60615a1027..0000000000 --- a/npm/ng-packs/scripts/sync.ts +++ /dev/null @@ -1,37 +0,0 @@ -import fse from 'fs-extra'; -import execa from 'execa'; - -(async () => { - const { projects } = await fse.readJSON('../angular.json'); - const projectNames = Object.keys(projects).filter(project => project !== 'dev-app'); - - for (let i = 0; i < projectNames.length; i++) { - const project = projectNames[i]; - const { dependencies: distDependencies, version } = await fse.readJson( - `../dist/${project}/package.json`, - ); - const srcPackagePath = `../packages/${project}/package.json`; - const srcPackage = await fse.readJson(srcPackagePath); - - if (distDependencies) { - for (const key in srcPackage.dependencies) { - if (distDependencies.hasOwnProperty(key)) { - srcPackage.dependencies[key] = distDependencies[key]; - } - } - } - - await fse.writeJson(srcPackagePath, { ...srcPackage, version }, { spaces: 2 }); - } - - try { - await execa('git', ['add', '../packages/*', '../package.json'], { stdout: 'inherit' }); - await execa('git', ['commit', '-m', 'Update source packages versions', '--no-verify'], { - stdout: 'inherit', - }); - } catch (error) { - console.error(error.stderr); - } - - process.exit(0); -})(); diff --git a/npm/ng-packs/scripts/yarn.lock b/npm/ng-packs/scripts/yarn.lock index f2a26a3bc5..bd91324394 100644 --- a/npm/ng-packs/scripts/yarn.lock +++ b/npm/ng-packs/scripts/yarn.lock @@ -15,14 +15,14 @@ integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/node@*": - version "14.0.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.11.tgz#61d4886e2424da73b7b25547f59fdcb534c165a3" - integrity sha512-lCvvI24L21ZVeIiyIUHZ5Oflv1hhHQ5E1S25IRlKIXaRkVgmXpJMI3wUJkmym2bTbCe+WoIibQnMVAU3FguaOg== + version "14.0.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.13.tgz#ee1128e881b874c371374c1f72201893616417c9" + integrity sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA== "@types/node@^13.1.2": - version "13.13.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.10.tgz#34a9be3cbc409fd235984bd18a130006f5234396" - integrity sha512-J+FbkhLTcFstD7E5mVZDjYxa1VppwT2HALE6H3n2AnBSP8uiCQk0Pyr6BkJcP38dFV9WecoVJRJmFnl9ikIW7Q== + version "13.13.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.12.tgz#9c72e865380a7dc99999ea0ef20fc9635b503d20" + integrity sha512-zWz/8NEPxoXNT9YyF2osqyA9WjssZukYpgI4UYZpOjcyqwIUqWGkcCionaEb9Ki+FULyPyvNFpg/329Kd2/pbw== ansi-bgblack@^0.1.1: version "0.1.1" diff --git a/npm/ng-packs/tslint.json b/npm/ng-packs/tslint.json index d75b6a274b..5a5d15bc35 100644 --- a/npm/ng-packs/tslint.json +++ b/npm/ng-packs/tslint.json @@ -37,7 +37,7 @@ "import-blacklist": [true, "rxjs/Rx"], "interface-over-type-literal": true, "interface-name": [true, "never-prefix"], - "member-access": [true, "no-public"], + "member-access": [false], "member-ordering": [ true, { diff --git a/npm/ng-packs/yarn.lock b/npm/ng-packs/yarn.lock index afa58bc631..bdfcea9f74 100644 --- a/npm/ng-packs/yarn.lock +++ b/npm/ng-packs/yarn.lock @@ -110,9 +110,9 @@ tslib "^1.9.0" "@abp/ng.theme.shared@~2.9.0": - version "2.9.0" - resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-2.9.0.tgz#aaf97cf94ff798b3695cbb1b0dec33d20996fbda" - integrity sha512-04xlt3aKiDVyE+6NpxA0KGHGuupJWhXTjPWwlzVtfNiPqYjx0rG2FbTGgNWMW4K1qTlRRVjPA5a/MDhZtZHZIw== + version "2.9.1" + resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-2.9.1.tgz#c44064057505a5a2cdb4ca269daa6bd423a99ba6" + integrity sha512-iSlhRcjmNXYclAkSJ0vN20GBF7ART3340Mpn4V+hMRachkJSewwMNYG73G6ukVwZrF+0Sjdn2r9jJrJHhhKm3w== dependencies: "@abp/ng.core" "~2.9.0" "@fortawesome/fontawesome-free" "^5.12.1" @@ -139,29 +139,29 @@ jest-preset-angular "^8.1.2" lodash "^4.17.10" -"@angular-devkit/architect@0.901.7", "@angular-devkit/architect@>=0.900.0 < 0.1000.0": - version "0.901.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.901.7.tgz#6a09cb076ca92b3202053fca757a456d1f8e4395" - integrity sha512-yW/PUEqle55QihOFbmeNXaVTodhfeXkteoFDUpz+YpX3xiQDXDtNbIJSzKOQTojtBKdSMKMvZkQLr+RAa7/1EA== +"@angular-devkit/architect@0.901.8", "@angular-devkit/architect@>=0.900.0 < 0.1000.0": + version "0.901.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.901.8.tgz#d2f5f4c16fba3ed61ee27c7fc72118421ea2b45d" + integrity sha512-tK9ZQlubH6n+q+c2J9Wvfcxg3RFuRiTfJriNoodo6GHvtF2KLdPY67w3Gen0Sp172A5Q8Y927NseddNI8RZ/0A== dependencies: - "@angular-devkit/core" "9.1.7" + "@angular-devkit/core" "9.1.8" rxjs "6.5.4" "@angular-devkit/build-angular@~0.901.7": - version "0.901.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.901.7.tgz#10d55e3c73213971ba7d733f15d66494dfe9918a" - integrity sha512-NiBwapx/XJqYGzSmENff78i6Yif9PjYDJ9BB+59t2eDofkCZUcPFrhQmRgliO7rt6RATvT81lDP89+LBXCTQPw== - dependencies: - "@angular-devkit/architect" "0.901.7" - "@angular-devkit/build-optimizer" "0.901.7" - "@angular-devkit/build-webpack" "0.901.7" - "@angular-devkit/core" "9.1.7" + version "0.901.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.901.8.tgz#6450be4743dacf564af143c85d2a03b7bdd81551" + integrity sha512-W2RTjtPPJRbke6K7Qt9eZOPRGfFBFsYzskxsuxXwkW2RPopj6k1wUWh9Be8CtAMAUlhyPvlzviOtv3F7leYr3w== + dependencies: + "@angular-devkit/architect" "0.901.8" + "@angular-devkit/build-optimizer" "0.901.8" + "@angular-devkit/build-webpack" "0.901.8" + "@angular-devkit/core" "9.1.8" "@babel/core" "7.9.0" "@babel/generator" "7.9.3" "@babel/preset-env" "7.9.0" "@babel/template" "7.8.6" "@jsdevtools/coverage-istanbul-loader" "3.0.3" - "@ngtools/webpack" "9.1.7" + "@ngtools/webpack" "9.1.8" ajv "6.12.0" autoprefixer "9.7.4" babel-loader "8.0.6" @@ -169,7 +169,7 @@ cacache "15.0.0" caniuse-lite "^1.0.30001032" circular-dependency-plugin "5.2.0" - copy-webpack-plugin "5.1.1" + copy-webpack-plugin "6.0.2" core-js "3.6.4" css-loader "3.5.1" cssnano "4.1.10" @@ -178,7 +178,7 @@ glob "7.1.6" jest-worker "25.1.0" karma-source-map-support "1.4.0" - less "3.11.1" + less "3.11.3" less-loader "5.0.0" license-webpack-plugin "2.1.4" loader-utils "2.0.0" @@ -215,17 +215,17 @@ worker-plugin "4.0.3" "@angular-devkit/build-ng-packagr@~0.901.7": - version "0.901.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.901.7.tgz#d8bce9c67cba4299f6c4a5bdd13840bc39ed0f58" - integrity sha512-HJ6nzXIUyI8yUuXGtdk26qDgLzlmfSwsSuc8JWdeqieP82fz/qokf78vVAqyHyJ9gi90IZiPO2+oh6Ot6UMo+g== + version "0.901.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.901.8.tgz#7a8fee590f0898451c98da410ff36edc160912d9" + integrity sha512-7a419fmAq+uYunVEBm79MncqOvmprH+6QmGXkORYPAKZ+p5wWdu84AM1aKK/l0UaZ335ErxexpEVnbvSVtVc1Q== dependencies: - "@angular-devkit/architect" "0.901.7" + "@angular-devkit/architect" "0.901.8" rxjs "6.5.4" -"@angular-devkit/build-optimizer@0.901.7": - version "0.901.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.901.7.tgz#e72fc3031207a78aee175a76d3317cdf226984e9" - integrity sha512-Xuce3StdxhcgLYb0BAaFGr3Bzj5EM2OsAqIT15PkikWY1k5cK50vPxoC/BkX4QDL9eXSHtqAfMBfA6h5N422vw== +"@angular-devkit/build-optimizer@0.901.8": + version "0.901.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.901.8.tgz#55a6cecf9b963bac15f84b5db8ec211c82119954" + integrity sha512-k9DynuWKMsJk5xg+LthdsqmOlGVMVP/TEu2odiVty9gnTVlIjs1bUzs+HNAF/w11juIBcVKa690K+FkSCalo9w== dependencies: loader-utils "2.0.0" source-map "0.7.3" @@ -233,13 +233,13 @@ typescript "3.6.5" webpack-sources "1.4.3" -"@angular-devkit/build-webpack@0.901.7": - version "0.901.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.901.7.tgz#6d93c38756540a02e67d2c3ccfac4220c62962de" - integrity sha512-pTLW5Eqy9cHgv78LKiH0e30lxqKzUPjh1djvNtFsEemOHsfKQdAfjLjikoaQvqMoBKVaUU7r2vmyyS17cH+1yw== +"@angular-devkit/build-webpack@0.901.8": + version "0.901.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.901.8.tgz#19fbac49c3f60c16d6814d61e518431503ab746a" + integrity sha512-OyLfPI0yo1Qg4I1QP8ZxEYVxrf3IDjGfpxlKXqSChpEy5m/uZmBIRDZ/n/G3+32xFc6MWEdU4EHfRrfn17ae/w== dependencies: - "@angular-devkit/architect" "0.901.7" - "@angular-devkit/core" "9.1.7" + "@angular-devkit/architect" "0.901.8" + "@angular-devkit/core" "9.1.8" rxjs "6.5.4" "@angular-devkit/core@7.3.10", "@angular-devkit/core@^7.3.6": @@ -253,10 +253,10 @@ rxjs "6.3.3" source-map "0.7.3" -"@angular-devkit/core@8.3.26", "@angular-devkit/core@^8.0.3": - version "8.3.26" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.3.26.tgz#a331f02603a64fe53b9527b07e04ee18c798b9a6" - integrity sha512-b1ng9091o33s55/cwQYh1kboiJtj8y8z8xQWATDI9kRmNIQkWYVwVa/MzgPRJ4bzbEGG3zIUHCsp52A6vuGr2A== +"@angular-devkit/core@8.3.27", "@angular-devkit/core@^8.0.3": + version "8.3.27" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.3.27.tgz#d99e797bb0c8be643f01c707fdb3904ab347b106" + integrity sha512-Mf2u86mIMqCEbbhaN74r4lvMb4nKgAZIQo3s/GQIfKkGaGYvxmiQHkltdVvSR+fgaQ0CMh4ARI0uefmzQyUsAA== dependencies: ajv "6.10.2" fast-json-stable-stringify "2.0.0" @@ -264,10 +264,10 @@ rxjs "6.4.0" source-map "0.7.3" -"@angular-devkit/core@9.1.7", "@angular-devkit/core@^9.0.0": - version "9.1.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.1.7.tgz#f193ccbae4c80b34188bc9cc401c16b3ced50339" - integrity sha512-guvolu9Cl+qYMTtedLZD9wCqustJjdqzJ2psD2C1Sr1LrX9T0mprmDldR/YnhsitThveJEb6sM/0EvqWxoSvKw== +"@angular-devkit/core@9.1.8", "@angular-devkit/core@^9.0.0": + version "9.1.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-9.1.8.tgz#7c517a14e3ddfd180858972d9f1836aa90a1248e" + integrity sha512-4k1pZwje2oh5c/ULg7pnCBzTstx3l3uF7O5tQq/KXomDDsam97IhLm6cKUqQpaoyC1NUsBV6xJARJ0PyUP5TPQ== dependencies: ajv "6.12.0" fast-json-stable-stringify "2.1.0" @@ -275,12 +275,12 @@ rxjs "6.5.4" source-map "0.7.3" -"@angular-devkit/schematics@9.1.7": - version "9.1.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.1.7.tgz#45394a1c928db449b412dacf205c3ec78fb5ef0c" - integrity sha512-oeHPJePBcPp/bd94jHQeFUnft93PGF5iJiKV9szxqS8WWC5OMZ5eK7icRY0PwvLyfenspAZxdZcNaqJqPMul5A== +"@angular-devkit/schematics@9.1.8": + version "9.1.8" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-9.1.8.tgz#8eeea0b6f9702a5b065f909cdcaf1d35cc8e2fa3" + integrity sha512-/8L5J4X6SkcFMRmrSQHvJWOPilrMWTNlv1lD+1z06D3xGJEktVxXM3gCUXhDrbMvpoi+lYtR2Fuia0E6zvyjCQ== dependencies: - "@angular-devkit/core" "9.1.7" + "@angular-devkit/core" "9.1.8" ora "4.0.3" rxjs "6.5.4" @@ -293,28 +293,28 @@ rxjs "6.3.3" "@angular-devkit/schematics@^8.0.6": - version "8.3.26" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.3.26.tgz#91fcea47279a09d7504051bec17277c07f16e03b" - integrity sha512-IoZbXVFGLvVi5d0ozfssWDXuzot0/pMSKbQPzWIG8K7nCo7nNMVYpsMHrEVYUikA9EQEL5LqMCGohH36/zVPcA== + version "8.3.27" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-8.3.27.tgz#1e2c720cd464a14ec65a963025e7eb0d980eda33" + integrity sha512-SGh/yubOPT7jFZTfTyBLJqYUUBYU6pcTY3ifKVoa1JG28GegKSycusCMeX19gEY7awKYIuxR9WJeGgV6evSQPA== dependencies: - "@angular-devkit/core" "8.3.26" + "@angular-devkit/core" "8.3.27" rxjs "6.4.0" "@angular/animations@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-9.1.9.tgz#de54334ea195189402487855c9a98f5618605da4" - integrity sha512-qWVi0TxmU6HeXAgEsfpQvFFygh+a0kH2kGe6bWij4XvG6dWfV3xZjlaFwSIYGk+yK4yL0+9+PAXH+ENfxNw+Cw== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-9.1.11.tgz#2c7b6e584df0ba0884d05f01fa7ab86c1fdd1c5e" + integrity sha512-VKAExUnEJfo1PDQKagpx2pn+QMZCsPLRiADzTdl4U0VPylK3ALbn4ZNY9UbdwyE2plitz++LkH7sEGGfh+PNrQ== "@angular/cli@~9.1.7": - version "9.1.7" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-9.1.7.tgz#0532b9c55d267cd6ee3edb79fec8b19c4e64e607" - integrity sha512-NhsIa725S/U/n7nDxp6ForusdYHEXF4aSIvsFRdoK6vbQ889c5e1Rdj+T5EWXLmpQZxeprSKhLI2alNX0nVhhQ== - dependencies: - "@angular-devkit/architect" "0.901.7" - "@angular-devkit/core" "9.1.7" - "@angular-devkit/schematics" "9.1.7" - "@schematics/angular" "9.1.7" - "@schematics/update" "0.901.7" + version "9.1.8" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-9.1.8.tgz#fd143e26c913ccea5b8ac1716e1c168432ac96d3" + integrity sha512-yfF7glPo3Xm7fTJVln1bFZVXqHu8wkIGZRZGb6lsJa+QH4ePxHgn+dNYXho0MYpGUnhY7xOBW4MJzjS7E+1y5Q== + dependencies: + "@angular-devkit/architect" "0.901.8" + "@angular-devkit/core" "9.1.8" + "@angular-devkit/schematics" "9.1.8" + "@schematics/angular" "9.1.8" + "@schematics/update" "0.901.8" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" debug "4.1.1" @@ -332,14 +332,14 @@ uuid "7.0.2" "@angular/common@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-9.1.9.tgz#16e77b2db675b80e32f1788a20c538150fd09294" - integrity sha512-y/tJtkuOJhV2kcaXZyrLZH84i4uQ1r+vaaEHvXj+JZYfYfcMMd/TDqMiPcIkUb3RxqghtZ+q0ZNW5D1Nlru3Pw== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-9.1.11.tgz#1323f7b043410791bd2d0d71b0bbb1f862319c04" + integrity sha512-Vh5lF7zWwDK9RedmYXUc8vUXyrecR3j1mAWlTlnmcHYxxFThPzN/dr0slQcPi6nyJn0EmyRKUGvAoZx4rIb7wg== "@angular/compiler-cli@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-9.1.9.tgz#e3c234d888074002fa5f6b7eab4f63f4ddbdb7bd" - integrity sha512-aLr2eaDlREN8XybgTbelvjtSZ8eAkxBPilnkddc700BgiC6ImyUSKaItOwa8bnjQwq4Wlz5eVG0ibsrX+5MXwg== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-9.1.11.tgz#39da68ddadb52008fe5231141707bddd3aa790b2" + integrity sha512-9qIxbtpRXOQnRm6inxCa5HuH87MSuMzuceD0YBVzl8v+vLtewon9KXYMmF4kTBhWa/LEa8FrajljLh0azf3VLg== dependencies: canonical-path "1.0.0" chokidar "^3.0.0" @@ -355,48 +355,48 @@ yargs "15.3.0" "@angular/compiler@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.1.9.tgz#cbf678ee28a0811a8ef3ee7be565d4911ff28ec7" - integrity sha512-kjFgaTB2ckr9lgmkS1dOGRT7kmzpQueydxsxXSHWgICNVE6F/u1PHyeSOyJRpxW0GnrkLq3QM2EUFnQGGga5bg== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.1.11.tgz#4c7100f53c87f47e793e149427b8bdee44302381" + integrity sha512-MbVheCG0U8gt6xtiipau20N26mD2sXjLChVmRKgO6rbDruxboNMZfEd94q9NP9JRaUsVnjXvY7GMDldoymdXig== "@angular/core@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.1.9.tgz#db4241f867d6e14b81ed6e7c50334813c6ebfc10" - integrity sha512-q/DERgVU6vK2LtTcdVCGGBcoO424WsEfImh3Vcuy+P/ZVmthlDUC/+q+tSKt8MMf4hLpxFDQJE8vUSkktj7QEw== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.1.11.tgz#7a92d27292212ed381be15f9000d4019867f1c7c" + integrity sha512-KAlEedBo761O1aeoTJVziOSHi8Fttk9ipvbDZXYT/o0W/KdVwubxP34g9t5aD8LCcF8+L0z4VLw++HjdJAUpwg== "@angular/forms@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-9.1.9.tgz#20c9a79d1dcb2cace45df9e2f304b658e02c1687" - integrity sha512-r675yImnb/0pY7K5W3V2ITa7YETu1I2AS+bRfII6UQ6gthyeFFOHb5noa7YneC2yqQiM6E4DQmF5ig3daPuFNg== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-9.1.11.tgz#fa246144649236613598a0471aa7f39b60f986b5" + integrity sha512-t4WHrh6ot1r8zdV+3fJz7g9rCok77c9CiIevhH2dR/idxD+HtFR0wqmcBQzsn+rNVB0f0TiSHDrj+TeELIFyWw== "@angular/language-service@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-9.1.9.tgz#332a7e54d5e553b92342b10703dc13aba474a28f" - integrity sha512-yT6HPpdAe2mD9HRoTCiWFog1MRJt+0j+CLbI/Ql7C6pH6vbjmfsJ55xMmQ7eS6trsnebpMWTUv1f2GRykv3ygw== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-9.1.11.tgz#a2afed4f0167e6f4d3d748214b8e1c27980703ce" + integrity sha512-jfm4etbqldj6MTwECwyoAs7tXEAR8K/8P8dBZnsELhY+V8oFidTJI3NY52PB3Ym7leSPorYdOAeUMMuQfPaVxg== "@angular/localize@~9.1.0", "@angular/localize@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.9.tgz#0867a82abcdeb6b88cbd4d61e1410f3c63a66ddb" - integrity sha512-U/4axUr99GO3+dRuMIEyJ1FqXauT82x/w7GulWu05qk8ML8MaIQ58RcB/LTJJY1s1qfwdcsyzX2wZrZcwBLKlA== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-9.1.11.tgz#25921d794836fb7a07d284c1ac0ed06c10e77d50" + integrity sha512-CrR7RniwJIK3+QKH8nHl35KDAHZn1mp1QAd5vujTWKw6YRLfio7SjM9qIfzw5y4WZuUitTsqKlQT/m/NK146Ag== dependencies: "@babel/core" "7.8.3" glob "7.1.2" yargs "15.3.0" "@angular/platform-browser-dynamic@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.9.tgz#12f8b05d3c9ef0844df88f3833e29ea1e49ec5e0" - integrity sha512-b9MG5MWne+IuL3uLm8jwPhlJzqYaGBGk/qibOqb17T24j1iyrlO7T5bZ8zO6pUy5iT/TahVfHPnPJC1qTK5OmA== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.11.tgz#82af336b05e0d7b7478a2ca7f6282825b211f340" + integrity sha512-Qw3rfVFF0Wtu+UwraqKPCgTA3uoNPGf4vKSfuCuXTrG0p7j+3mCP59aUv5gGH7GV1UQ++jZRx5pbWF43zrC8Hw== "@angular/platform-browser@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.1.9.tgz#c2fcc50aebfdc268521b407e32dc0d967cb40411" - integrity sha512-V861X3MxJp1AlMTnkUPldpBLIJbApXF3ka0A5Dq2nVJCyOFeteGkaRWSBgqe2jxmq+LVpJbzcNvtDFXw6mQ0jA== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-9.1.11.tgz#4da8e2a4231d5162304746a39f54cb2d3f241b7c" + integrity sha512-KDxoiFhW4UD+EqchcKpQVSLwg9Rd3JbWytZLchFV6nH8BFnshfJtw2tyPT8bMhFVG9n9zSR4QSGaozWgoDs9mw== "@angular/router@~9.1.9": - version "9.1.9" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-9.1.9.tgz#5d79d72cb62ea1c04ea70238846653ecf9c4a73c" - integrity sha512-4u+CWMPB4hCkAsFCEzC94YEWT0wVozqGkc/Dortt2hFaqvZpIegg6iJVZlDxuyDjzFYBPnnbTDdgiTTA8ckfuA== + version "9.1.11" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-9.1.11.tgz#b6d28af55fe5631bbc46f306a0e7866253d4f3b1" + integrity sha512-D6CCDeSK/F6dWSB/a1g/zB072xG5LadLSV8afQ57oX1KHePx21LcoRG4tUtFMMHh/jZXRc9pMQIR1/9FrrXF3Q== "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.8.3": version "7.10.1" @@ -2052,10 +2052,10 @@ inquirer "^6.2.0" npmlog "^4.1.2" -"@lerna/publish@3.22.0": - version "3.22.0" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.22.0.tgz#7a3fb61026d3b7425f3b9a1849421f67d795c55d" - integrity sha512-8LBeTLBN8NIrCrLGykRu+PKrfrCC16sGCVY0/bzq9TDioR7g6+cY0ZAw653Qt/0Kr7rg3J7XxVNdzj3fvevlwA== +"@lerna/publish@3.22.1": + version "3.22.1" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.22.1.tgz#b4f7ce3fba1e9afb28be4a1f3d88222269ba9519" + integrity sha512-PG9CM9HUYDreb1FbJwFg90TCBQooGjj+n/pb3gw/eH5mEDq0p8wKdLFe0qkiqUkm/Ub5C8DbVFertIo0Vd0zcw== dependencies: "@evocateur/libnpmaccess" "^3.1.2" "@evocateur/npm-registry-fetch" "^4.0.0" @@ -2078,7 +2078,7 @@ "@lerna/run-lifecycle" "3.16.2" "@lerna/run-topologically" "3.18.5" "@lerna/validation-error" "3.13.0" - "@lerna/version" "3.22.0" + "@lerna/version" "3.22.1" figgy-pudding "^3.5.1" fs-extra "^8.1.0" npm-package-arg "^6.1.0" @@ -2191,10 +2191,10 @@ dependencies: npmlog "^4.1.2" -"@lerna/version@3.22.0": - version "3.22.0" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.22.0.tgz#67e1340c1904e9b339becd66429f32dd8ad65a55" - integrity sha512-6uhL6RL7/FeW6u1INEgyKjd5dwO8+IsbLfkfC682QuoVLS7VG6OOB+JmTpCvnuyYWI6fqGh1bRk9ww8kPsj+EA== +"@lerna/version@3.22.1": + version "3.22.1" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.22.1.tgz#9805a9247a47ee62d6b81bd9fa5fb728b24b59e2" + integrity sha512-PSGt/K1hVqreAFoi3zjD0VEDupQ2WZVlVIwesrE5GbrL2BjXowjCsTDPqblahDUPy0hp6h7E2kG855yLTp62+g== dependencies: "@lerna/check-working-tree" "3.16.5" "@lerna/child-process" "3.16.5" @@ -2262,12 +2262,12 @@ schematics-utilities "^1.1.1" tslib "^1.9.0" -"@ngtools/webpack@9.1.7": - version "9.1.7" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-9.1.7.tgz#4322757b029e1175a3361183c06b31d0576538d8" - integrity sha512-A7VB2I42Kn+7jl0tDKzGNLAoZLWSqkKo9Hg1bmKpvAAIz+DSbq3uV+JWgGgTprM3tn0lfkVgmqk4H17HKwAOcg== +"@ngtools/webpack@9.1.8": + version "9.1.8" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-9.1.8.tgz#50a906047b284098e5cd669e8174c9cfcaf3bafc" + integrity sha512-2Y27PrHLMyrIDmuicjp2OU7KIr9bggwMLNZdjfpcuXlOPP/BYviuhgkkYsfJysrpDRUJUHlXRJG7OJbgyFM7gQ== dependencies: - "@angular-devkit/core" "9.1.7" + "@angular-devkit/core" "9.1.8" enhanced-resolve "4.1.1" rxjs "6.5.4" webpack-sources "1.4.3" @@ -2314,24 +2314,52 @@ dependencies: tslib "^1.9.0" +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + "@nodelib/fs.stat@^1.1.2": version "1.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + +"@npmcli/move-file@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464" + integrity sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw== + dependencies: + mkdirp "^1.0.4" + "@octokit/auth-token@^2.4.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.1.tgz#375d79eebd03750e6a9b0299e80b8167c7c85655" - integrity sha512-NB81O5h39KfHYGtgfWr2booRxp2bWOJoqbWwbyUg2hw6h35ArWYlAST5B3XwAkbdcx13yt84hFXyFP5X0QToWA== + version "2.4.2" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.2.tgz#10d0ae979b100fa6b72fa0e8e63e27e6d0dbff8a" + integrity sha512-jE/lE/IKIz2v1+/P0u4fJqv0kYwXOTujKemJMFr6FeopsxlIK3+wKDCJGnysg81XID5TgZQbIfuJ5J0lnTiuyQ== dependencies: - "@octokit/types" "^4.0.1" + "@octokit/types" "^5.0.0" "@octokit/endpoint@^6.0.1": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.2.tgz#e876aafe68d7f9b6c6d80bf29458403f9afe7b2b" - integrity sha512-xs1mmCEZ2y4shXCpFjNq3UbmNR+bLzxtZim2L0zfEtj9R6O6kc4qLDvYw66hvO6lUsYzPTM5hMkltbuNAbRAcQ== + version "6.0.3" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.3.tgz#dd09b599662d7e1b66374a177ab620d8cdf73487" + integrity sha512-Y900+r0gIz+cWp6ytnkibbD95ucEzDSKzlEnaWS52hbCDNcCJYO5mRmWW7HRAnDc7am+N/5Lnd8MppSaTYx1Yg== dependencies: - "@octokit/types" "^4.0.1" + "@octokit/types" "^5.0.0" is-plain-object "^3.0.0" universal-user-agent "^5.0.0" @@ -2379,13 +2407,13 @@ once "^1.4.0" "@octokit/request@^5.2.0": - version "5.4.4" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.4.tgz#dc57e85e86284fa016d0c1a2701a70a10cec4ff2" - integrity sha512-vqv1lz41c6VTxUvF9nM+a6U+vvP3vGk7drDpr0DVQg4zyqlOiKVrY17DLD6de5okj+YLHKcoqaUZTBtlNZ1BtQ== + version "5.4.5" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.5.tgz#8df65bd812047521f7e9db6ff118c06ba84ac10b" + integrity sha512-atAs5GAGbZedvJXXdjtKljin+e2SltEs48B3naJjqWupYl2IUBbB/CJisyjbNHcKpHzb3E+OYEZ46G8eakXgQg== dependencies: "@octokit/endpoint" "^6.0.1" "@octokit/request-error" "^2.0.0" - "@octokit/types" "^4.0.1" + "@octokit/types" "^5.0.0" deprecation "^2.0.0" is-plain-object "^3.0.0" node-fetch "^2.3.0" @@ -2422,9 +2450,16 @@ "@types/node" ">= 8" "@octokit/types@^4.0.1": - version "4.1.9" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-4.1.9.tgz#a3e1ff1a15637ab830fbab0268c2d7ca824bc969" - integrity sha512-hinM/BA2c1vebN2HSR3JtVdYtrSbmvn/doUBZXXuQuh/9o60hYwitQQAGTpJu+k6pjtjURskDHQxUFvqLvYCeA== + version "4.1.10" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-4.1.10.tgz#e4029c11e2cc1335051775bc1600e7e740e4aca4" + integrity sha512-/wbFy1cUIE5eICcg0wTKGXMlKSbaAxEr00qaBXzscLXpqhcwgXeS6P8O0pkysBhRfyjkKjJaYrvR1ExMO5eOXQ== + dependencies: + "@types/node" ">= 8" + +"@octokit/types@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.0.0.tgz#cbdf3c060f6c0436c004ec402c5082c32de72511" + integrity sha512-3LVS+MbeqwSd5G4KS8123cZz+hWomsiGeMnQ/QJIBFDwL/YHX8kkr0FZXrgWEMO7Fgi2/VOrhbiFnk9sZ+s4qA== dependencies: "@types/node" ">= 8" @@ -2468,21 +2503,21 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@schematics/angular@9.1.7": - version "9.1.7" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.1.7.tgz#b7801a5e20f844da560db81d2971590e8ac090ff" - integrity sha512-ld3WcoMWvup04V3OWioQ+AFGQBzz7IDM4Fxc5+Qc3wILWkDJnNkrc4EmJAow96Ab4/T1+Wl1vof3tV4At0BTzA== +"@schematics/angular@9.1.8": + version "9.1.8" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-9.1.8.tgz#da6cd63b65776b18c43d8515bfca754dd9acdbc9" + integrity sha512-fjyAP9m4aF51OVdksRXCOF8BTyt96PqFmKK9G0kuwOzgfx2gPZNOO3wOZH6xFAMZ09y86VGzasZxZNeDdyN4sQ== dependencies: - "@angular-devkit/core" "9.1.7" - "@angular-devkit/schematics" "9.1.7" + "@angular-devkit/core" "9.1.8" + "@angular-devkit/schematics" "9.1.8" -"@schematics/update@0.901.7": - version "0.901.7" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.901.7.tgz#164bff4e97383a0a7d266fe5eb2e1bf41f14dfe9" - integrity sha512-6IpQVFvbu47CrXfqqHAzv2vi7AOdfi1S+SiayXU6FWTeA2wV47H8R60VjxurL8JkDGoVhFgC4+lK6KG++g3dQw== +"@schematics/update@0.901.8": + version "0.901.8" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.901.8.tgz#d48be9931a2462062d4d4ac05c4b24b319bc064a" + integrity sha512-v1tEYX6yM5vuwXW7AG7OZ4OtjqRwTo3kd69LVJyOdF/d9HlqaAFU301RuEsAPwOrPqZEQdTwklH1fNJnqgpB/w== dependencies: - "@angular-devkit/core" "9.1.7" - "@angular-devkit/schematics" "9.1.7" + "@angular-devkit/core" "9.1.8" + "@angular-devkit/schematics" "9.1.8" "@yarnpkg/lockfile" "1.1.0" ini "1.3.5" npm-package-arg "^8.0.0" @@ -2508,6 +2543,13 @@ dependencies: type-detect "4.0.8" +"@swimlane/ngx-datatable@^16.0.0": + version "16.1.1" + resolved "https://registry.yarnpkg.com/@swimlane/ngx-datatable/-/ngx-datatable-16.1.1.tgz#1215e81f51400314ba6899f66acdbd4e3bde2915" + integrity sha512-h2A9xC/jo314eC/CzA19aoY/YadhdTupolCwpgB9Cbw3sDRi3x/LeGsdLSV0bj3XBKQUB3DA/gPuASKYuhm8iA== + dependencies: + tslib "^1.9.0" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -2591,9 +2633,9 @@ "@types/node" "*" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz#79d7a78bad4219f4c03d6557a1c72d9ca6ba62d5" - integrity sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" + integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== "@types/istanbul-lib-report@*": version "3.0.0" @@ -2624,9 +2666,9 @@ pretty-format "^25.2.1" "@types/json-schema@^7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" - integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + version "7.0.5" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" + integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== "@types/minimatch@*": version "3.0.3" @@ -2639,14 +2681,14 @@ integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= "@types/node@*", "@types/node@>= 8": - version "14.0.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.11.tgz#61d4886e2424da73b7b25547f59fdcb534c165a3" - integrity sha512-lCvvI24L21ZVeIiyIUHZ5Oflv1hhHQ5E1S25IRlKIXaRkVgmXpJMI3wUJkmym2bTbCe+WoIibQnMVAU3FguaOg== + version "14.0.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.13.tgz#ee1128e881b874c371374c1f72201893616417c9" + integrity sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA== "@types/node@^12.11.1": - version "12.12.44" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.44.tgz#0d400a1453adcb359b133acceae4dd8bb0e0a159" - integrity sha512-jM6QVv0Sm5d3nW+nUD5jSzPcO6oPqboitSNcwgBay9hifVq/Rauq1PYnROnsmuw45JMBiTnsPAno0bKu2e2xrg== + version "12.12.47" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.47.tgz#5007b8866a2f9150de82335ca7e24dd1d59bdfb5" + integrity sha512-yzBInQFhdY8kaZmqoL2+3U5dSTMrKaYcb561VU+lDzAYvqt+2lojvBEy+hmpSNuXnPTx7m9+04CzWYOUqWME2A== "@types/node@^8.0.31": version "8.10.61" @@ -2945,9 +2987,9 @@ acorn@^6.0.1, acorn@^6.2.1: integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== acorn@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" - integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== + version "7.3.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" + integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== add-stream@^1.0.0: version "1.0.0" @@ -3241,6 +3283,11 @@ array-union@^1.0.1, array-union@^1.0.2: dependencies: array-uniq "^1.0.1" +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" @@ -3886,6 +3933,29 @@ cacache@^13.0.1: ssri "^7.0.0" unique-filename "^1.1.1" +cacache@^15.0.4: + version "15.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.4.tgz#b2c23cf4ac4f5ead004fb15a0efb0a20340741f1" + integrity sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw== + dependencies: + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^5.1.1" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.0" + tar "^6.0.2" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -4005,9 +4075,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001061: - version "1.0.30001079" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001079.tgz#ed3e5225cd9a6850984fdd88bf24ce45d69b9c22" - integrity sha512-2KaYheg0iOY+CMmDuAB3DHehrXhhb4OZU4KBVGDr/YKyYAcpudaiUQ9PJ9rxrPlKEoJ3ATasQ5AN48MqpwS43Q== + version "1.0.30001081" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001081.tgz#40615a3c416a047c5a4d45673e5257bf128eb3b5" + integrity sha512-iZdh3lu09jsUtLE6Bp8NAbJskco4Y3UDtkR3GTCJGsbMowBU5IWDFF79sV2ws7lSqTzWyKazxam2thasHymENQ== canonical-path@1.0.0: version "1.0.0" @@ -4055,9 +4125,9 @@ chalk@^3.0.0: supports-color "^7.1.0" chalk@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" - integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -4775,23 +4845,22 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-webpack-plugin@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz#5481a03dea1123d88a988c6ff8b78247214f0b88" - integrity sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg== - dependencies: - cacache "^12.0.3" - find-cache-dir "^2.1.0" - glob-parent "^3.1.0" - globby "^7.1.1" - is-glob "^4.0.1" - loader-utils "^1.2.3" - minimatch "^3.0.4" +copy-webpack-plugin@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.2.tgz#10efc6ad219a61acbf2f5fb50af83da38431bc34" + integrity sha512-9Gm8X0c6eXlKnmltMPFCBeGOKjtcRIyTt4VaO3k1TkNgVTe5Ov2lYsYVuyLp0kp8DItO3apewflM+1GYgh6V2Q== + dependencies: + cacache "^15.0.4" + fast-glob "^3.2.2" + find-cache-dir "^3.3.1" + glob-parent "^5.1.1" + globby "^11.0.1" + loader-utils "^2.0.0" normalize-path "^3.0.0" - p-limit "^2.2.1" - schema-utils "^1.0.0" - serialize-javascript "^2.1.2" - webpack-log "^2.0.0" + p-limit "^2.3.0" + schema-utils "^2.7.0" + serialize-javascript "^3.1.0" + webpack-sources "^1.4.3" core-js-compat@^3.6.2: version "3.6.5" @@ -5441,13 +5510,20 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -dir-glob@^2.0.0, dir-glob@^2.2.2: +dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== dependencies: path-type "^3.0.0" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + directory-tree@^2.2.3: version "2.2.4" resolved "https://registry.yarnpkg.com/directory-tree/-/directory-tree-2.2.4.tgz#6d5bd7d82e48378e256a1e87b678a43c50076e2e" @@ -5566,9 +5642,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.413: - version "1.3.464" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.464.tgz#fe13feaa08f6f865d3c89d5d72e54c194f463aa5" - integrity sha512-Oo+0+CN9d2z6FToQW6Hwvi9ez09Y/usKwr0tsDsyg43a871zVJCi1nR0v03djLbRNcaCKjtrnVf2XJhTxEpPCg== + version "1.3.467" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.467.tgz#84eeb332134d49f0e49b88588824e56b20af9e27" + integrity sha512-U+QgsL8TZDU/n+rDnYDa3hY5uy3C4iry9mrJS0PNBBGwnocuQ+aHSfgY44mdlaK9744X5YqrrGUvD9PxCLY1HA== elliptic@^6.0.0, elliptic@^6.5.2: version "6.5.2" @@ -5997,6 +6073,18 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" +fast-glob@^3.1.1, fast-glob@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" + integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-stable-stringify@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -6017,6 +6105,13 @@ fastparse@^1.1.2: resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== +fastq@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481" + integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q== + dependencies: + reusify "^1.0.4" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -6105,7 +6200,7 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@3.3.1, find-cache-dir@^3.2.0: +find-cache-dir@3.3.1, find-cache-dir@^3.2.0, find-cache-dir@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== @@ -6471,7 +6566,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@~5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== @@ -6558,6 +6653,18 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globby@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.1.1" + ignore "^5.1.4" + merge2 "^1.3.0" + slash "^3.0.0" + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -6581,18 +6688,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= - dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" @@ -6975,16 +7070,16 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" -ignore@^3.3.5: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - ignore@^4.0.3: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" @@ -8338,9 +8433,9 @@ lcid@^2.0.0: invert-kv "^2.0.0" lerna@^3.19.0: - version "3.22.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.22.0.tgz#da14d08f183ffe6eec566a4ef3f0e11afa621183" - integrity sha512-xWlHdAStcqK/IjKvjsSMHPZjPkBV1lS60PmsIeObU8rLljTepc4Sg/hncw4HWfQxPIewHAUTqhrxPIsqf9L2Eg== + version "3.22.1" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.22.1.tgz#82027ac3da9c627fd8bf02ccfeff806a98e65b62" + integrity sha512-vk1lfVRFm+UuEFA7wkLKeSF7Iz13W+N/vFd48aW2yuS7Kv0RbNm2/qcDPV863056LMfkRlsEe+QYOw3palj5Lg== dependencies: "@lerna/add" "3.21.0" "@lerna/bootstrap" "3.21.0" @@ -8355,9 +8450,9 @@ lerna@^3.19.0: "@lerna/init" "3.21.0" "@lerna/link" "3.21.0" "@lerna/list" "3.21.0" - "@lerna/publish" "3.22.0" + "@lerna/publish" "3.22.1" "@lerna/run" "3.21.0" - "@lerna/version" "3.22.0" + "@lerna/version" "3.22.1" import-local "^2.0.0" npmlog "^4.1.2" @@ -8370,24 +8465,7 @@ less-loader@5.0.0: loader-utils "^1.1.0" pify "^4.0.1" -less@3.11.1: - version "3.11.1" - resolved "https://registry.yarnpkg.com/less/-/less-3.11.1.tgz#c6bf08e39e02404fe6b307a3dfffafdc55bd36e2" - integrity sha512-tlWX341RECuTOvoDIvtFqXsKj072hm3+9ymRBe76/mD6O5ZZecnlAOVDlWAleF2+aohFrxNidXhv2773f6kY7g== - dependencies: - clone "^2.1.2" - tslib "^1.10.0" - optionalDependencies: - errno "^0.1.1" - graceful-fs "^4.1.2" - image-size "~0.5.0" - mime "^1.4.1" - mkdirp "^0.5.0" - promise "^7.1.1" - request "^2.83.0" - source-map "~0.6.0" - -less@^3.10.3: +less@3.11.3, less@^3.10.3: version "3.11.3" resolved "https://registry.yarnpkg.com/less/-/less-3.11.3.tgz#2d853954fcfe0169a8af869620bcaa16563dcc1c" integrity sha512-VkZiTDdtNEzXA3LgjQiC3D7/ejleBPFVvq+aRI9mIj+Zhmif5TvFPM244bT4rzkvOCvJ9q4zAztok1M7Nygagw== @@ -8880,7 +8958,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3: +merge2@^1.2.3, merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -9103,7 +9181,7 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*, mkdirp@^1.0.3: +mkdirp@*, mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -9856,7 +9934,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1, p-limit@^2.2.2: +p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.2, p-limit@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -9903,6 +9981,13 @@ p-map@^3.0.0: dependencies: aggregate-error "^3.0.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -10181,6 +10266,11 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pbkdf2@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" @@ -11420,6 +11510,11 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rgb-regex@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" @@ -11435,7 +11530,7 @@ right-pad@^1.0.1: resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" integrity sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA= -rimraf@3.0.2, rimraf@^3.0.0: +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -11489,6 +11584,11 @@ run-async@^2.2.0, run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== +run-parallel@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -11614,7 +11714,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6.4, schema-utils@^2.6.5: +schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6.4, schema-utils@^2.6.5, schema-utils@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== @@ -11862,11 +11962,6 @@ sisteransi@^1.0.4: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - slash@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" @@ -12565,7 +12660,7 @@ tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: safe-buffer "^5.1.2" yallist "^3.0.3" -tar@^6.0.1: +tar@^6.0.1, tar@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39" integrity sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg== @@ -13598,9 +13693,9 @@ widest-line@^3.1.0: string-width "^4.0.0" windows-release@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.0.tgz#dce167e9f8be733f21c849ebd4d03fe66b29b9f0" - integrity sha512-2HetyTg1Y+R+rUgrKeUEhAG/ZuOmTrI1NBb3ZyAGQMYmOJjBBPe4MTodghRkmLJZHwkuPi02anbeGP+Zf401LQ== + version "3.3.1" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.1.tgz#cb4e80385f8550f709727287bf71035e209c4ace" + integrity sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A== dependencies: execa "^1.0.0" diff --git a/templates/app/angular/angular.json b/templates/app/angular/angular.json index dfcaa5b9d3..9177a6d86c 100644 --- a/templates/app/angular/angular.json +++ b/templates/app/angular/angular.json @@ -46,6 +46,21 @@ "inject": true, "bundleName": "fontawesome-v4-shims.min" }, + { + "input": "node_modules/@swimlane/ngx-datatable/index.css", + "inject": true, + "bundleName": "ngx-datatable-index" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/assets/icons.css", + "inject": true, + "bundleName": "ngx-datatable-icons" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/themes/material.css", + "inject": true, + "bundleName": "ngx-datatable-material" + }, "src/styles.scss" ], "scripts": [] diff --git a/templates/module/angular/angular.json b/templates/module/angular/angular.json index 7120592c4d..642e441e81 100644 --- a/templates/module/angular/angular.json +++ b/templates/module/angular/angular.json @@ -24,27 +24,44 @@ "tsConfig": "tsconfig.app.json", "aot": true, "extractCss": true, - "assets": [ - "src/favicon.ico", - "src/assets" - ], + "assets": ["src/favicon.ico", "src/assets"], "styles": [ + { + "input": "node_modules/@abp/ng.theme.shared/styles/bootstrap-rtl.min.css", + "inject": false, + "bundleName": "bootstrap-rtl.min" + }, { "input": "node_modules/bootstrap/dist/css/bootstrap.min.css", "inject": true, - "bundleName": "bootstrap.min" + "bundleName": "bootstrap-ltr.min" }, - "src/styles.scss", { "input": "node_modules/@fortawesome/fontawesome-free/css/all.min.css", - "inject": false, + "inject": true, "bundleName": "fontawesome-all.min" }, { "input": "node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css", - "inject": false, + "inject": true, "bundleName": "fontawesome-v4-shims.min" - } + }, + { + "input": "node_modules/@swimlane/ngx-datatable/index.css", + "inject": true, + "bundleName": "ngx-datatable-index" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/assets/icons.css", + "inject": true, + "bundleName": "ngx-datatable-icons" + }, + { + "input": "node_modules/@swimlane/ngx-datatable/themes/material.css", + "inject": true, + "bundleName": "ngx-datatable-material" + }, + "src/styles.scss" ], "scripts": [] }, @@ -102,10 +119,7 @@ "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.spec.json", "karmaConfig": "karma.conf.js", - "assets": [ - "src/favicon.ico", - "src/assets" - ], + "assets": ["src/favicon.ico", "src/assets"], "styles": [ { "input": "node_modules/bootstrap/dist/css/bootstrap.min.css", @@ -130,14 +144,8 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": [ - "tsconfig.app.json", - "tsconfig.spec.json", - "e2e/tsconfig.json" - ], - "exclude": [ - "**/node_modules/**" - ] + "tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"], + "exclude": ["**/node_modules/**"] } }, "e2e": { @@ -187,9 +195,7 @@ "projects/my-project-name/tsconfig.lib.json", "projects/my-project-name/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } } } @@ -227,9 +233,7 @@ "projects/my-project-name-config/tsconfig.lib.json", "projects/my-project-name-config/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] + "exclude": ["**/node_modules/**"] } } } @@ -239,4 +243,4 @@ "cli": { "analytics": false } -} \ No newline at end of file +} diff --git a/templates/module/angular/src/app/app.component.ts b/templates/module/angular/src/app/app.component.ts index 1cf17b8116..bf2a27962a 100644 --- a/templates/module/angular/src/app/app.component.ts +++ b/templates/module/angular/src/app/app.component.ts @@ -1,6 +1,4 @@ -import { LazyLoadService, LOADING_STRATEGY } from '@abp/ng.core'; -import { Component, OnInit } from '@angular/core'; -import { forkJoin } from 'rxjs'; +import { Component } from '@angular/core'; @Component({ selector: 'app-root', @@ -9,17 +7,4 @@ import { forkJoin } from 'rxjs'; `, }) -export class AppComponent implements OnInit { - constructor(private lazyLoadService: LazyLoadService) {} - - ngOnInit() { - forkJoin([ - this.lazyLoadService.load( - LOADING_STRATEGY.PrependAnonymousStyleToHead('fontawesome-v4-shims.min.css'), - ), - this.lazyLoadService.load( - LOADING_STRATEGY.PrependAnonymousStyleToHead('fontawesome-all.min.css'), - ), - ]).subscribe(); - } -} +export class AppComponent {}