Browse Source

Merge remote-tracking branch 'abpframework/dev' into docs

pull/4335/head
liangshiw@outlook.com 6 years ago
parent
commit
1e128f787f
  1. 23
      docs/en/API/Application-Configuration.md
  2. 3
      docs/en/API/JavaScript-API/Auth.md
  3. 4
      docs/en/Application-Services.md
  4. 2
      docs/en/AspNet-Boilerplate-Migration-Guide.md
  5. 3
      docs/en/AspNetCore/JavaScript-API/Auth.md
  6. 3
      docs/en/AspNetCore/JavaScript-API/Index.md
  7. 58
      docs/en/Blob-Storing-Azure.md
  8. 2
      docs/en/Blob-Storing-Custom-Provider.md
  9. 2
      docs/en/Blob-Storing-Database.md
  10. 3
      docs/en/Clock.md
  11. 159
      docs/en/Data-Seeding.md
  12. 281
      docs/en/Data-Transfer-Objects.md
  13. 4
      docs/en/Domain-Driven-Design.md
  14. 8
      docs/en/Entities.md
  15. 4
      docs/en/Guid-Generation.md
  16. 3
      docs/en/Json.md
  17. 29
      docs/en/Modules/Docs.md
  18. 8
      docs/en/Modules/Virtual-File-Explorer.md
  19. 113
      docs/en/Timing.md
  20. 5
      docs/en/UI/AspNetCore/JavaScript-API/Index.md
  21. 37
      docs/en/UI/AspNetCore/Tag-Helpers/Button-groups.md
  22. 74
      docs/en/UI/AspNetCore/Tag-Helpers/Carousel.md
  23. 20
      docs/en/docs-nav.json
  24. 30
      docs/zh-Hans/Modules/Docs.md
  25. 8
      docs/zh-Hans/Modules/Virtual-File-Explorer.md
  26. 14
      framework/Volo.Abp.sln
  27. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Carousel/AbpCarouselTagHelperService.cs
  28. 24
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  29. 3
      framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xml
  30. 30
      framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xsd
  31. 22
      framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj
  32. 10
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureModule.cs
  33. 24
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs
  34. 110
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProvider.cs
  35. 39
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfiguration.cs
  36. 9
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobProviderConfigurationNames.cs
  37. 22
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNameCalculator.cs
  38. 7
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/IAzureBlobNameCalculator.cs
  39. 2
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs
  40. 14
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/MySQL/AbpEntityFrameworkCoreMySQLModule.cs
  41. 14
      framework/src/Volo.Abp.EntityFrameworkCore.Oracle.Devart/Volo/Abp/EntityFrameworkCore/Oracle/Devart/AbpEntityFrameworkCoreOracleDevartModule.cs
  42. 14
      framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo/Abp/EntityFrameworkCore/PostgreSql/AbpEntityFrameworkCorePostgreSqlModule.cs
  43. 14
      framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/SqlServer/AbpEntityFrameworkCoreSqlServerModule.cs
  44. 18
      framework/src/Volo.Abp.Guids/Volo/Abp/Guids/AbpSequentialGuidGeneratorOptions.cs
  45. 2
      framework/src/Volo.Abp.Guids/Volo/Abp/Guids/SequentialGuidGenerator.cs
  46. 4
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
  47. 6
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json
  48. 4
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json
  49. 19
      framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj
  50. 20
      framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestBase.cs
  51. 64
      framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AbpBlobStoringAzureTestModule.cs
  52. 16
      framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobContainer_Tests.cs
  53. 57
      framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/AzureBlobNameCalculator_Tests.cs
  54. 14
      modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/BlobStoringDatabaseDomainModule.cs
  55. 8
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml
  56. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs
  57. 6
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs
  58. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs
  59. 2
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs
  60. 10
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs
  61. 8
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs
  62. 12
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs
  63. 1
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs
  64. 41
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreOrganizationUnitRepository.cs
  65. 32
      modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoOrganizationUnitRepository.cs
  66. 13
      modules/identity/test/Volo.Abp.Identity.TestBase/Volo/Abp/Identity/OrganizationUnitRepository_Tests.cs
  67. 18
      npm/ng-packs/angular.json
  68. 7
      npm/ng-packs/package.json
  69. 22
      npm/ng-packs/packages/core/src/lib/services/list.service.ts
  70. 6
      npm/ng-packs/packages/core/src/lib/tests/list.service.spec.ts
  71. 123
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html
  72. 42
      npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.ts
  73. 154
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.html
  74. 59
      npm/ng-packs/packages/identity/src/lib/components/users/users.component.ts
  75. 142
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html
  76. 51
      npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.ts
  77. 1
      npm/ng-packs/packages/theme-shared/ng-package.json
  78. 1
      npm/ng-packs/packages/theme-shared/package.json
  79. 8
      npm/ng-packs/packages/theme-shared/src/lib/constants/styles.ts
  80. 2
      npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts
  81. 23
      npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-default.directive.ts
  82. 70
      npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts
  83. 46
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  84. 5
      npm/ng-packs/scripts/build.ts
  85. 59
      npm/ng-packs/scripts/install-new-dependencies.ts
  86. 5
      npm/ng-packs/scripts/package.json
  87. 29
      npm/ng-packs/scripts/post-install.ts
  88. 10
      npm/ng-packs/scripts/prod-build.ts
  89. 2
      npm/ng-packs/scripts/publish.ts
  90. 19
      npm/ng-packs/scripts/push.ts
  91. 37
      npm/ng-packs/scripts/sync.ts
  92. 12
      npm/ng-packs/scripts/yarn.lock
  93. 2
      npm/ng-packs/tslint.json
  94. 555
      npm/ng-packs/yarn.lock
  95. 15
      templates/app/angular/angular.json
  96. 60
      templates/module/angular/angular.json
  97. 19
      templates/module/angular/src/app/app.component.ts

23
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.

3
docs/en/API/JavaScript-API/Auth.md

@ -1,3 +0,0 @@
# abp.auth JavaScript API
TODO

4
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.

2
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

3
docs/en/AspNetCore/JavaScript-API/Auth.md

@ -1,3 +0,0 @@
This document has moved.
[Click to navigate to JavaScript Auth document](../../API/JavaScript-API/Auth.md)

3
docs/en/AspNetCore/JavaScript-API/Index.md

@ -1,3 +0,0 @@
This document has moved.
[Click to navigate to JavaScript API document](../../API/JavaScript-API/Index.md)

58
docs/en/Blob-Storing-Azure.md

@ -1,3 +1,59 @@
# BLOB Storing Azure Provider
This feature will be available with v3.0!
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<AbpBlobStoringOptions>(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/<tenant-id>` 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.

2
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.
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.

2
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**

3
docs/en/Clock.md

@ -1,3 +0,0 @@
# Clock
TODO

159
docs/en/Data-Seeding.md

@ -1,3 +1,160 @@
# Data Seeding
TODO
## 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<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public BookStoreDataSeedContributor(
IRepository<Book, Guid> 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.

281
docs/en/Data-Transfer-Objects.md

@ -1,3 +1,280 @@
## Data Transfer Objects
# Data Transfer Objects
TODO
## 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<User>`, 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<Permission>` 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<TKey>` is a simple interface that only defines an `Id` property. You can implement it or inherit from the `EntityDto<TKey>` 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<Guid>
{
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<T>` interface and `ListResultDto<T>` class is used to make it standard.
The definition of the `IListResult<T>` interface:
````csharp
public interface IListResult<T>
{
IReadOnlyList<T> 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<Product, Guid> _productRepository;
public ProductAppService(IRepository<Product, Guid> productRepository)
{
_productRepository = productRepository;
}
public async Task<ListResultDto<ProductDto>> GetListAsync()
{
//Get entities from the repository
List<Product> products = await _productRepository.GetListAsync();
//Map entities to DTOs
List<ProductDto> productDtos =
ObjectMapper.Map<List<Product>, List<ProductDto>>(products);
//Return the result
return new ListResultDto<ProductDto>(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<ProductDto>` 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<T>` inherits from the `IListResult<T>` 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<T>` inherits from the `ListResultDto<T>` and also implements the `IPagedResult<T>`.
**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<Product, Guid> _productRepository;
public ProductAppService(IRepository<Product, Guid> productRepository)
{
_productRepository = productRepository;
}
public async Task<PagedResultDto<ProductDto>> 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<Product> products = await query.ToListAsync();
//Map entities to DTOs
List<ProductDto> productDtos =
ObjectMapper.Map<List<Product>, List<ProductDto>>(products);
//Return the result
return new PagedResultDto<ProductDto>(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.

4
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

8
docs/en/Entities.md

@ -26,9 +26,9 @@ public class Book : Entity<Guid>
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.

4
docs/en/Guid-Generation.md

@ -106,4 +106,6 @@ Configure<AbpSequentialGuidGeneratorOptions>(options =>
{
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary;
});
````
````
> 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.

3
docs/en/Json.md

@ -0,0 +1,3 @@
# JSON
TODO

29
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

8
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:

113
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<AbpClockOptions>(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.

5
docs/en/API/JavaScript-API/Index.md → 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

37
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
<abp-button-group>
<abp-button button-type="Secondary">Left</abp-button>
<abp-button button-type="Secondary">Middle</abp-button>
<abp-button button-type="Secondary">Right</abp-button>
</abp-button-group>
````
## 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`

74
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
<abp-carousel>
<abp-carousel-item src=""></abp-carousel-item>
<abp-carousel-item src=""></abp-carousel-item>
<abp-carousel-item src=""></abp-carousel-item>
</abp-carousel>
````
## 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.

20
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"
}
]
},

30
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 集成

8
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` 如下所示:

14
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}

4
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<CarouselItem> itemList)
{
var itemsHtml = new StringBuilder("");
itemsHtml.Append("<div class= \"carousel-inner\">");
foreach (var carouselItem in itemList)
{
@ -46,6 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel
itemsHtml.AppendLine(carouselItem.Html);
}
itemsHtml.Append("</div>");
output.Content.SetHtmlContent(itemsHtml.ToString());
}
@ -137,4 +139,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Carousel
}
}
}
}

24
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<TimingDto> 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
};
}
}
}
}

3
framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.BlobStoring.Azure/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

22
framework/src/Volo.Abp.BlobStoring.Azure/Volo.Abp.BlobStoring.Azure.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.BlobStoring.Azure</AssemblyName>
<PackageId>Volo.Abp.BlobStoring.Azure</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.4.3" />
</ItemGroup>
</Project>

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

24
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<AzureBlobProviderConfiguration> azureConfigureAction)
{
containerConfiguration.ProviderType = typeof(AzureBlobProvider);
azureConfigureAction(new AzureBlobProviderConfiguration(containerConfiguration));
return containerConfiguration;
}
}
}

110
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<bool> 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<bool> ExistsAsync(BlobProviderExistsArgs args)
{
var blobName = AzureBlobNameCalculator.Calculate(args);
return await BlobExistsAsync(args, blobName);
}
public override async Task<Stream> 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<bool> 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<bool> ContainerExistsAsync(BlobContainerClient blobContainerClient)
{
return (await blobContainerClient.ExistsAsync()).Value;
}
}
}

39
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<string>(AzureBlobProviderConfigurationNames.ConnectionString);
set => _containerConfiguration.SetConfiguration(AzureBlobProviderConfigurationNames.ConnectionString, Check.NotNullOrWhiteSpace(value, nameof(value)));
}
/// <summary>
/// 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 <see cref="BlobProviderArgs"/> will be used.
/// </summary>
public string ContainerName
{
get => _containerConfiguration.GetConfiguration<string>(AzureBlobProviderConfigurationNames.ContainerName);
set => _containerConfiguration.SetConfiguration(AzureBlobProviderConfigurationNames.ContainerName, Check.NotNullOrWhiteSpace(value, nameof(value)));
}
/// <summary>
/// Default value: false.
/// </summary>
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;
}
}
}

9
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";
}
}

22
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}";
}
}
}

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

2
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs

@ -28,4 +28,4 @@ namespace Volo.Abp.BlobStoring
CancellationToken = cancellationToken;
}
}
}
}

14
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<AbpSequentialGuidGeneratorOptions>(options =>
{
if (options.DefaultSequentialGuidType == null)
{
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString;
}
});
}
}
}

14
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<AbpSequentialGuidGeneratorOptions>(options =>
{
if (options.DefaultSequentialGuidType == null)
{
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsBinary;
}
});
}
}
}

14
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<AbpSequentialGuidGeneratorOptions>(options =>
{
if (options.DefaultSequentialGuidType == null)
{
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAsString;
}
});
}
}
}

14
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<AbpSequentialGuidGeneratorOptions>(options =>
{
if (options.DefaultSequentialGuidType == null)
{
options.DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd;
}
});
}
}
}

18
framework/src/Volo.Abp.Guids/Volo/Abp/Guids/AbpSequentialGuidGeneratorOptions.cs

@ -3,13 +3,21 @@
public class AbpSequentialGuidGeneratorOptions
{
/// <summary>
/// Default value: <see cref="SequentialGuidType.SequentialAtEnd"/>.
/// Default value: null (unspecified).
/// Use <see cref="GetDefaultSequentialGuidType"/> method
/// to get the value on use, since it fall backs to a default value.
/// </summary>
public SequentialGuidType DefaultSequentialGuidType { get; set; }
public AbpSequentialGuidGeneratorOptions()
public SequentialGuidType? DefaultSequentialGuidType { get; set; }
/// <summary>
/// Get the <see cref="DefaultSequentialGuidType"/> value
/// or returns <see cref="SequentialGuidType.SequentialAtEnd"/>
/// if <see cref="DefaultSequentialGuidType"/> was null.
/// </summary>
public SequentialGuidType GetDefaultSequentialGuidType()
{
DefaultSequentialGuidType = SequentialGuidType.SequentialAtEnd;
return DefaultSequentialGuidType ??
SequentialGuidType.SequentialAtEnd;
}
}
}

2
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)

4
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"
}
}

6
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"
}
}
}

4
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"
}
}
}

19
framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo.Abp.BlobStoring.Azure.Tests.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace />
<UserSecretsId>9f0d2c00-80c1-435b-bfab-2c39c8249091</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.BlobStoring.Azure\Volo.Abp.BlobStoring.Azure.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<ProjectReference Include="..\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj" />
</ItemGroup>
</Project>

20
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<AbpBlobStoringAzureTestCommonModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
public class AbpBlobStoringAzureTestBase : AbpIntegratedTest<AbpBlobStoringAzureTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
}

64
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
{
/// <summary>
/// This module will not try to connect to azure.
/// </summary>
[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<AbpBlobStoringOptions>(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();
}
}
}

16
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<AbpBlobStoringAzureTestModule>
{
public AzureBlobContainer_Tests()
{
}
}
*/
}

57
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<IAzureBlobNameCalculator>();
_currentTenant = GetRequiredService<ICurrentTenant>();
}
[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
);
}
}
}

14
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<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
if (container.ProviderType == null)
{
container.UseDatabase();
}
});
});
}
}
}

8
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml

@ -92,7 +92,7 @@
}
<div class="list-group">
@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];
<div class="list-group-item list-group-item-action flex-column align-items-start">
@ -140,16 +140,16 @@
<div class="pb-5">
<div class="row">
<div class="col-md-8 box-articles">
@if (Model.Posts.Count > 3)
@if (Model.Posts.Count > 4)
{
<div class="vs-blog-title">
<h2>@L["LastPosts"]</h2>
</div>
<div class="list-group">
@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;
<div class="list-group-item">
<div class="row">

2
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");
}
}
}

6
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);
}
}
}
}

2
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs

@ -183,4 +183,4 @@ namespace Volo.Abp.FeatureManagement
return value;
}
}
}
}

2
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs

@ -9,6 +9,8 @@ namespace Volo.Abp.FeatureManagement
{
Task<FeatureValue> FindAsync(string name, string providerName, string providerKey);
Task<List<FeatureValue>> FindAllAsync(string name, string providerName, string providerKey);
Task<List<FeatureValue>> GetListAsync(string providerName, string providerKey);
}
}

10
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<List<FeatureValue>> 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<List<FeatureValue>> GetListAsync(string providerName, string providerKey)
{
return await DbSet
@ -31,4 +39,4 @@ namespace Volo.Abp.FeatureManagement.EntityFrameworkCore
).ToListAsync();
}
}
}
}

8
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<List<FeatureValue>> 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<List<FeatureValue>> GetListAsync(string providerName, string providerKey)
{
return await GetMongoQueryable()
@ -29,4 +35,4 @@ namespace Volo.Abp.FeatureManagement.MongoDB
.ToListAsync();
}
}
}
}

12
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()
{

1
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IOrganizationUnitRepository.cs

@ -67,6 +67,7 @@ namespace Volo.Abp.Identity
Task<int> GetMembersCountAsync(
OrganizationUnit organizationUnit,
string filter = null,
CancellationToken cancellationToken = default
);

41
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<IdentityUserOrganizationUnit>()
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<int> GetMembersCountAsync(
OrganizationUnit organizationUnit,
string filter = null,
CancellationToken cancellationToken = default)
{
var query = from userOu in DbContext.Set<IdentityUserOrganizationUnit>()
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<IdentityUserOrganizationUnit>().RemoveRange(ouMembersQuery);
}
protected virtual IQueryable<IdentityUser> CreateGetMembersFilteredQuery(OrganizationUnit organizationUnit, string filter = null)
{
var query = from userOu in DbContext.Set<IdentityUserOrganizationUnit>()
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;
}
}
}

32
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<IdentityUser, IMongoQueryable<IdentityUser>>(
!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<IMongoQueryable<IdentityUser>>()
.PageBy<IdentityUser, IMongoQueryable<IdentityUser>>(skipCount, maxResultCount)
@ -132,12 +127,12 @@ namespace Volo.Abp.Identity.MongoDB
public virtual async Task<int> 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<IMongoQueryable<IdentityUser>>()
.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<IdentityUser> CreateGetMembersFilteredQuery(OrganizationUnit organizationUnit, string filter = null)
{
return DbContext.Users.AsQueryable()
.Where(u => u.OrganizationUnits.Any(uou => uou.OrganizationUnitId == organizationUnit.Id))
.WhereIf<IdentityUser, IMongoQueryable<IdentityUser>>(
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter) ||
(u.PhoneNumber != null && u.PhoneNumber.Contains(filter))
);
}
}
}

13
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()
{

18
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": [
{

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

22
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<QueryParamsType = ABP.PageQueryParams> 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<ABP.PageQueryParams>(1);
private _query$ = new ReplaySubject<QueryParamsType>(1);
get query$(): Observable<ABP.PageQueryParams> {
get query$(): Observable<QueryParamsType> {
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<T extends any>(
streamCreatorCallback: QueryStreamCreatorCallback<T>,
streamCreatorCallback: QueryStreamCreatorCallback<T, QueryParamsType>,
): Observable<PagedResultDto<T>> {
this._isLoading$.next(true);
@ -96,6 +98,6 @@ export class ListService implements OnDestroy {
ngOnDestroy() {}
}
export type QueryStreamCreatorCallback<T> = (
query: ABP.PageQueryParams,
export type QueryStreamCreatorCallback<T, QueryParamsType = ABP.PageQueryParams> = (
query: QueryParamsType,
) => Observable<PagedResultDto<T>>;

6
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();

123
npm/ng-packs/packages/identity/src/lib/components/roles/roles.component.html

@ -20,89 +20,54 @@
</div>
<div class="card-body">
<abp-table
*ngIf="[150, 0] as columnWidths"
[abpLoading]="loading"
[abpLoadingDelay]="500"
[abpTableSort]="{ key: sortKey, order: sortOrder }"
[colgroupTemplate]="tableColGroup"
[headerTemplate]="tableHeader"
[bodyTemplate]="tableBody"
[value]="data$ | async"
[rows]="pageQuery.maxResultCount"
[totalRecords]="totalCount$ | async"
[scrollable]="true"
(pageChange)="onPageChange($event)"
>
<ng-template #tableColGroup>
<colgroup>
<col *ngFor="let width of columnWidths" [ngStyle]="{ 'width.px': width || undefined }" />
</colgroup>
</ng-template>
<ng-template #tableHeader>
<tr>
<th>{{ 'AbpIdentity::Actions' | abpLocalization }}</th>
<th (click)="sortOrderIcon.sort('name')">
{{ 'AbpIdentity::RoleName' | abpLocalization }}
<abp-sort-order-icon
#sortOrderIcon
sortKey="name"
[(selectedSortKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td class="text-center">
<div ngbDropdown container="body" class="d-inline-block">
<ngx-datatable [rows]="data$ | async" [count]="totalCount$ | async" [list]="list" default>
<ngx-datatable-column
[name]="'AbpIdentity::Actions' | abpLocalization"
[maxWidth]="150"
[sortable]="false"
>
<ng-template let-row="row" ngx-datatable-cell-template>
<div ngbDropdown container="body" class="d-inline-block">
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
>
<i class="fa fa-cog mr-1"></i>{{ 'AbpIdentity::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
*abpPermission="'AbpIdentity.Roles.Update'"
ngbDropdownItem
(click)="edit(row.id)"
>
<i class="fa fa-cog mr-1"></i>{{ 'AbpIdentity::Actions' | abpLocalization }}
{{ 'AbpIdentity::Edit' | abpLocalization }}
</button>
<button
*abpPermission="'AbpIdentity.Roles.ManagePermissions'"
ngbDropdownItem
(click)="openPermissionsModal(row.name)"
>
{{ 'AbpIdentity::Permissions' | abpLocalization }}
</button>
<button
*ngIf="!row.isStatic"
[abpPermission]="'AbpIdentity.Roles.Delete'"
ngbDropdownItem
(click)="delete(row.id, row.name)"
>
{{ 'AbpIdentity::Delete' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
*abpPermission="'AbpIdentity.Roles.Update'"
ngbDropdownItem
(click)="edit(data.id)"
>
{{ 'AbpIdentity::Edit' | abpLocalization }}
</button>
<button
*abpPermission="'AbpIdentity.Roles.ManagePermissions'"
ngbDropdownItem
(click)="openPermissionsModal(data.name)"
>
{{ 'AbpIdentity::Permissions' | abpLocalization }}
</button>
<button
*ngIf="!data.isStatic"
[abpPermission]="'AbpIdentity.Roles.Delete'"
ngbDropdownItem
(click)="delete(data.id, data.name)"
>
{{ 'AbpIdentity::Delete' | abpLocalization }}
</button>
</div>
</div>
</td>
<td>
{{ data.name
}}<span *ngIf="data.isDefault" class="badge badge-pill badge-success ml-1">{{
'AbpIdentity::DisplayName:IsDefault' | abpLocalization
}}</span>
<span *ngIf="data.isPublic" class="badge badge-pill badge-info ml-1">{{
'AbpIdentity::DisplayName:IsPublic' | abpLocalization
}}</span>
</td>
</tr>
</ng-template>
</abp-table>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'AbpIdentity::RoleName' | abpLocalization"
prop="name"
></ngx-datatable-column>
</ngx-datatable>
</div>
</div>

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

154
npm/ng-packs/packages/identity/src/lib/components/users/users.component.html

@ -20,108 +20,74 @@
</div>
<div class="card-body">
<div id="data-tables-table-filter" class="data-tables-filter">
<label
><input
<div class="input-group">
<input
type="search"
class="form-control form-control-sm"
class="form-control"
[placeholder]="'AbpUi::PagerSearch' | abpLocalization"
(input.debounce)="onSearch($event.target.value)"
/></label>
[(ngModel)]="list.filter"
/>
</div>
</div>
<abp-table
*ngIf="[150, 250, 250, 250] as columnWidths"
[abpLoading]="loading"
[abpLoadingDelay]="500"
[abpTableSort]="{ key: sortKey, order: sortOrder }"
[colgroupTemplate]="tableColGroup"
[headerTemplate]="tableHeader"
[bodyTemplate]="tableBody"
[value]="data$ | async"
[rows]="pageQuery.maxResultCount"
[totalRecords]="totalCount$ | async"
[scrollable]="true"
(pageChange)="onPageChange($event)"
>
<ng-template #tableColGroup>
<colgroup>
<col *ngFor="let width of columnWidths" [ngStyle]="{ 'width.px': width || undefined }" />
</colgroup>
</ng-template>
<ng-template #tableHeader>
<tr>
<th>{{ 'AbpIdentity::Actions' | abpLocalization }}</th>
<th (click)="sortOrderIcon.sort('userName')">
{{ 'AbpIdentity::UserName' | abpLocalization }}
<abp-sort-order-icon
#sortOrderIcon
sortKey="userName"
[(selectedSortKey)]="sortKey"
[(order)]="sortOrder"
>
</abp-sort-order-icon>
</th>
<th (click)="sortOrderIcon.sort('email')">
{{ 'AbpIdentity::EmailAddress' | abpLocalization }}
<abp-sort-order-icon
sortKey="email"
[(selectedSortKey)]="sortKey"
[(order)]="sortOrder"
></abp-sort-order-icon>
</th>
<th (click)="sortOrderIcon.sort('phoneNumber')">
{{ 'AbpIdentity::PhoneNumber' | abpLocalization }}
<abp-sort-order-icon
sortKey="phoneNumber"
[(selectedSortKey)]="sortKey"
[(order)]="sortOrder"
<ngx-datatable [rows]="data$ | async" [count]="totalCount$ | async" [list]="list" default>
<ngx-datatable-column
[name]="'AbpIdentity::Actions' | abpLocalization"
[maxWidth]="150"
[sortable]="false"
>
<ng-template let-row="row" ngx-datatable-cell-template>
<div ngbDropdown container="body" class="d-inline-block">
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
>
</abp-sort-order-icon>
</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td class="text-center">
<div ngbDropdown container="body" class="d-inline-block">
<i class="fa fa-cog mr-1"></i>{{ 'AbpIdentity::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
*abpPermission="'AbpIdentity.Users.Update'"
ngbDropdownItem
(click)="edit(row.id)"
>
<i class="fa fa-cog mr-1"></i>{{ 'AbpIdentity::Actions' | abpLocalization }}
{{ 'AbpIdentity::Edit' | abpLocalization }}
</button>
<button
*abpPermission="'AbpIdentity.Users.ManagePermissions'"
ngbDropdownItem
(click)="openPermissionsModal(row.id)"
>
{{ 'AbpIdentity::Permissions' | abpLocalization }}
</button>
<button
*abpPermission="'AbpIdentity.Users.Delete'"
ngbDropdownItem
(click)="delete(row.id, row.userName)"
>
{{ 'AbpIdentity::Delete' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
*abpPermission="'AbpIdentity.Users.Update'"
ngbDropdownItem
(click)="edit(data.id)"
>
{{ 'AbpIdentity::Edit' | abpLocalization }}
</button>
<button
*abpPermission="'AbpIdentity.Users.ManagePermissions'"
ngbDropdownItem
(click)="openPermissionsModal(data.id)"
>
{{ 'AbpIdentity::Permissions' | abpLocalization }}
</button>
<button
*abpPermission="'AbpIdentity.Users.Delete'"
ngbDropdownItem
(click)="delete(data.id, data.userName)"
>
{{ 'AbpIdentity::Delete' | abpLocalization }}
</button>
</div>
</div>
</td>
<td>{{ data.userName }}</td>
<td>{{ data.email }}</td>
<td>{{ data.phoneNumber }}</td>
</tr>
</ng-template>
</abp-table>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[width]="200"
[name]="'AbpIdentity::UserName' | abpLocalization"
prop="userName"
></ngx-datatable-column>
<ngx-datatable-column
[width]="200"
[name]="'AbpIdentity::EmailAddress' | abpLocalization"
prop="email"
></ngx-datatable-column>
<ngx-datatable-column
[width]="200"
[name]="'AbpIdentity::PhoneNumber' | abpLocalization"
prop="phoneNumber"
></ngx-datatable-column>
</ngx-datatable>
</div>
</div>

59
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<number>;
@ViewChild('modalContent', {static: false})
@ViewChild('modalContent', { static: false })
modalContent: TemplateRef<any>;
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<AbstractControl> = (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) {

142
npm/ng-packs/packages/tenant-management/src/lib/components/tenants/tenants.component.html

@ -18,99 +18,73 @@
</div>
</div>
</div>
<div class="card-body">
<div id="data-tables-table-filter" class="data-tables-filter">
<label
><input
<div class="input-group">
<input
type="search"
class="form-control form-control-sm"
class="form-control"
[placeholder]="'AbpUi::PagerSearch' | abpLocalization"
(input.debounce)="onSearch($event.target.value)"
/></label>
[(ngModel)]="list.filter"
/>
</div>
</div>
<abp-table
*ngIf="[150, 0] as columnWidths"
[abpLoading]="loading"
[abpLoadingDelay]="500"
[abpTableSort]="{ key: sortKey, order: sortOrder }"
[colgroupTemplate]="tableColGroup"
[headerTemplate]="tableHeader"
[bodyTemplate]="tableBody"
[value]="data$ | async"
[abpTableSort]="{ key: sortKey, order: sortOrder }"
[rows]="pageQuery.maxResultCount"
[totalRecords]="totalCount$ | async"
[scrollable]="true"
(pageChange)="onPageChange($event)"
>
<ng-template #tableColGroup>
<colgroup>
<col *ngFor="let width of columnWidths" [ngStyle]="{ 'width.px': width || undefined }" />
</colgroup>
</ng-template>
<ng-template #tableHeader let-columns>
<tr>
<th>{{ 'AbpTenantManagement::Actions' | abpLocalization }}</th>
<th (click)="sortOrderIcon.sort('name')">
{{ 'AbpTenantManagement::TenantName' | abpLocalization }}
<abp-sort-order-icon
#sortOrderIcon
sortKey="name"
[(selectedSortKey)]="sortKey"
[(order)]="sortOrder"
<ngx-datatable [rows]="data$ | async" [count]="totalCount$ | async" [list]="list" default>
<ngx-datatable-column
[name]="'AbpTenantManagement::Actions' | abpLocalization"
[maxWidth]="150"
[sortable]="false"
>
<ng-template let-row="row" ngx-datatable-cell-template>
<div ngbDropdown container="body" class="d-inline-block">
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
>
</abp-sort-order-icon>
</th>
</tr>
</ng-template>
<ng-template #tableBody let-data>
<tr>
<td class="text-center">
<div ngbDropdown container="body" class="d-inline-block">
<i class="fa fa-cog mr-1"></i>{{ 'AbpTenantManagement::Actions' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
*abpPermission="'AbpTenantManagement.Tenants.Update'"
ngbDropdownItem
(click)="editTenant(row.id)"
>
{{ 'AbpTenantManagement::Edit' | abpLocalization }}
</button>
<button
*abpPermission="'AbpTenantManagement.Tenants.ManageConnectionStrings'"
ngbDropdownItem
(click)="onEditConnectionString(row.id)"
>
{{ 'AbpTenantManagement::Permission:ManageConnectionStrings' | abpLocalization }}
</button>
<button
*abpPermission="'AbpTenantManagement.Tenants.ManageFeatures'"
ngbDropdownItem
(click)="openFeaturesModal(row.id)"
>
{{ 'AbpTenantManagement::Permission:ManageFeatures' | abpLocalization }}
</button>
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
*abpPermission="'AbpTenantManagement.Tenants.Delete'"
ngbDropdownItem
(click)="delete(row.id, row.name)"
>
<i class="fa fa-cog mr-1"></i>{{ 'AbpTenantManagement::Actions' | abpLocalization }}
{{ 'AbpTenantManagement::Delete' | abpLocalization }}
</button>
<div ngbDropdownMenu>
<button
*abpPermission="'AbpTenantManagement.Tenants.Update'"
ngbDropdownItem
(click)="editTenant(data.id)"
>
{{ 'AbpTenantManagement::Edit' | abpLocalization }}
</button>
<button
*abpPermission="'AbpTenantManagement.Tenants.ManageConnectionStrings'"
ngbDropdownItem
(click)="onEditConnectionString(data.id)"
>
{{ 'AbpTenantManagement::Permission:ManageConnectionStrings' | abpLocalization }}
</button>
<button
*abpPermission="'AbpTenantManagement.Tenants.ManageFeatures'"
ngbDropdownItem
(click)="openFeaturesModal(data.id)"
>
{{ 'AbpTenantManagement::Permission:ManageFeatures' | abpLocalization }}
</button>
<button
*abpPermission="'AbpTenantManagement.Tenants.Delete'"
ngbDropdownItem
(click)="delete(data.id, data.name)"
>
{{ 'AbpTenantManagement::Delete' | abpLocalization }}
</button>
</div>
</div>
</td>
<td>{{ data.name }}</td>
</tr>
</ng-template>
</abp-table>
</div>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column
[name]="'AbpTenantManagement::TenantName' | abpLocalization"
prop="name"
></ngx-datatable-column>
</ngx-datatable>
</div>
</div>
@ -128,7 +102,7 @@
{{ 'AbpTenantManagement::Cancel' | abpLocalization }}
</button>
<abp-button iconClass="fa fa-check" (click)="save()" [disabled]="isDisabledSaveButton">{{
'AbpIdentity::Save' | abpLocalization
'AbpTenantManagement::Save' | abpLocalization
}}</abp-button>
</ng-template>
</abp-modal>

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

1
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"

1
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"
},

8
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 {

2
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';

23
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';
}
}

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

46
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],
},
],
};
}

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

59
npm/ng-packs/scripts/install-new-dependencies.ts

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

5
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": {

29
npm/ng-packs/scripts/post-install.ts

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

10
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',

2
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');

19
npm/ng-packs/scripts/push.ts

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

37
npm/ng-packs/scripts/sync.ts

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

12
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"

2
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,
{

555
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"

15
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": []

60
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
}
}
}

19
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';
<router-outlet></router-outlet>
`,
})
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 {}

Loading…
Cancel
Save