diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index c190a91470..a292da5f9b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -198,6 +198,21 @@ "Language": "Language", "Optional": "Optional", "CreateArticleLanguageInfo": "The language in which the post is written", - "Enum:ContentSource:2": "Video Post" + "Enum:ContentSource:2": "Video Post", + "VideoPreview": "Video Preview", + "VideoPreviewErrorMessage": "Given video url couldn't retrieve from Youtube. This can be caused by either video is private or the given URL is not available.", + "DeleteCoverImage": "Delete Cover Image", + "DeleteCoverImageConfirmationMessage": "Are you sure you want to delete the cover image for \"{0}\"?", + "DeleteCoverImageSuccessMessage": "Cover image successfully deleted", + "PaymentsOf": "Payments of", + "ShowPaymentsOfOrganization": "Show payments", + "Date": "Date", + "Products": "Products", + "TotalAmount": "Total amount", + "Currency": "Currency", + "Gateway": "Gateway", + "State": "State", + "FailReason": "Fail reason" + } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json index 9ac7e39b9e..1884b0025f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/tr.json @@ -159,6 +159,10 @@ "UnlimitedQuestionCount": "Sınırsız soru sayısı", "Language": "Dil", "Optional": "Opsiyonel", - "CreateArticleLanguageInfo": "Makalenin yazıldığı dil" + "CreateArticleLanguageInfo": "Makalenin yazıldığı dil", + "Enum:ContentSource:2": "Video İçerik", + "DeleteCoverImage": "Kapak Fotoğrafını Sil", + "DeleteCoverImageConfirmationMessage": "Kapak fotoğrafını \"{0}\" isimli makale için silmek istediğinize emin misiniz?", + "DeleteCoverImageSuccessMessage": "Kapak fotoğrafı başarılı bir şekilde silinmiştir" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index 5bf922f012..9c1e434ac1 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -132,6 +132,10 @@ "ChooseYourContentType": "Please choose the way you want to add your content.", "PostContentViaGithub": "I want to add my article with GitHub in accordance with the markdown rules.", "PostContentViaYoutube": "I want to share my videos available on Youtube here.", - "PostContentViaExternalSource": "I want to add the content I published on another platform here." + "PostContentViaExternalSource": "I want to add the content I published on another platform here.", + "GitHubUserNameValidationMessage": "Your Github username can not include whitespace, please be sure your Github username is correct.", + "PersonalSiteUrlValidationMessage": "Your personal site URL can not include whitespace, please be sure your personal site URL is correct.", + "TwitterUserNameValidationMessage": "Your Twitter username can not include whitespace, please be sure your Twitter username is correct.", + "LinkedinUrlValidationMessage": "Your Linkedin URL can not include whitespace, please be sure your Linkedin URL is correct." } } diff --git a/common.props b/common.props index f5db446396..593fe92213 100644 --- a/common.props +++ b/common.props @@ -1,7 +1,7 @@ - latest - 4.2.0 + latest + 4.3.0 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/POST.md b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/POST.md new file mode 100644 index 0000000000..d16afb4244 --- /dev/null +++ b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/POST.md @@ -0,0 +1,241 @@ +# ABP Framework 4.2 RC Has Been Published + +Today, we have released the [ABP Framework](https://abp.io/) and the [ABP Commercial](https://commercial.abp.io/) 4.2.0 RC (Release Candidate). This blog post introduces the new features and important changes in this new version. + +> **The planned release date for the [4.2.0 final](https://github.com/abpframework/abp/milestone/48) version is January 28, 2021**. + +## Get Started with the 4.2 RC + +If you want to try the version `4.2.0` today, follow the steps below; + +1) **Upgrade** the ABP CLI to the version `4.2.0-rc.2` using a command line terminal: + +````bash +dotnet tool update Volo.Abp.Cli -g --version 4.2.0-rc.2 +```` + +**or install** if you haven't installed before: + +````bash +dotnet tool install Volo.Abp.Cli -g --version 4.2.0-rc.2 +```` + +2) Create a **new application** with the `--preview` option: + +````bash +abp new BookStore --preview +```` + +See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all the available options. + +> You can also use the *Direct Download* tab on the [Get Started](https://abp.io/get-started) page by selecting the **Preview checkbox**. + +## What's new with the ABP Framework 4.2 + +## IRepository.GetQueryableAsync() + +> **This version comes with an important change about using `IQueryable` features over the [repositories](https://docs.abp.io/en/abp/4.2/Repositories). It is suggested to read this section carefully and apply in your applications.** + +`IRepository` interface inherits `IQueryable`, so you can directly use the standard LINQ extension methods, like `Where`, `OrderBy`, `First`, `Sum`... etc. + +**Example: Using LINQ directly over the repository object** + +````csharp +public class BookAppService : ApplicationService, IBookAppService +{ + private readonly IRepository _bookRepository; + + public BookAppService(IRepository bookRepository) + { + _bookRepository = bookRepository; + } + + public async Task DoItInOldWayAsync() + { + //Apply any standard LINQ extension method + var query = _bookRepository + .Where(x => x.Price > 10) + .OrderBy(x => x.Name); + + //Execute the query asynchronously + var books = await AsyncExecuter.ToListAsync(query); + } +} +```` + +*See [the documentation](https://docs.abp.io/en/abp/4.2/Repositories#iqueryable-async-operations) if you wonder what is the `AsyncExecuter`.* + +Beginning from the version 4.2, the recommended way is using `IRepository.GetQueryableAsync()` to obtain an `IQueryable`, then use the LINQ extension methods over it. + +**Example: Using the new GetQueryableAsync method** + +````csharp +public async Task DoItInNewWayAsync() +{ + //Use GetQueryableAsync to obtain the IQueryable first + var queryable = await _bookRepository.GetQueryableAsync(); + + //Then apply any standard LINQ extension method + var query = queryable + .Where(x => x.Price > 10) + .OrderBy(x => x.Name); + + //Finally, execute the query asynchronously + var books = await AsyncExecuter.ToListAsync(query); +} +```` + +ABP may start a database transaction when you get an `IQueryable` (If current [Unit Of Work](https://docs.abp.io/en/abp/latest/Unit-Of-Work) is transactional). In this new way, it is possible to **start the database transaction in an asynchronous way**. Previously, we could not get the advantage of asynchronous while starting the transactions. + +> **The new way has a significant performance and scalability gain. The old usage (directly using LINQ over the repositories) will be removed in the next major version.** You have a lot of time for the change, but we recommend to immediately take the action since the old usage has a big scalability problem. + +#### About IRepository Async Extension Methods + +Using IRepository Async Extension Methods has no such a problem. The examples below are pretty fine: + +````csharp +var countAll = await _personRepository + .CountAsync(); + +var count = await _personRepository + .CountAsync(x => x.Name.StartsWith("A")); + +var book1984 = await _bookRepository + .FirstOrDefaultAsync(x => x.Name == "John"); +```` + +See the [repository documentation](https://docs.abp.io/en/abp/4.2/Repositories#iqueryable-async-operations) to understand the relation between `IQueryable` and asynchronous operations. + +### Repository Bulk Operations + +This version adds the following methods to the repositories: + +* `InsertManyAsync` +* `UpdateManyAsync` +* `DeleteManyAsync` + +The purpose of these methods to insert, update or delete many entities in one call with a better performance. + +Currently, **MongoDB** provider implements these methods as a single bulk operation since MongoDB API natively supports. But current **Entity Framework Core** implementation is not a real bulk operation. Instead, it does its best with the native API of the EF Core. If you want to implement in a more performant way, you can [customize the bulk operations](https://docs.abp.io/en/abp/4.2/Entity-Framework-Core#customize-bulk-operations) with your own implementation or by using a library. We could find a good open source library for EF Core 5.0 to implement it. + +### Selecting DBMS on Template Creation + +[ABP CLI](https://docs.abp.io/en/abp/4.2/CLI#new) now has an option to specify the DBMS when you use EF Core as the database provider. + +**Example: Select MySQL as the DBMS** + +````bash +abp new BookStore -dbms mysql --preview +```` + +Available options: `SqlServer` (default), `MySQL`, `SQLite`, `Oracle-Devart`, `PostgreSQL`. See the [documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Other-DBMS) to use any other DBMS or switch the DBMS later. + +One change related to this feature is that: Now, the startup template doesn't come with an **initial migration** file. This is because the database migrations are different based on your DBMS preference and should be re-created. However, when you first run the `.DbMigrator` application, it will create the initial migration and create the database just like before. + +> See The Initial Migration section in the [Getting Started](https://docs.abp.io/en/abp/4.2/Getting-Started-Running-Solution?DB=EF#database-migrations) document if you have problems on running the `.DbMigrator` application first time. + +### Swagger UI Login / Authorization + +Testing the swagger UI was requiring some additional work, especially your authentication server is separated from the application that hosts the Swagger UI. + +With the version 4.2, the startup templates come with the authorization pre-configured for you. An Authorize button is available when you open the Swagger UI: + +![swagger-authorize](swagger-authorize.png) + +When you click, it opens a modal to authorize: + +![swagger-authorize](swagger-authorize-modal.png) + +When you click to the Authorize button here, you are redirected to the login page to login with your username and password (default username is `admin` and password is `1q2w3E*`). + +> Remember to select the Scopes (typically **select all**) you want to use before clicking to the Authorize button. + +### Angular Unit Testing + +We've improved the modules and the startup template to setup and write unit tests easier with the Angular UI. See the [Angular Unit Testing document](https://docs.abp.io/en/abp/4.2/UI/Angular/Testing) for details. + +### Other News + +* Improved HTTP **request-response performance** by resolving dependencies in a deferred way in the action/page filters, interceptors and some other services. +* Removed `MultipleActiveResultSets` from connection strings for new templates for SQL Server, since the new EF Core gives a warning when using it. If you want to use it, you need to change the connection string yourself. +* Added `HardDeleteAsync` extension method that takes a predicate to delete multiple entities. This extension method is available if the entity [Soft Delete](https://docs.abp.io/en/abp/latest/Data-Filtering). +* Implemented the [Page Alerts](https://docs.abp.io/en/abp/4.2/UI/Angular/Page-Alerts) for the **Angular UI**. +* Implemented [Page Progress](https://docs.abp.io/en/abp/4.2/UI/Blazor/Page-Progress) for the **Blazor UI**. It automatically shows an undetermined progress bar on top of the page while performing an AJAX request. It also proves an API to you if you need to show/hide the progress bar in your code. + +## What's new with the ABP Commercial 4.2 + +### Microservice Startup Template + +The new [Microservice Startup Template](https://docs.abp.io/en/commercial/4.2/startup-templates/microservice/index) is a generic solution to start a new microservice solution. + +While we accept that every microservice solution will be different and every system has its own design requirements and trade-offs, we believe such a startup solution is a very useful starting point for most of the solutions, and a useful example for others. + +![microservice-template-diagram](microservice-template-diagram.png) + +*Figure: A simplified overall diagram of the microservice solution.* + +You can [follow the documentation](https://docs.abp.io/en/commercial/4.2/startup-templates/microservice/index) to get started with this startup template. **This template should be considered as an early release**. We will improve it and write a lot of guides. + +If you want to use the ABP Suite to create your solution, then you need to first upgrade it: + +````bash +abp suite update +```` + +If you want, you can directly create a new solution from the command line: + +````bash +abp new Volosoft.MyMicroserviceSystem -t microservice-pro --preview +```` + +Company Name is optional. Solution name could be *MyMicroserviceSystem* for this example. + +### Public Website in the Startup Templates + +As mentioned in the previous release post, we've added a *Public Website* application to the startup templates. It is configured to authenticate through the IdentityServer with a single sign-on system. + +You can use this application to create a landing page for your actual application or a corporate website for your business. An example screenshot: + +![public-website](public-website.jpg) + +It uses the same *Lepton Theme*, so you can apply [all the styles](https://commercial.abp.io/themes). The Public Website has a different layout and also has a different setting for the styling (that can be configured in the *Settings / Lepton Theme* page of the main web application). + +> *Public Website* is optional and you need to select the "Public Website" option while creating a new solution using the ABP Suite, or use the `--with-public-website` option while using the `abp new` CLI command. + +### Easy CRM Blazor UI + +[Easy CRM](https://docs.abp.io/en/commercial/latest/samples/easy-crm) is an example application built with the ABP Commercial. MVC (Razor Pages) and Angular UI implementations were already provided. With the version 4.2, we are providing the Blazor UI implementation for this application. + +![easy-crm](easy-crm.png) + +### Other News + +* Implemented Iyzico as a payment gateway provider for the [payment module](https://commercial.abp.io/modules/Volo.Payment) in addition to Paypal, Stripe, 2Checkout and Payu providers. +* ABP Suite supports the new microservice template creation, public website and DBMS selection options. +* Swagger authorization and other features mentioned in the ABP Framework section are already implemented for the ABP Commercial too. + +## ABP Community News + +### Sharing Video Contents + +[community.abp.io](https://community.abp.io/) is a place to share ABP related contents. It started with publishing articles. Now, it supports to publish video contents. [See this example](https://community.abp.io/articles/be-a-superhero-on-day-1-with-abp.io-wvifcy9s). All you need to do is to create a video and upload to YouTube. Then you can [submit](https://community.abp.io/articles/submit) the YouTube link to the ABP Community website. + +### Multi-language support + +We planned ABP Community to publish English-only contents. However, we see that people want to share contents in other languages too. Now, **it is possible to submit a content in any language**. Just select the Language option while submitting your content. + +**When you submit a non-English content, it is not visible to all the visitors by default**. Visitors can see a non-English content only if their browser language or the selected language matches to the content language (there is a language selection at the end of the website). + +### External Contents + +If you want to publish your content anywhere else, but want to post a link of your content, you can select *External Content* option while submitting the post. For example, [this article](https://community.abp.io/articles/aspnet-boilerplate-to-abp-framework-xml-to-json-localization-conversion-0mxyjrzj) is an external article and also written in Chinese language. + +## About the Next Release + +The next feature version will be 4.3.0. It is planned to release the 4.3 RC (Release Candidate) on March 11 and the final version on March 25, 2021. + +We decided to slow down the feature development for the [next milestone](https://github.com/abpframework/abp/milestone/49). We will continue to improve the existing features and introduce new ones, sure, but wanted to have more time for the planning, documentation, creating guides and improving the development experience. + +## Feedback + +Please check out the ABP Framework 4.2.0 RC and [provide feedback](https://github.com/abpframework/abp/issues/new) to help us to release a more stable version. **The planned release date for the [4.2.0 final](https://github.com/abpframework/abp/milestone/48) version is January 28, 2021**. diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/easy-crm.png b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/easy-crm.png new file mode 100644 index 0000000000..02c47777a4 Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/easy-crm.png differ diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/microservice-template-diagram.png b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/microservice-template-diagram.png new file mode 100644 index 0000000000..2c6df5ab74 Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/microservice-template-diagram.png differ diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/public-website.jpg b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/public-website.jpg new file mode 100644 index 0000000000..b5784519bd Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/public-website.jpg differ diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/swagger-authorize-modal.png b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/swagger-authorize-modal.png new file mode 100644 index 0000000000..52b43f248e Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/swagger-authorize-modal.png differ diff --git a/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/swagger-authorize.png b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/swagger-authorize.png new file mode 100644 index 0000000000..650a212fe5 Binary files /dev/null and b/docs/en/Blog-Posts/2021-01-14 v4_2_Preview/swagger-authorize.png differ diff --git a/docs/en/Blog-Posts/2021-01-28 v4_2_Release_Stable/POST.md b/docs/en/Blog-Posts/2021-01-28 v4_2_Release_Stable/POST.md new file mode 100644 index 0000000000..b5664d8de8 --- /dev/null +++ b/docs/en/Blog-Posts/2021-01-28 v4_2_Release_Stable/POST.md @@ -0,0 +1,53 @@ +# ABP.IO Platform 4.2 Final Has Been Released! + +[ABP Framework](https://abp.io/) and [ABP Commercial](https://commercial.abp.io/) 4.2 versions have been released today. + +## What's New With 4.2? + +Since all the new features are already explained in details with the [4.2 RC Announcement Post](https://blog.abp.io/abp/ABP-IO-Platform-v4-2-RC-Has-Been-Released), I will not repeat all the details again. See the [RC Blog Post](https://blog.abp.io/abp/ABP-IO-Platform-v4-2-RC-Has-Been-Released) for all the features and enhancements. + +## Creating New Solutions + +You can create a new solution with the ABP Framework version 4.2 by either using the `abp new` command or using the **direct download** tab on the [get started page](https://abp.io/get-started). + +> See the [getting started document](https://docs.abp.io/en/abp/latest/Getting-Started) for details. + +## How to Upgrade an Existing Solution + +### Install/Update the ABP CLI + +First of all, install the ABP CLI or upgrade to the latest version. + +If you haven't installed yet: + +```bash +dotnet tool install -g Volo.Abp.Cli +``` + +To update an existing installation: + +```bash +dotnet tool update -g Volo.Abp.Cli +``` + +### ABP UPDATE Command + +[ABP CLI](https://docs.abp.io/en/abp/latest/CLI) provides a handy command to update all the ABP related NuGet and NPM packages in your solution with a single command: + +```bash +abp update +``` + +Run this command in the root folder of your solution. + +## Migration Guide + +Check [the migration guide](https://docs.abp.io/en/abp/4.2/Migration-Guides/Abp-4_2) for the applications with the version 4.x upgrading to the version 4.2. + +> It is strongly recommended to check the migration guide for this version. Especially, the new `IRepository.GetQueryableAsync()` method is a core change should be considered after upgrading the solution. + +## About the Next Version + +The next feature version will be 4.3. It is planned to release the 4.3 RC (Release Candidate) on March 11 and the final version on March 25, 2021. + +We decided to slow down the feature development for the [next milestone](https://github.com/abpframework/abp/milestone/49). We will continue to improve the existing features and introduce new ones, sure, but wanted to have more time for the planning, documentation, creating guides and improving the development experience. \ No newline at end of file diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/POST.md b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/POST.md new file mode 100644 index 0000000000..f30f31d541 --- /dev/null +++ b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/POST.md @@ -0,0 +1,103 @@ +## Using MatBlazor UI Components With the ABP Framework + +Hi, in this step by step article, I will show you how to integrate [MatBlazor](https://www.matblazor.com/), a blazor UI components into ABP Framework-based applications. + +![example-result](example-result.png) + +*(A screenshot from the example application developed in this article)* + +## Create the Project + +> First thing is to create the project. ABP Framework offers startup templates to get into business faster. + +In this article, I will create a new startup template with EF Core as a database provider and Blazor for UI framework. But if you already have a project with Blazor UI, you don't need to create a new startup template, you can directly implement the following steps to your existing project. + +> If you already have a project with the Blazor UI, you can skip this section. + +* Before starting the development, we will create a new solution named `MatBlazorSample` (or whatever you want). We will create a new startup template with EF Core as a database provider and Blazor for UI framework by using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI): + +````bash +abp new MatBlazorSample -u blazor +```` + +This will create new project inside of `aspnet-core`, so: + +````bash +cd aspnet-core +```` + +and + +````bash +dotnet restore +```` + +* Our project boilerplate will be ready after the download is finished. Then, we can open the solution in the Visual Studio (or any other IDE) and run the `MatBlazorSample.DbMigrator` to create the database and seed initial data (which creates the admin user, admin role, permissions etc.) + +![initial-project](initial-project.png) + +* After database and initial data created, +* Run the `MatBlazorSample.HttpApi.Host` to see our server side working and +* Run the `MatBlazorSample.Blazor` to see our UI working properly. + +> _Default login credentials for admin: username is **admin** and password is **1q2w3E\***_ + +## Install MatBlazor + +You can follow [this documentation](https://www.matblazor.com/) to install MatBlazor packages into your computer. + +### Adding MatBlazor NuGet Packages + +```bash +Install-Package MatBlazor +``` + +### Register MatBlazor Resources + +1. Add the following line to the HEAD section of the `wwwroot/index.html` file within the `MatBlazorSample.Blazor` project: + + ```Razor + + + + + + ``` + +2. In the `MatBlazorSampleBlazorModule` class, call the `AddMatBlazor()` method from your project's `ConfigureServices()` method: + + ```csharp + public override void ConfigureServices(ServiceConfigurationContext context) + { + var environment = context.Services.GetSingletonInstance(); + var builder = context.Services.GetSingletonInstance(); + // ... + builder.Services.AddMatBlazor(); + } + ``` + +3. Register the **MatBlazorSample.Blazor** namespace in the `_Imports.razor` file: + + ```Razor + @using MatBlazor + ``` + +## The Sample Application + +We have created a sample application with [Table](https://www.matblazor.com/Table) example. + +### The Source Code + +You can download the source code from [here](https://github.com/abpframework/abp-samples/tree/master/MatBlazorSample). + +The related files for this example are marked in the following screenshots. + +![table-app-contract](table-app-contract.png) + +![table-application](table-application.png) + +![table-web](table-web.png) + +## Conclusion + +In this article, I've explained how to use [MatBlazor](https://www.matblazor.com/) components in your application. ABP Framework is designed so that it can work with any UI library/framework. \ No newline at end of file diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/example-result.png b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/example-result.png new file mode 100644 index 0000000000..89a2fecdce Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/example-result.png differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/initial-project.png b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/initial-project.png new file mode 100644 index 0000000000..85e71d5692 Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/initial-project.png differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-app-contract.png b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-app-contract.png new file mode 100644 index 0000000000..8108cdc606 Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-app-contract.png differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-application.png b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-application.png new file mode 100644 index 0000000000..7f07af97e1 Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-application.png differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-web.png b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-web.png new file mode 100644 index 0000000000..0034aec095 Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Integrate-the-MatBlazor-Blazor-Component/table-web.png differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/POST.md b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/POST.md new file mode 100644 index 0000000000..7a854291d5 --- /dev/null +++ b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/POST.md @@ -0,0 +1,343 @@ +# How to Use PrimeNG Components with the ABP Angular UI + +## Introduction + +In this article, we will use components of the [PrimeNG](https://www.primefaces.org/primeng/) that is a popular UI component library for Angular with the ABP Framework Angular UI that will be generated via [ABP CLI](https://docs.abp.io/en/abp/latest/CLI). + +We will create an organization units page and use PrimeNG's [OrganizationChart](https://primefaces.org/primeng/showcase/#/organizationchart) and [Table](https://primefaces.org/primeng/showcase/#/table) components on the page. + +![Introduction](intro.gif) + +The UI shown above contains many PrimeNG components. You can reach the source code of this rich UI. Take a look at the source code section below. + +> This article does not cover any backend code. I used mock data to provide data source to the components. + +## Pre-Requirements + +The following tools should be installed on your development machine: + +* [.NET Core 5.0+](https://www.microsoft.com/net/download/dotnet-core/) +* [Node v12 or v14](https://nodejs.org/) +* [VS Code](https://code.visualstudio.com/) or another IDE + +## Source Code + +I have prepared a sample project that contains more PrimeNG components than described in this article. You can download the source code [on GitHub](https://github.com/abpframework/abp-samples/tree/master/PrimengSample). + +## Creating a New Solution + +In this step, we will create a new solution that contains Angular UI and backend startup templates. If you have a startup template with Angular UI, you can skip this step. + +Run the following command to install the ABP CLI: + +```bash +dotnet tool install -g Volo.Abp.Cli +``` + +...or update: + +```bash +dotnet tool update -g Volo.Abp.Cli +``` + +Create a new solution named `AbpPrimengSample` by running the following command: + +```bash +abp new AbpPrimengSample -u angular -csf +``` + +See the [ABP CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all available options. + +You can also use the Direct Download tab on the [Get Started](https://abp.io/get-started) page. + +## Running the Solution + +You can run the solution as described in [here](https://docs.abp.io/en/abp/latest/Getting-Started-Running-Solution?UI=NG&DB=EF&Tiered=No). + +## PrimeNG Setup + +Open the `angular` folder and run the following command to install packages: + +```bash +npm install +``` + +Next, we need to install `primeng` and required packages (`primeicons` and `@angular/cdk`) for the library. Run the command below to install these packages: + +```bash +npm install primeng primeicons @angular/cdk --save +``` + +The packages we have installed; + + - `primeng` is the main package that is a component library. + - `primeicons` is an icon font library. Many PrimeNG components use this font internally. + - `@angular/cdk` is a component dev kit created by the Angular team. Some PrimeNG modules depend on it. + +As the last step of the setup, we should add the required style files for the library to `angular.json`: + +```js +//angular.json + +"projects": { + "AbpPrimengSample": { + //... + "styles": { + "node_modules/primeicons/primeicons.css", + "node_modules/primeng/resources/themes/saga-blue/theme.css", + "node_modules/primeng/resources/primeng.min.css", + //...other styles + } + } +} +``` + +We have added the `primeng.min.css`, Saga Blue theme's `theme.css`, and `primeicons.css` files to the project. You can choose another theme instead of the Sage Blue. See available themes on the [Get Started](https://www.primefaces.org/primeng/showcase/#/setup) document of the PrimeNG. + + +> You have to restart the running `ng serve` process to see the effect of the changes you made in the `angular.json`. + + +## Creating the Organization Units Page + +Run the following command to create a new module named `OrganizationUnits`: + +```bash +npm run ng -- generate module organization-units --route organization-units --module app.module +``` + +Then open the `src/route.provider.ts` and add a new route as an array element to add a navigation link labeled "Organization Units" to the menu: + +```js +//route.provider.ts + +import { eThemeSharedRouteNames } from '@abp/ng.theme.shared'; +//... + +routesService.add([ + //... + { + path: '/organization-units', + name: 'Organization Units', + parentName: eThemeSharedRouteNames.Administration, + iconClass: 'fas fa-sitemap', + layout: eLayoutType.application, + }, +]); +``` + +We have created a lazy-loadable module and defined a menu navigation link. We can navigate to the page as shown below: + +![organization units menu navigation item](organization-units-menu-item.jpg) + +## Using the PrimeNG Components + +### Implementing the Organization Chart Component + +When you would like to use any component from PrimeNG, you have to import the component's module to your module. Since we will use the `OrganizationChart` on the organization units page, we need to import `OrganizationChartModule` into `OrganizationUnitsModule`. + +Open the `src/organization-units/organization-units.module.ts` and add the `OrganizationChartModule` to the imports array as shown below: + +```js +import { OrganizationChartModule } from 'primeng/organizationchart'; +//... + +@NgModule({ + //... + imports: [ + //... + OrganizationChartModule + ], +}) +export class OrganizationUnitsModule {} +``` + +> Since NGCC need to work in some cases, restarting the `ng serve` process would be good when you import any modules from `primeng` package to your module. + +Let's define a mock data source for the `OrganizationChartComponent` and add the component to the page. + +Open the `src/organization-units/organization-units.component.ts` and add two variables as shown below: + +```js +//... +import { TreeNode } from 'primeng/api'; + +@Component(/* component metadata*/) +export class OrganizationUnitsComponent implements OnInit { + //... + + organizationUnits: TreeNode[] = [ + { + label: 'Management', + expanded: true, + children: [ + { + label: 'Selling', + expanded: true, + children: [ + { + label: 'Customer Relations', + }, + { + label: 'Marketing', + }, + ], + }, + { + label: 'Supporting', + expanded: true, + children: [ + { + label: 'Buying', + }, + { + label: 'Human Resources', + }, + ], + }, + ], + }, + ]; + + selectedUnit: TreeNode; +``` + +- First variable is `organizationUnits`. It provides mock data source to `OrganizationChartComponent`. +- Second variable is `selectedUnit`. It keeps chosen unit on the chart. + +Then, open the `src/organization-units/organization-units.component.html` and replace the file content with the following: + +```html +
+
+
Organization Units
+
+
+ +
+
+``` + +We have implemented the `OrganizationChart`. The final UI looks like below: + +![organization chart](organization-chart.jpg) + +## Implementing the Table Component + +In order to use the `TableComponent`, we have to import the `TableModule` to the `OrganizationUnitsModule`. + +Open the `organization-units.module.ts` and add `TableModule` to the imports array as shown below: + +```js +import { TableModule } from 'primeng/table'; +//... + +@NgModule({ + //... + imports: [ + //... + TableModule + ], +}) +export class OrganizationUnitsModule {} +``` + +Open the `organization-units.component.ts` and add a variable named `members` with initial value and add a getter named `tableData` as shown below: + +```js +//... +export class OrganizationUnitsComponent implements OnInit { + //... + + members = [ + { + fullName: 'John Doe', + username: 'John.Doe', + phone: '+1-202-555-0125', + email: 'john.doe@example.com', + parent: 'Customer Relations', + }, + { + fullName: 'Darrion Walter', + username: 'Darrion.Walter', + phone: '+1-262-155-0355', + email: 'Darrion_Walter@example.com', + parent: 'Marketing', + }, + { + fullName: 'Rosa Labadie', + username: 'Rosa.Labadie', + phone: '+1-262-723-2255', + email: 'Rosa.Labadie@example.com', + parent: 'Marketing', + }, + { + fullName: 'Adelle Hills', + username: 'Adelle.Hills', + phone: '+1-491-112-9011', + email: 'Adelle.Hills@example.com', + parent: 'Buying', + }, + { + fullName: 'Brian Hane', + username: 'Brian.Hane', + phone: '+1-772-509-1823', + email: 'Brian.Hane@example.com', + parent: 'Human Resources', + }, + ]; + + get tableData() { + return this.members.filter(user => user.parent === this.selectedUnit.label); + } +``` + +What we have done above? + +- We defined a variable named `members` to provide mock data to the table. +- We have defined a getter named `tableData` to provide filtered data source to the table using `members` variable. + +We are now ready to add the table to the HTML template. + +Open the `organization-units.component.html`, find the `p-organizationChart` tag and place the following code to the bottom of this tag: + +```html +
+
Members of {{ selectedUnit.label }}
+ + + + + Name + Username + Email + Phone + + + + + {{ member.fullName }} + {{ member.username }} + {{ member.email }} + {{ member.phone }} + + + +
+``` + +We have added a new `div` that contains the `TableComponent`. The table appears when an organization unit is selected. +The table contains 4 columns which are name, username, email, and phone for displaying the members' information. + +After adding the table, the final UI looks like this: + +![PrimeNG TableComponent](table.gif) + + +## Conclusion + +We have implemented the PrimeNG component library on the ABP Angular UI project and used two components on a page in a short time. You can use any PrimeNG components by following the documentation. The ABP Angular UI will not block you in any case. diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/intro.gif b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/intro.gif new file mode 100644 index 0000000000..5f96a65422 Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/intro.gif differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/organization-chart.jpg b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/organization-chart.jpg new file mode 100644 index 0000000000..9d18f35ba1 Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/organization-chart.jpg differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/organization-units-menu-item.jpg b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/organization-units-menu-item.jpg new file mode 100644 index 0000000000..dc6111f553 Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/organization-units-menu-item.jpg differ diff --git a/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/table.gif b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/table.gif new file mode 100644 index 0000000000..d27dcb26bd Binary files /dev/null and b/docs/en/Community-Articles/2021-01-20-How-to-Use-PrimeNG-Components-with-the-ABP-Angular-UI/table.gif differ diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index 60b6a94823..a12d216b3d 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -254,7 +254,7 @@ public async override Task DeleteAsync( ### Access to the MongoDB API -In most cases, you want to hide MongoDB APIs behind a repository (this is the main purpose of the repository). However, if you want to access the MongoDB API over the repository, you can use `GetDatabaseAsync()` or `GetCollectionAsync()` extension methods. Example: +In most cases, you want to hide MongoDB APIs behind a repository (this is the main purpose of the repository). However, if you want to access the MongoDB API over the repository, you can use `GetDatabaseAsync()`, `GetCollectionAsync()` or `GetAggregateAsync()` extension methods. Example: ```csharp public class BookService @@ -270,6 +270,7 @@ public class BookService { IMongoDatabase database = await _bookRepository.GetDatabaseAsync(); IMongoCollection books = await _bookRepository.GetCollectionAsync(); + IAggregateFluent bookAggregate = await _bookRepository.GetAggregateAsync(); } } ``` diff --git a/docs/en/UI/Angular/Ellipsis-Directive.md b/docs/en/UI/Angular/Ellipsis-Directive.md new file mode 100644 index 0000000000..c462331574 --- /dev/null +++ b/docs/en/UI/Angular/Ellipsis-Directive.md @@ -0,0 +1,83 @@ +# Ellipsis + +Text inside an HTML element can be truncated easily with an ellipsis by using CSS. To make this even easier, you can use the `EllipsisDirective` which has been exposed by the `@abp/ng.theme.shared` package. + + +## Getting Started + +In order to use the `EllipsisDirective` in an HTML template, the **`ThemeSharedModule`** should be imported into your module like this: + +```js +// ... +import { ThemeSharedModule } from '@abp/ng.theme.shared'; + +@NgModule({ + //... + imports: [..., ThemeSharedModule], +}) +export class MyFeatureModule {} +``` + +or **if you would not like to import** the `ThemeSharedModule`, you can import the **`EllipsisModule`** as shown below: + + +```js +// ... +import { EllipsisModule } from '@abp/ng.theme.shared'; + +@NgModule({ + //... + imports: [..., EllipsisModule], +}) +export class MyFeatureModule {} +``` + +## Usage + +The `EllipsisDirective` is very easy to use. The directive's selector is **`abpEllipsis`**. By adding the `abpEllipsis` attribute to an HTML element, you can activate the `EllipsisDirective` for the HTML element. + +See an example usage: + +```html +

+ Lorem ipsum dolor sit, amet consectetur adipisicing elit. Laboriosam commodi quae aspernatur, + corporis velit et suscipit id consequuntur amet minima expedita cum reiciendis dolorum + cupiditate? Voluptas eaque voluptatum odio deleniti quo vel illum nemo accusamus nulla ratione + impedit dolorum expedita necessitatibus fugiat ullam beatae, optio eum cupiditate ducimus + architecto. +

+``` + +The `abpEllipsis` attribute has been added to the `

` element that containing very long text inside to activate the `EllipsisDirective`. + +See the result: + +![Ellipsis directive result](./images/ellipsis-directive-result1.jpg) + +The long text has been truncated by using the directive. + +The UI before using the directive looks like this: + +![Before using the EllipsisDirective](./images/ellipsis-directive-before.jpg) + +### Specifying Max Width of an HTML Element + +An HTML element max width can be specified as shown below: + +```html +

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, optio! +
+ +
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, optio! +
+ +
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque, optio! +
+``` + +See the result: + +![Ellipsis directive result 2](./images/ellipsis-directive-result2.jpg) \ No newline at end of file diff --git a/docs/en/UI/Angular/Modal.md b/docs/en/UI/Angular/Modal.md new file mode 100644 index 0000000000..f1583d4813 --- /dev/null +++ b/docs/en/UI/Angular/Modal.md @@ -0,0 +1,248 @@ +# Modal + +`ModalComponent` is a pre-built component exposed by `@abp/ng.theme.shared` package to show modals. The component uses the [`ng-bootstrap`](https://ng-bootstrap.github.io/)'s modal service inside to render a modal. + +The `abp-modal` provides some additional benefits: + + - It is **flexible**. You can pass header, body, footer templates easily by adding the templates to the `abp-modal` content. It can also be implemented quickly. + - Provides several inputs be able to customize the modal and several outputs be able to listen to some events. + - Automatically detects the close button which has a `#abpClose` template variable and closes the modal when pressed this button. + - Automatically detects the `abp-button` and triggers its loading spinner when the `busy` input value of the modal component is true. + - Automatically checks if the form inside the modal **has changed, but not saved**. It warns the user by displaying a [confirmation popup](Confirmation-Service) in this case when a user tries to close the modal or refresh/close the tab of the browser. + + +> Note: A modal can also be rendered by using the `ng-bootstrap` modal. For further information, see [Modal doc](https://ng-bootstrap.github.io/#/components/modal) on the `ng-bootstrap` documentation. + +## Getting Started + +In order to use the `abp-modal` in an HTML template, the **`ThemeSharedModule`** should be imported into your module like this: + +```js +// ... +import { ThemeSharedModule } from '@abp/ng.theme.shared'; + +@NgModule({ + //... + imports: [..., ThemeSharedModule], +}) +export class MyFeatureModule {} +``` + +## Usage + +You can add the `abp-modal` to your component very quickly. See an example: + +```html + + + + + + +

Modal Title

+
+ + +

Modal content

+
+ + + + +
+``` + +```js +// sample.component.ts + +@Component(/* component metadata */) +export class SampleComponent { + isModelOpen = false +} +``` + +![Example modal result](./images/modal-result-1.jpg) + + +See an example form inside a modal: + +```html + + + + +

Book

+
+ + +
+
+ * + +
+ +
+ * + +
+ +
+ * + +
+ +
+ * + +
+ +
+ * + +
+
+
+ + + + + + +
+``` + +```ts +// book.component.ts + +import { Component } from '@angular/core'; +import { FormBuilder, Validators } from '@angular/forms'; + +@Component(/* component metadata */) +export class BookComponent { + form = this.fb.group({ + author: [null, [Validators.required]], + name: [null, [Validators.required]], + price: [null, [Validators.required, Validators.min(0)]], + type: [null, [Validators.required]], + publishDate: [null, [Validators.required]], + }); + + inProgress: boolean; + + isModalOpen: boolean; + + constructor(private fb: FormBuilder, private service: BookService) {} + + save() { + if (this.form.invalid) return; + + this.inProgress = true; + + this.service.save(this.form.value).subscribe(() => { + this.inProgress = false; + }); + } +} +``` + +The modal with form looks like this: + +![Form example result](./images/modal-result-2.jpg) + +## API + +### Inputs + +#### visible + +```js +@Input() visible: boolean +``` + +**`visible`** is a boolean input that determines whether the modal is open. It is also can be used two-way binding. + +#### busy + +```js +@Input() busy: boolean +``` + +**`busy`** is a boolean input that determines whether the busy status of the modal is true. When `busy` is true, the modal cannot be closed and the `abp-button` loading spinner is triggered. + + +#### options + +```js +@Input() options: NgbModalOptions +``` + +**`options`** is an input typed [NgbModalOptions](https://ng-bootstrap.github.io/#/components/modal/api#NgbModalOptions). It is configuration for the `ng-bootstrap` modal. + +#### suppressUnsavedChangesWarning + +```js +@Input() suppressUnsavedChangesWarning: boolean +``` + +**`suppressUnsavedChangesWarning`** is a boolean input that determines whether the confirmation popup triggering active or not. It can also be set globally as shown below: + +```ts +//app.module.ts + +// app.module.ts + +import { SUPPRESS_UNSAVED_CHANGES_WARNING } from '@abp/ng.theme.shared'; + +// ... + +@NgModule({ + // ... + providers: [{provide: SUPPRESS_UNSAVED_CHANGES_WARNING, useValue: true}] +}) +export class AppModule {} +``` + +Note: The `suppressUnsavedChangesWarning` input of `abp-modal` value overrides the `SUPPRESS_UNSAVED_CHANGES_WARNING` injection token value. + +### Outputs + +#### visibleChange + +```js +@Output() readonly visibleChange = new EventEmitter(); +``` + +**`visibleChange`** is an event emitted when the modal visibility has changed. The event payload is a boolean. + +#### appear + +```js + @Output() readonly appear = new EventEmitter(); +``` + +**`appear`** is an event emitted when the modal has opened. + +#### disappear + +```js + @Output() readonly disappear = new EventEmitter(); +``` + +**`disappear`** is an event emitted when the modal has closed. diff --git a/docs/en/UI/Angular/images/ellipsis-directive-before.jpg b/docs/en/UI/Angular/images/ellipsis-directive-before.jpg new file mode 100644 index 0000000000..dc534d9a97 Binary files /dev/null and b/docs/en/UI/Angular/images/ellipsis-directive-before.jpg differ diff --git a/docs/en/UI/Angular/images/ellipsis-directive-result1.jpg b/docs/en/UI/Angular/images/ellipsis-directive-result1.jpg new file mode 100644 index 0000000000..fe693f4c8c Binary files /dev/null and b/docs/en/UI/Angular/images/ellipsis-directive-result1.jpg differ diff --git a/docs/en/UI/Angular/images/ellipsis-directive-result2.jpg b/docs/en/UI/Angular/images/ellipsis-directive-result2.jpg new file mode 100644 index 0000000000..3a416bbc05 Binary files /dev/null and b/docs/en/UI/Angular/images/ellipsis-directive-result2.jpg differ diff --git a/docs/en/UI/Angular/images/modal-result-1.jpg b/docs/en/UI/Angular/images/modal-result-1.jpg new file mode 100644 index 0000000000..89dbbc0cb1 Binary files /dev/null and b/docs/en/UI/Angular/images/modal-result-1.jpg differ diff --git a/docs/en/UI/Angular/images/modal-result-2.jpg b/docs/en/UI/Angular/images/modal-result-2.jpg new file mode 100644 index 0000000000..e17f2631ab Binary files /dev/null and b/docs/en/UI/Angular/images/modal-result-2.jpg differ diff --git a/docs/en/UI/AspNetCore/Bundling-Minification.md b/docs/en/UI/AspNetCore/Bundling-Minification.md index 9d88a37135..aeaea6c2e6 100644 --- a/docs/en/UI/AspNetCore/Bundling-Minification.md +++ b/docs/en/UI/AspNetCore/Bundling-Minification.md @@ -165,7 +165,27 @@ public class MyWebExtensionModule : AbpModule } ```` -> It's not possible to configure unnamed bundle tag helpers by code, because their name are not known at the development time. It's suggested to always use a name for a bundle tag helper. +You can also use the `ConfigureAll` method to configure all existing bundles: + +````C# +[DependsOn(typeof(MyWebModule))] +public class MyWebExtensionModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options + .ScriptBundles + .ConfigureAll(bundle => { + bundle.AddFiles( + "/scripts/my-extension-script.js" + ); + }); + }); + } +} +```` ## Bundle Contributors diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 3fff6d67c1..f855338d13 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -819,6 +819,10 @@ "text": "Projecting Angular Content", "path": "UI/Angular/Content-Projection-Service.md" }, + { + "text": "Modal", + "path": "UI/Angular/Modal.md" + }, { "text": "Confirmation Popup", "path": "UI/Angular/Confirmation-Service.md" @@ -830,6 +834,10 @@ { "text": "Page Alerts", "path": "UI/Angular/Page-Alerts.md" + }, + { + "text": "Ellipsis", + "path": "UI/Angular/Ellipsis-Directive.md" } ] }, diff --git a/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md b/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md index db434ab365..81755fbf0d 100644 --- a/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md +++ b/docs/zh-Hans/Domain-Driven-Design-Implementation-Guide.md @@ -1767,7 +1767,7 @@ public async Task ChangeTitleAsync(Issue issue, string title) 如前所述,领域驱动设计中的*业务逻辑*分为两部分(各层):领域逻辑和应用逻辑 -![domain-driven-design-domain-vs-application-logic](../../../abpframework.abp/docs/en/images/domain-driven-design-domain-vs-application-logic.png) +![domain-driven-design-domain-vs-application-logic](images/domain-driven-design-domain-vs-application-logic.png) 领域逻辑是系统的*核心领域规则*组成,而应用逻辑则满足特定的*用例*. @@ -1783,7 +1783,7 @@ public async Task ChangeTitleAsync(Issue issue, string title) * 一个**后台管理系统**,UI使用Angular,通过REST API请求数据.内部员工使用这个系统来维护数据(例如,编辑商品说明). * 一个**移动端应用程序**,它比公开的网站UI上更加简洁.它通过REST API或其它技术(例如,TCP sockets)请求数据. -![domain-driven-design-multiple-applications](../../../abpframework.abp/docs/en/images/domain-driven-design-multiple-applications.png) +![domain-driven-design-multiple-applications](images/domain-driven-design-multiple-applications.png) 每个应用程序都有不同的**需求**,不同的**用例**(应用服务方法),不同的DTO,不同的**验证**和**授权**规则等. diff --git a/docs/zh-Hans/MongoDB.md b/docs/zh-Hans/MongoDB.md index 2ceeb1e00c..af63642a41 100644 --- a/docs/zh-Hans/MongoDB.md +++ b/docs/zh-Hans/MongoDB.md @@ -211,7 +211,7 @@ public async override Task DeleteAsync( #### 访问MongoDB API -大多数情况下,你想要将MongoDB API隐藏在仓储后面(这是仓储的主要目的).如果你想在仓储之上访问MongoDB API,你可以使用`GetDatabase()`或`GetCollection()`方法.例如: +大多数情况下,你想要将MongoDB API隐藏在仓储后面(这是仓储的主要目的).如果你想在仓储之上访问MongoDB API,你可以使用`GetDatabaseAsync()`, `GetAggregateAsync()` 或`GetCollectionAsync()`方法.例如: ```csharp public class BookService @@ -223,10 +223,11 @@ public class BookService _bookRepository = bookRepository; } - public void Foo() + public async Task FooAsync() { - IMongoDatabase database = _bookRepository.GetDatabase(); - IMongoCollection books = _bookRepository.GetCollection(); + IMongoDatabase database = await _bookRepository.GetDatabaseAsync(); + IMongoCollection books = await _bookRepository.GetCollectionAsync(); + IAggregateFluent bookAggregate = await _bookRepository.GetAggregateAsync(); } } ``` diff --git a/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md b/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md index 6a229714c1..dd688f256c 100644 --- a/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md +++ b/docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md @@ -167,7 +167,27 @@ public class MyWebExtensionModule : AbpModule } ```` -> 无法通过代码配置未命名的bundle tag helpers, 因为它们的名称在开发时是未知的. 建议始终使用bundle tag helper的名称. +你也可以使用 `ConfigureAll` 方法配置所有现有的捆绑包: + +````C# +[DependsOn(typeof(MyWebModule))] +public class MyWebExtensionModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options + .ScriptBundles + .ConfigureAll(bundle => { + bundle.AddFiles( + "/scripts/my-extension-script.js" + ); + }); + }); + } +} +```` ### Bundle 贡献者 diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationCollection.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationCollection.cs index afc4ec6a4a..acb2ae9fac 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationCollection.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationCollection.cs @@ -9,11 +9,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling { private readonly ConcurrentDictionary _bundles; private readonly ConcurrentDictionary>> _lazyBundleConfigurationActions; + private readonly List> _lazyAllBundleConfigurationActions; public BundleConfigurationCollection() { _bundles = new ConcurrentDictionary(); _lazyBundleConfigurationActions = new ConcurrentDictionary>>(); + _lazyAllBundleConfigurationActions = new List>(); } /// @@ -90,6 +92,12 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling } } + lock (_lazyAllBundleConfigurationActions) + { + _lazyAllBundleConfigurationActions.ForEach(c => c.Invoke(bundle)); + } + + return bundle; } @@ -123,6 +131,29 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling return this; } + /// + /// Configures all bundles. + /// This method also works for lazy bundles (those are created using razor tag helpers). + /// + /// Configure action + /// Returns this object for chained calls. + public BundleConfigurationCollection ConfigureAll([NotNull] Action configureAction) + { + Check.NotNull(configureAction, nameof(configureAction)); + + foreach (var bundle in _bundles) + { + configureAction.Invoke(bundle.Value); + } + + lock (_lazyAllBundleConfigurationActions) + { + _lazyAllBundleConfigurationActions.Add(configureAction); + } + + return this; + } + /// /// Gets a bundle. /// @@ -140,4 +171,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling return bundle; } } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs index 449747d0cc..a2d02bb3fe 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs @@ -27,6 +27,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + [Obsolete("Use LazyServiceProvider instead.")] public IServiceProvider ServiceProvider { get; set; } protected IClock Clock => LazyServiceProvider.LazyGetRequiredService(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs index 52f1d251cb..85822414cf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs @@ -25,6 +25,7 @@ namespace Volo.Abp.AspNetCore.Mvc { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + [Obsolete("Use LazyServiceProvider instead.")] public IServiceProvider ServiceProvider { get; set; } protected IUnitOfWorkManager UnitOfWorkManager => LazyServiceProvider.LazyGetRequiredService(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 8cbba0fc46..873c532ba3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Auditing; -using Volo.Abp.AspNetCore.Mvc.Content; +using Volo.Abp.AspNetCore.Mvc.ContentFormatters; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; using Volo.Abp.AspNetCore.Mvc.Features; diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpViewComponent.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpViewComponent.cs index 2ac02b8ac5..215f31b855 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpViewComponent.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpViewComponent.cs @@ -10,6 +10,7 @@ namespace Volo.Abp.AspNetCore.Mvc { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + [Obsolete("Use LazyServiceProvider instead.")] public IServiceProvider ServiceProvider { get; set; } protected Type ObjectMapperContext { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs index 5cd1333f34..135b506e69 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs @@ -25,17 +25,20 @@ namespace Volo.Abp.AspNetCore.Mvc { public ILogger Logger { get; set; } + private readonly AspNetCoreApiDescriptionModelProviderOptions _options; private readonly IApiDescriptionGroupCollectionProvider _descriptionProvider; - private readonly AbpAspNetCoreMvcOptions _options; + private readonly AbpAspNetCoreMvcOptions _abpAspNetCoreMvcOptions; private readonly AbpApiDescriptionModelOptions _modelOptions; public AspNetCoreApiDescriptionModelProvider( + IOptions options, IApiDescriptionGroupCollectionProvider descriptionProvider, - IOptions options, + IOptions abpAspNetCoreMvcOptions, IOptions modelOptions) { - _descriptionProvider = descriptionProvider; _options = options.Value; + _descriptionProvider = descriptionProvider; + _abpAspNetCoreMvcOptions = abpAspNetCoreMvcOptions.Value; _modelOptions = modelOptions.Value; Logger = NullLogger.Instance; @@ -82,14 +85,14 @@ namespace Volo.Abp.AspNetCore.Mvc var controllerModel = moduleModel.GetOrAddController( controllerType.FullName, - CalculateControllerName(controllerType, setting), + _options.ControllerNameGenerator(controllerType, setting), controllerType, _modelOptions.IgnoredInterfaces ); var method = apiDescription.ActionDescriptor.GetMethodInfo(); - var uniqueMethodName = GetUniqueActionName(method); + var uniqueMethodName = _options.ActionNameGenerator(method); if (controllerModel.Actions.ContainsKey(uniqueMethodName)) { Logger.LogWarning( @@ -119,44 +122,6 @@ namespace Volo.Abp.AspNetCore.Mvc AddParameterDescriptionsToModel(actionModel, method, apiDescription); } - private static string CalculateControllerName(Type controllerType, ConventionalControllerSetting setting) - { - var controllerName = controllerType.Name.RemovePostFix("Controller") - .RemovePostFix(ApplicationService.CommonPostfixes); - - if (setting?.UrlControllerNameNormalizer != null) - { - controllerName = - setting.UrlControllerNameNormalizer( - new UrlControllerNameNormalizerContext(setting.RootPath, controllerName)); - } - - return controllerName; - } - - private static string GetUniqueActionName(MethodInfo method) - { - var methodNameBuilder = new StringBuilder(method.Name); - - var parameters = method.GetParameters(); - if (parameters.Any()) - { - methodNameBuilder.Append("By"); - - for (var i = 0; i < parameters.Length; i++) - { - if (i > 0) - { - methodNameBuilder.Append("And"); - } - - methodNameBuilder.Append(parameters[i].Name.ToPascalCase()); - } - } - - return methodNameBuilder.ToString(); - } - private static List GetSupportedVersions(Type controllerType, MethodInfo method, ConventionalControllerSetting setting) { @@ -377,7 +342,7 @@ namespace Volo.Abp.AspNetCore.Mvc [CanBeNull] private ConventionalControllerSetting FindSetting(Type controllerType) { - foreach (var controllerSetting in _options.ConventionalControllers.ConventionalControllerSettings) + foreach (var controllerSetting in _abpAspNetCoreMvcOptions.ConventionalControllers.ConventionalControllerSettings) { if (controllerSetting.ControllerTypes.Contains(controllerType)) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProviderOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProviderOptions.cs new file mode 100644 index 0000000000..4cd607e07a --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProviderOptions.cs @@ -0,0 +1,57 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Text; +using Volo.Abp.Application.Services; +using Volo.Abp.AspNetCore.Mvc.Conventions; + +namespace Volo.Abp.AspNetCore.Mvc +{ + public class AspNetCoreApiDescriptionModelProviderOptions + { + public Func ControllerNameGenerator { get; set; } + + public Func ActionNameGenerator { get; set; } + + public AspNetCoreApiDescriptionModelProviderOptions() + { + ControllerNameGenerator = (controllerType, setting) => + { + var controllerName = controllerType.Name.RemovePostFix("Controller") + .RemovePostFix(ApplicationService.CommonPostfixes); + + if (setting?.UrlControllerNameNormalizer != null) + { + controllerName = + setting.UrlControllerNameNormalizer( + new UrlControllerNameNormalizerContext(setting.RootPath, controllerName)); + } + + return controllerName; + }; + + ActionNameGenerator = (method) => + { + var methodNameBuilder = new StringBuilder(method.Name); + + var parameters = method.GetParameters(); + if (parameters.Any()) + { + methodNameBuilder.Append("By"); + + for (var i = 0; i < parameters.Length; i++) + { + if (i > 0) + { + methodNameBuilder.Append("And"); + } + + methodNameBuilder.Append(parameters[i].Name.ToPascalCase()); + } + } + + return methodNameBuilder.ToString(); + }; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/InternalRemoteStreamContent.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/InternalRemoteStreamContent.cs deleted file mode 100644 index aaaa849f4f..0000000000 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/InternalRemoteStreamContent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.IO; -using Microsoft.AspNetCore.Http; -using Volo.Abp.Content; - -namespace Volo.Abp.AspNetCore.Mvc.Content -{ - internal class InternalRemoteStreamContent : IRemoteStreamContent - { - private readonly HttpContext _httpContext; - - public InternalRemoteStreamContent(HttpContext httpContext) - { - _httpContext = httpContext; - } - - public string ContentType => _httpContext.Request.ContentType; - - public long? ContentLength => _httpContext.Request.ContentLength; - - public Stream GetStream() - { - return _httpContext.Request.Body; - } - } -} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/RemoteStreamContentInputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentInputFormatter.cs similarity index 70% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/RemoteStreamContentInputFormatter.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentInputFormatter.cs index a4f5668b7d..ba07e28f77 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/RemoteStreamContentInputFormatter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentInputFormatter.cs @@ -1,10 +1,10 @@ using System; using System.Threading.Tasks; -using Microsoft.Net.Http.Headers; using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.Net.Http.Headers; using Volo.Abp.Content; -namespace Volo.Abp.AspNetCore.Mvc.Content +namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { public class RemoteStreamContentInputFormatter : InputFormatter { @@ -20,9 +20,10 @@ namespace Volo.Abp.AspNetCore.Mvc.Content public override Task ReadRequestBodyAsync(InputFormatterContext context) { - return InputFormatterResult.SuccessAsync( - new InternalRemoteStreamContent(context.HttpContext) - ); + return InputFormatterResult.SuccessAsync(new RemoteStreamContent(context.HttpContext.Request.Body) + { + ContentType = context.HttpContext.Request.ContentType + }); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/RemoteStreamContentOutputFormatter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs similarity index 65% rename from framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/RemoteStreamContentOutputFormatter.cs rename to framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs index 706e514254..685822a4b6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Content/RemoteStreamContentOutputFormatter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentOutputFormatter.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Net.Http.Headers; using Volo.Abp.Content; -namespace Volo.Abp.AspNetCore.Mvc.Content +namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters { public class RemoteStreamContentOutputFormatter : OutputFormatter { @@ -21,9 +21,16 @@ namespace Volo.Abp.AspNetCore.Mvc.Content public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context) { var remoteStream = (IRemoteStreamContent)context.Object; - using (var stream = remoteStream.GetStream()) + + if (remoteStream != null) { - await stream.CopyToAsync(context.HttpContext.Response.Body); + context.HttpContext.Response.ContentType = remoteStream.ContentType; + + using (var stream = remoteStream.GetStream()) + { + stream.Position = 0; + await stream.CopyToAsync(context.HttpContext.Response.Body); + } } } } diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHub.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHub.cs index a9d9316f41..616cbd3706 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHub.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHub.cs @@ -17,6 +17,7 @@ namespace Volo.Abp.AspNetCore.SignalR { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + [Obsolete("Use LazyServiceProvider instead.")] public IServiceProvider ServiceProvider { get; set; } protected ILoggerFactory LoggerFactory => LazyServiceProvider.LazyGetService(); diff --git a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs index 4a4d4df722..99d0f2a4c3 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs @@ -269,7 +269,7 @@ namespace Volo.Abp.BlazoriseUI await GetEntitiesAsync(); - StateHasChanged(); + await InvokeAsync(StateHasChanged); } protected virtual async Task OnDataGridReadAsync(DataGridReadDataEventArgs e) @@ -282,7 +282,7 @@ namespace Volo.Abp.BlazoriseUI await GetEntitiesAsync(); - StateHasChanged(); + await InvokeAsync(StateHasChanged); } protected virtual async Task OpenCreateModalAsync() @@ -295,7 +295,7 @@ namespace Volo.Abp.BlazoriseUI // Mapper will not notify Blazor that binded values are changed // so we need to notify it manually by calling StateHasChanged - await InvokeAsync(() => StateHasChanged()); + await InvokeAsync(StateHasChanged); CreateModal.Show(); } @@ -317,7 +317,7 @@ namespace Volo.Abp.BlazoriseUI EditingEntityId = entity.Id; EditingEntity = MapToEditingEntity(entityDto); - await InvokeAsync(() => StateHasChanged()); + await InvokeAsync(StateHasChanged); EditModal.Show(); } @@ -362,11 +362,8 @@ namespace Volo.Abp.BlazoriseUI await CheckCreatePolicyAsync(); var createInput = MapToCreateInput(NewEntity); await AppService.CreateAsync(createInput); - await GetEntitiesAsync(); await OnCreatedEntityAsync(); - - CreateModal.Hide(); } } @@ -375,9 +372,11 @@ namespace Volo.Abp.BlazoriseUI return Task.CompletedTask; } - protected virtual Task OnCreatedEntityAsync() + protected virtual async Task OnCreatedEntityAsync() { - return Task.CompletedTask; + await GetEntitiesAsync(); + + CreateModal.Hide(); } protected virtual async Task UpdateEntityAsync() @@ -389,11 +388,8 @@ namespace Volo.Abp.BlazoriseUI await CheckUpdatePolicyAsync(); var updateInput = MapToUpdateInput(EditingEntity); await AppService.UpdateAsync(EditingEntityId, updateInput); - await GetEntitiesAsync(); await OnUpdatedEntityAsync(); - - EditModal.Hide(); } } @@ -402,9 +398,11 @@ namespace Volo.Abp.BlazoriseUI return Task.CompletedTask; } - protected virtual Task OnUpdatedEntityAsync() + protected virtual async Task OnUpdatedEntityAsync() { - return Task.CompletedTask; + await GetEntitiesAsync(); + + EditModal.Hide(); } protected virtual async Task DeleteEntityAsync(TListViewModel entity) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs index 9125a0c8ab..f3cb291f56 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs @@ -22,7 +22,7 @@ namespace Volo.Abp.Cli.Auth { var configuration = new IdentityClientConfiguration( CliUrls.AccountAbpIo, - "role email abpio abpio_www abpio_commercial offline_access", + "role email abpio abpio_www abpio_commercial offline_access", "abp-cli", "1q2w3e*", OidcConstants.GrantTypes.Password, @@ -43,6 +43,7 @@ namespace Volo.Abp.Cli.Auth public Task LogoutAsync() { FileHelper.DeleteIfExists(CliPaths.AccessToken); + FileHelper.DeleteIfExists(CliPaths.Lic); return Task.CompletedTask; } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs index 2b422446ca..068911ac8c 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliPaths.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Text; namespace Volo.Abp.Cli { @@ -10,7 +11,8 @@ namespace Volo.Abp.Cli public static string Root => Path.Combine(AbpRootPath, "cli"); public static string AccessToken => Path.Combine(AbpRootPath, "cli", "access-token.bin"); public static string Build => Path.Combine(AbpRootPath, "build"); - + public static string Lic => Path.Combine(Path.GetTempPath(), Encoding.ASCII.GetString(new byte[] { 65, 98, 112, 76, 105, 99, 101, 110, 115, 101, 46, 98, 105, 110 })); + private static readonly string AbpRootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".abp"); } -} +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs index 90f0af8b7e..c82c434916 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs @@ -12,6 +12,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Auth; +using Volo.Abp.Cli.Commands.Services; using Volo.Abp.Cli.Http; using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.ProjectBuilding.Building; @@ -32,14 +33,17 @@ namespace Volo.Abp.Cli.Commands protected TemplateProjectBuilder TemplateProjectBuilder { get; } public ITemplateInfoProvider TemplateInfoProvider { get; } + public ConnectionStringProvider ConnectionStringProvider { get; } public NewCommand(TemplateProjectBuilder templateProjectBuilder , ITemplateInfoProvider templateInfoProvider, - EfCoreMigrationManager efCoreMigrationManager) + EfCoreMigrationManager efCoreMigrationManager, + ConnectionStringProvider connectionStringProvider) { _efCoreMigrationManager = efCoreMigrationManager; TemplateProjectBuilder = templateProjectBuilder; TemplateInfoProvider = templateInfoProvider; + ConnectionStringProvider = connectionStringProvider; Logger = NullLogger.Instance; } @@ -165,7 +169,7 @@ namespace Volo.Abp.Cli.Commands databaseManagementSystem != DatabaseManagementSystem.NotSpecified && databaseManagementSystem != DatabaseManagementSystem.SQLServer) { - connectionString = GetNewConnectionStringByDbms(databaseManagementSystem, outputFolder); + connectionString = ConnectionStringProvider.GetByDbms(databaseManagementSystem, outputFolder); } commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command); @@ -241,24 +245,6 @@ namespace Volo.Abp.Cli.Commands } } - private string GetNewConnectionStringByDbms(DatabaseManagementSystem databaseManagementSystem, string outputFolder) - { - switch (databaseManagementSystem) - { - case DatabaseManagementSystem.MySQL: - return "Server=localhost;Port=3306;Database=MyProjectName;Uid=root;Pwd=myPassword;"; - case DatabaseManagementSystem.PostgreSQL: - return "User ID=root;Password=myPassword;Host=localhost;Port=5432;Database=MyProjectName;Pooling=true;Min Pool Size=0;Max Pool Size=100;Connection Lifetime=0;"; - //case DatabaseManagementSystem.Oracle: - case DatabaseManagementSystem.OracleDevart: - return "Data Source=MyProjectName;Integrated Security=yes;"; - case DatabaseManagementSystem.SQLite: - return $"Data Source={Path.Combine(outputFolder , "MyProjectName.db")};".Replace("\\", "\\\\"); - default: - return null; - } - } - private void OpenThanksPage(UiFramework uiFramework, DatabaseProvider databaseProvider, bool tiered, bool commercial) { uiFramework = uiFramework == UiFramework.NotSpecified || uiFramework == UiFramework.None ? UiFramework.Mvc : uiFramework; diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/ConnectionStringProvider.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/ConnectionStringProvider.cs new file mode 100644 index 0000000000..32e09b3339 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/ConnectionStringProvider.cs @@ -0,0 +1,30 @@ +using System.IO; +using Volo.Abp.Cli.ProjectBuilding.Building; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.Commands.Services +{ + public class ConnectionStringProvider : ITransientDependency + { + public string GetByDbms(DatabaseManagementSystem databaseManagementSystem, string outputFolder = "") + { + switch (databaseManagementSystem) + { + case DatabaseManagementSystem.NotSpecified: + case DatabaseManagementSystem.SQLServer: + return "Server=localhost;Database=MyProjectName;Trusted_Connection=True"; + case DatabaseManagementSystem.MySQL: + return "Server=localhost;Port=3306;Database=MyProjectName;Uid=root;Pwd=myPassword;"; + case DatabaseManagementSystem.PostgreSQL: + return "User ID=root;Password=myPassword;Host=localhost;Port=5432;Database=MyProjectName;Pooling=true;Min Pool Size=0;Max Pool Size=100;Connection Lifetime=0;"; + //case DatabaseManagementSystem.Oracle: + case DatabaseManagementSystem.OracleDevart: + return "Data Source=MyProjectName;Integrated Security=yes;"; + case DatabaseManagementSystem.SQLite: + return $"Data Source={Path.Combine(outputFolder , "MyProjectName.db")};".Replace("\\", "\\\\"); + default: + return null; + } + } + } +} diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs index 232afb4280..4ac41663e4 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs @@ -35,6 +35,7 @@ namespace Volo.Abp.Application.Services { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + [Obsolete("Use LazyServiceProvider instead.")] public IServiceProvider ServiceProvider { get; set; } public static string[] CommonPostfixes { get; set; } = { "AppService", "ApplicationService", "Service" }; @@ -51,7 +52,7 @@ namespace Volo.Abp.Application.Services ? provider.GetRequiredService() : (IObjectMapper) provider.GetRequiredService(typeof(IObjectMapper<>).MakeGenericType(ObjectMapperContext))); - public IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetService(SimpleGuidGenerator.Instance); + protected IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetService(SimpleGuidGenerator.Instance); protected ILoggerFactory LoggerFactory => LazyServiceProvider.LazyGetRequiredService(); diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs index 751b938f1d..39938b081a 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs @@ -13,11 +13,12 @@ namespace Volo.Abp.Domain.Services { public IAbpLazyServiceProvider LazyServiceProvider { get; set; } + [Obsolete("Use LazyServiceProvider instead.")] public IServiceProvider ServiceProvider { get; set; } protected IClock Clock => LazyServiceProvider.LazyGetRequiredService(); - public IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetService(SimpleGuidGenerator.Instance); + protected IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetService(SimpleGuidGenerator.Instance); protected ILoggerFactory LoggerFactory => LazyServiceProvider.LazyGetRequiredService(); diff --git a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj index 8616838cc0..e8c90ccf13 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj +++ b/framework/src/Volo.Abp.EntityFrameworkCore.PostgreSql/Volo.Abp.EntityFrameworkCore.PostgreSql.csproj @@ -19,7 +19,7 @@ - + diff --git a/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs b/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs index eb8c4eb804..6c0c8ef193 100644 --- a/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs +++ b/framework/src/Volo.Abp.ExceptionHandling/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs @@ -141,7 +141,7 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling { foreach (var key in exception.Data.Keys) { - localizedValue = localizedValue.Replace("{" + key + "}", exception.Data[key].ToString()); + localizedValue = localizedValue.Replace("{" + key + "}", exception.Data[key]?.ToString()); } } diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/Content/ReferencedRemoteStreamContent.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/Content/ReferencedRemoteStreamContent.cs deleted file mode 100644 index b99cec8a51..0000000000 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/Content/ReferencedRemoteStreamContent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.IO; -using Volo.Abp.Content; - -namespace Volo.Abp.Http.Client.Content -{ - internal class ReferencedRemoteStreamContent : RemoteStreamContent - { - private readonly object[] _references; - - public ReferencedRemoteStreamContent(Stream stream, params object[] references) - : base(stream) - { - this._references = references; - } - } -} diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs index 7eb4e5ad78..dd4a681ba5 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs @@ -14,7 +14,6 @@ using Volo.Abp.Content; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; using Volo.Abp.Http.Client.Authentication; -using Volo.Abp.Http.Client.Content; using Volo.Abp.Http.Modeling; using Volo.Abp.Http.ProxyScripting.Generators; using Volo.Abp.Json; @@ -112,7 +111,10 @@ namespace Volo.Abp.Http.Client.DynamicProxying /* returning a class that holds a reference to response * content just to be sure that GC does not dispose of * it before we finish doing our work with the stream */ - return (T)((object)new ReferencedRemoteStreamContent(await responseContent.ReadAsStreamAsync(), responseContent)); + return (T)(object)new RemoteStreamContent(await responseContent.ReadAsStreamAsync()) + { + ContentType = responseContent.Headers.ContentType?.ToString() + }; } var stringContent = await responseContent.ReadAsStringAsync(); diff --git a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs index e4b82b21e1..5bee246bd0 100644 --- a/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs +++ b/framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/UrlBuilder.cs @@ -8,7 +8,6 @@ using JetBrains.Annotations; using Volo.Abp.Http.Modeling; using Volo.Abp.Http.ProxyScripting.Generators; using Volo.Abp.Localization; -using Volo.Abp.Reflection; namespace Volo.Abp.Http.Client.DynamicProxying { @@ -82,9 +81,10 @@ namespace Volo.Abp.Http.Client.DynamicProxying continue; } - AddQueryStringParameter(urlBuilder, isFirstParam, queryStringParameter.Name, value); - - isFirstParam = false; + if (AddQueryStringParameter(urlBuilder, isFirstParam, queryStringParameter.Name, value)) + { + isFirstParam = false; + } } if (apiVersion.ShouldSendInQueryString()) @@ -93,28 +93,37 @@ namespace Volo.Abp.Http.Client.DynamicProxying } } - private static void AddQueryStringParameter( + private static bool AddQueryStringParameter( StringBuilder urlBuilder, bool isFirstParam, string name, [NotNull] object value) { - urlBuilder.Append(isFirstParam ? "?" : "&"); - if (value.GetType().IsArray || (value.GetType().IsGenericType && value is IEnumerable)) { var index = 0; foreach (var item in (IEnumerable) value) { + if (index == 0) + { + urlBuilder.Append(isFirstParam ? "?" : "&"); + } urlBuilder.Append(name + $"[{index++}]=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(item)) + "&"); } - //remove & at the end of the urlBuilder. - urlBuilder.Remove(urlBuilder.Length - 1, 1); - } - else - { - urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(value))); + + if (index > 0) + { + //remove & at the end of the urlBuilder. + urlBuilder.Remove(urlBuilder.Length - 1, 1); + return true; + } + + return false; } + + urlBuilder.Append(isFirstParam ? "?" : "&"); + urlBuilder.Append(name + "=" + System.Net.WebUtility.UrlEncode(ConvertValueToString(value))); + return true; } private static string ConvertValueToString([NotNull] object value) diff --git a/framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumer.cs b/framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumer.cs index 8ae81a2a26..bb7e2d66a3 100644 --- a/framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumer.cs +++ b/framework/src/Volo.Abp.Kafka/Volo/Abp/Kafka/KafkaMessageConsumer.cs @@ -86,7 +86,7 @@ namespace Volo.Abp.Kafka } catch (CreateTopicsException e) { - if (!e.Error.Reason.Contains($"Topic '{TopicName}' already exists")) + if(e.Results.First().Error.Code != ErrorCode.TopicAlreadyExists) { throw; } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs index 960222679f..12e5987777 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs @@ -24,6 +24,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB IMongoQueryable GetMongoQueryable(); Task> GetMongoQueryableAsync(CancellationToken cancellationToken = default); + + Task> GetAggregateAsync(CancellationToken cancellationToken = default); } public interface IMongoDbRepository : IMongoDbRepository, IRepository diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index b252c48999..6894dc90a2 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -16,6 +16,7 @@ using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Guids; using Volo.Abp.MongoDB; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Domain.Repositories.MongoDB { @@ -314,16 +315,26 @@ namespace Volo.Abp.Domain.Repositories.MongoDB } public override async Task DeleteManyAsync( - IEnumerable entities, - bool autoSave = false, - CancellationToken cancellationToken = default) + IEnumerable entities, + bool autoSave = false, + CancellationToken cancellationToken = default) { - var entityArray = entities.ToArray(); + var softDeletedEntities = new List(); + var hardDeletedEntities = new List(); - foreach (var entity in entityArray) + foreach (var entity in entities) { await ApplyAbpConceptsForDeletedEntityAsync(entity); SetNewConcurrencyStamp(entity); + + if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && !IsHardDeleted(entity)) + { + softDeletedEntities.Add(entity); + } + else + { + hardDeletedEntities.Add(entity); + } } var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken)); @@ -331,54 +342,57 @@ namespace Volo.Abp.Domain.Repositories.MongoDB if (BulkOperationProvider != null) { - await BulkOperationProvider.DeleteManyAsync(this, entityArray, dbContext.SessionHandle, autoSave, cancellationToken); + await BulkOperationProvider.DeleteManyAsync(this, entities.ToArray(), dbContext.SessionHandle, autoSave, cancellationToken); return; } - var entitiesCount = entityArray.Count(); - - if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) + if (softDeletedEntities.Count > 0) { UpdateResult updateResult; + var softDeleteEntitiesCount = softDeletedEntities.Count; + if (dbContext.SessionHandle != null) { updateResult = await collection.UpdateManyAsync( dbContext.SessionHandle, - CreateEntitiesFilter(entityArray), + CreateEntitiesFilter(softDeletedEntities), Builders.Update.Set(x => ((ISoftDelete)x).IsDeleted, true) ); } else { updateResult = await collection.UpdateManyAsync( - CreateEntitiesFilter(entityArray), + CreateEntitiesFilter(softDeletedEntities), Builders.Update.Set(x => ((ISoftDelete)x).IsDeleted, true) ); } - if (updateResult.MatchedCount < entitiesCount) + if (updateResult.MatchedCount < softDeleteEntitiesCount) { ThrowOptimisticConcurrencyException(); } } - else + + if (hardDeletedEntities.Count > 0) { DeleteResult deleteResult; + var hardDeletedEntitiesCount = hardDeletedEntities.Count; + if (dbContext.SessionHandle != null) { deleteResult = await collection.DeleteManyAsync( dbContext.SessionHandle, - CreateEntitiesFilter(entityArray) + CreateEntitiesFilter(hardDeletedEntities) ); } else { deleteResult = await collection.DeleteManyAsync( - CreateEntitiesFilter(entityArray) + CreateEntitiesFilter(hardDeletedEntities) ); } - if (deleteResult.DeletedCount < entitiesCount) + if (deleteResult.DeletedCount < hardDeletedEntitiesCount) { ThrowOptimisticConcurrencyException(); } @@ -473,6 +487,17 @@ namespace Volo.Abp.Domain.Repositories.MongoDB ); } + public async Task> GetAggregateAsync(CancellationToken cancellationToken = default) + { + var dbContext = await GetDbContextAsync(cancellationToken); + var collection = await GetCollectionAsync(cancellationToken); + + return ApplyDataFilters( + dbContext.SessionHandle != null + ? collection.Aggregate(dbContext.SessionHandle) + : collection.Aggregate()); + } + protected virtual bool IsHardDeleted(TEntity entity) { var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet; @@ -621,6 +646,22 @@ namespace Volo.Abp.Domain.Repositories.MongoDB throw new AbpDbConcurrencyException("Database operation expected to affect 1 row but actually affected 0 row. Data may have been modified or deleted since entities were loaded. This exception has been thrown on optimistic concurrency check."); } + protected virtual IAggregateFluent ApplyDataFilters(IAggregateFluent aggregate) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) + { + aggregate = aggregate.Match(e => ((ISoftDelete)e).IsDeleted == false); + } + + if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) + { + var tenantId = CurrentTenant.Id; + aggregate = aggregate.Match(e => ((IMultiTenant)e).TenantId == tenantId); + } + + return aggregate; + } + [Obsolete("This method will be removed in future versions.")] public QueryableExecutionModel GetExecutionModel() { diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs index 90605f416e..bcbdc00d9e 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDbCoreRepositoryExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -16,10 +17,10 @@ namespace Volo.Abp.Domain.Repositories return repository.ToMongoDbRepository().Database; } - public static Task GetDatabaseAsync(this IBasicRepository repository) + public static Task GetDatabaseAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { - return repository.ToMongoDbRepository().GetDatabaseAsync(); + return repository.ToMongoDbRepository().GetDatabaseAsync(cancellationToken); } [Obsolete("Use GetCollectionAsync method.")] @@ -29,10 +30,10 @@ namespace Volo.Abp.Domain.Repositories return repository.ToMongoDbRepository().Collection; } - public static Task> GetCollectionAsync(this IBasicRepository repository) + public static Task> GetCollectionAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { - return repository.ToMongoDbRepository().GetCollectionAsync(); + return repository.ToMongoDbRepository().GetCollectionAsync(cancellationToken); } [Obsolete("Use GetMongoQueryableAsync method.")] @@ -42,10 +43,16 @@ namespace Volo.Abp.Domain.Repositories return repository.ToMongoDbRepository().GetMongoQueryable(); } - public static Task> GetMongoQueryableAsync(this IBasicRepository repository) + public static Task> GetMongoQueryableAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) where TEntity : class, IEntity { - return repository.ToMongoDbRepository().GetMongoQueryableAsync(); + return repository.ToMongoDbRepository().GetMongoQueryableAsync(cancellationToken); + } + + public static Task> GetAggregateAsync(this IBasicRepository repository, CancellationToken cancellationToken = default) + where TEntity : class, IEntity + { + return repository.ToMongoDbRepository().GetAggregateAsync(cancellationToken); } public static IMongoDbRepository ToMongoDbRepository(this IBasicRepository repository) diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationHelper.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationHelper.cs index d46281d2e9..9f6cec23e5 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationHelper.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationHelper.cs @@ -4,7 +4,9 @@ namespace Volo.Abp.Validation { public class ValidationHelper { - private const string EmailRegEx = @"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?"; + // Taken from W3C as an alternative to the RFC5322 specification: https://html.spec.whatwg.org/#valid-e-mail-address + // The RFC5322 regex can be found here: https://emailregex.com/ + public static string EmailRegEx { get; set; } = @"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"; public static bool IsValidEmailAddress(string email) { @@ -13,7 +15,6 @@ namespace Volo.Abp.Validation return false; } - /*RFC 2822 (simplified)*/ return Regex.IsMatch(email, EmailRegEx, RegexOptions.Compiled | RegexOptions.IgnoreCase); } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs new file mode 100644 index 0000000000..15491f9b21 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController.cs @@ -0,0 +1,36 @@ +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Shouldly; +using Volo.Abp.Content; + +namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters +{ + [Route("api/remote-stream-content-test")] + public class RemoteStreamContentTestController : AbpController + { + [HttpGet] + [Route("Download")] + public async Task DownloadAsync() + { + var memoryStream = new MemoryStream(); + await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("DownloadAsync")); + + return new RemoteStreamContent(memoryStream) + { + ContentType = "application/rtf" + }; + } + + [HttpPost] + [Route("Upload")] + public async Task UploadAsync([FromBody]IRemoteStreamContent streamContent) + { + using (var reader = new StreamReader(streamContent.GetStream())) + { + return await reader.ReadToEndAsync() + ":" + streamContent.ContentType; + } + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs new file mode 100644 index 0000000000..3d49a5b3ff --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ContentFormatters/RemoteStreamContentTestController_Tests.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.ContentFormatters +{ + public class RemoteStreamContentTestController_Tests : AspNetCoreMvcTestBase + { + [Fact] + public async Task DownloadAsync() + { + var result = await GetResponseAsync("/api/remote-stream-content-test/download"); + result.Content.Headers.ContentType?.ToString().ShouldBe("application/rtf"); + (await result.Content.ReadAsStringAsync()).ShouldBe("DownloadAsync"); + } + + [Fact] + public async Task UploadAsync() + { + using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/remote-stream-content-test/upload")) + { + var memoryStream = new MemoryStream(); + await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("UploadAsync")); + memoryStream.Position = 0; + requestMessage.Content = new StreamContent(memoryStream); + requestMessage.Content.Headers.Add("Content-Type", "application/rtf"); + + var response = await Client.SendAsync(requestMessage); + + (await response.Content.ReadAsStringAsync()).ShouldBe("UploadAsync:application/rtf"); + } + } + } +} diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs index e53e3cabbf..c2fc5ea4b3 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/PersonAppServiceClientProxy_Tests.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using NSubstitute.Extensions; using Shouldly; using Volo.Abp.Application.Dtos; +using Volo.Abp.Content; using Volo.Abp.Domain.Repositories; using Volo.Abp.Http.Client; using Volo.Abp.TestApp.Application; @@ -168,5 +171,31 @@ namespace Volo.Abp.Http.DynamicProxying result.Inner1.Value2.ShouldBe("value two"); result.Inner1.Inner2.Value3.ShouldBe("value three"); } + + [Fact] + public async Task DownloadAsync() + { + var result = await _peopleAppService.DownloadAsync(); + + result.ContentType.ShouldBe("application/rtf"); + using (var reader = new StreamReader(result.GetStream())) + { + var str = await reader.ReadToEndAsync(); + str.ShouldBe("DownloadAsync"); + } + } + + [Fact] + public async Task UploadAsync() + { + var memoryStream = new MemoryStream(); + await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("UploadAsync")); + memoryStream.Position = 0; + var result = await _peopleAppService.UploadAsync(new RemoteStreamContent(memoryStream) + { + ContentType = "application/rtf" + }); + result.ShouldBe("UploadAsync:application/rtf"); + } } } diff --git a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs index da61333d4e..e18c2f8f4a 100644 --- a/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs +++ b/framework/test/Volo.Abp.Http.Client.Tests/Volo/Abp/Http/DynamicProxying/RegularTestController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; @@ -140,5 +141,13 @@ namespace Volo.Abp.Http.DynamicProxying [FromQuery] public DateTime FirstReleaseDate { get; set; } + + [FromQuery] + public List Colors { get; set; } + + public Car() + { + Colors = new List(); + } } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs index a34cd19ebb..2344cb7c9b 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/IPeopleAppService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; +using Volo.Abp.Content; using Volo.Abp.TestApp.Application.Dto; namespace Volo.Abp.TestApp.Application @@ -20,5 +21,9 @@ namespace Volo.Abp.TestApp.Application Task GetWithAuthorized(); Task GetWithComplexType(GetWithComplexTypeInput input); + + Task DownloadAsync(); + + Task UploadAsync(IRemoteStreamContent streamContent); } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs index cff72c7869..788304900d 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Application/PeopleAppService.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Volo.Abp.Application.Dtos; using Volo.Abp.TestApp.Domain; using Volo.Abp.Domain.Repositories; using Volo.Abp.Application.Services; +using Volo.Abp.Content; using Volo.Abp.TestApp.Application.Dto; namespace Volo.Abp.TestApp.Application @@ -64,5 +67,24 @@ namespace Volo.Abp.TestApp.Application { return Task.FromResult(input); } + + public async Task DownloadAsync() + { + var memoryStream = new MemoryStream(); + await memoryStream.WriteAsync(Encoding.UTF8.GetBytes("DownloadAsync")); + + return new RemoteStreamContent(memoryStream) + { + ContentType = "application/rtf" + }; + } + + public async Task UploadAsync(IRemoteStreamContent streamContent) + { + using (var reader = new StreamReader(streamContent.GetStream())) + { + return await reader.ReadToEndAsync() + ":" + streamContent.ContentType; + } + } } } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs index 3d9b324da8..e31b64f6e4 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs @@ -1,7 +1,9 @@ using Shouldly; using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Data; +using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories; using Volo.Abp.Modularity; using Volo.Abp.TestApp.Domain; @@ -119,5 +121,40 @@ namespace Volo.Abp.TestApp.Testing john.ShouldBeNull(); } } + + [Fact] + public async Task Should_HardDelete_WithDeleteMany() + { + var persons = await PersonRepository.GetListAsync(); + + await WithUnitOfWorkAsync(async () => + { + var hardDeleteEntities = (HashSet)UnitOfWorkManager.Current.Items.GetOrAdd( + UnitOfWorkItemNames.HardDeletedEntities, + () => new HashSet() + ); + hardDeleteEntities.UnionWith(persons); + await PersonRepository.DeleteManyAsync(persons); + }); + + var personsCount = await PersonRepository.GetCountAsync(); + + personsCount.ShouldBe(0); + } + + [Fact] + public async Task Should_HardDelete_WithDeleteMany_WithPredicate() + { + await WithUnitOfWorkAsync(async () => + { + await PersonRepository.HardDeleteAsync(x => x.Id == TestDataBuilder.UserDouglasId); + + await PersonRepository.DeleteManyAsync(new[] { TestDataBuilder.UserDouglasId }); + }); + + var douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); + + douglas.ShouldBeNull(); + } } } diff --git a/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs b/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs index de9067e2f7..cd602c07af 100644 --- a/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs +++ b/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs @@ -180,15 +180,19 @@ namespace Volo.Abp.Validation { //Valid ValidationHelper.IsValidEmailAddress("john.doe@domain.com").ShouldBe(true); + ValidationHelper.IsValidEmailAddress("john-doe1@domain.co").ShouldBe(true); ValidationHelper.IsValidEmailAddress("ip@1.2.3.123").ShouldBe(true); ValidationHelper.IsValidEmailAddress("pharaoh@egyptian.museum").ShouldBe(true); ValidationHelper.IsValidEmailAddress("john.doe+regexbuddy@gmail.com").ShouldBe(true); ValidationHelper.IsValidEmailAddress("Mike.O'Dell@ireland.com").ShouldBe(true); + ValidationHelper.IsValidEmailAddress("admin@localhost").ShouldBe(true); + ValidationHelper.IsValidEmailAddress("j@h.c").ShouldBe(true); //Invalid - ValidationHelper.IsValidEmailAddress("1024x768@60Hz").ShouldBe(false); ValidationHelper.IsValidEmailAddress("not.a.valid.email").ShouldBe(false); ValidationHelper.IsValidEmailAddress("john@aol...com").ShouldBe(false); + ValidationHelper.IsValidEmailAddress("john@aol@domain.com").ShouldBe(false); + ValidationHelper.IsValidEmailAddress("jack@domain.").ShouldBe(false); } [DependsOn(typeof(AbpAutofacModule))] diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json index 755cd5674b..459d115398 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/en.json @@ -60,6 +60,8 @@ "Volo.Account:InvalidEmailAddress": "Can not find the given email address: {0}", "PasswordReset": "Password reset", "PasswordResetInfoInEmail": "We received an account recovery request! If you initiated this request, click the following link to reset your password.", - "ResetMyPassword": "Reset my password" + "ResetMyPassword": "Reset my password", + "AccessDenied": "Access denied!", + "AccessDeniedMessage": "You do not have access to this resource." } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json index d8cf5dd74f..a1c899357b 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/tr.json @@ -60,6 +60,8 @@ "Volo.Account:InvalidEmailAddress": "Email adresi bulunamadı: {0}", "PasswordReset": "Şifre Sıfırlama", "PasswordResetInfoInEmail": "Şifrenizi sıfırlamanız için bir talep aldık! Eğer bu talebi siz gerçekleştirmişseniz, şifrenizi sıfırlamak için bağlantıya tıklayın.", - "ResetMyPassword": "Şifremi sıfırla" + "ResetMyPassword": "Şifremi sıfırla", + "AccessDenied": "Erişim reddedildi!", + "AccessDeniedMessage": "Bu kaynağa erişiminiz yok." } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json index 53fe6135f2..26c6fc3337 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hans.json @@ -60,6 +60,8 @@ "Volo.Account:InvalidEmailAddress": "找不到给定的电子邮件地址:{0}", "PasswordReset": "重设密码", "PasswordResetInfoInEmail": "我们收到了帐户恢复请求!如果你发起了此请求,请单击以下链接以重置密码.", - "ResetMyPassword": "重置我的密码" + "ResetMyPassword": "重置我的密码", + "AccessDenied": "拒绝访问!", + "AccessDeniedMessage": "你无权访问此资源." } } diff --git a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json index a67ea8d63f..8a57429344 100644 --- a/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json +++ b/modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/zh-Hant.json @@ -58,6 +58,8 @@ "ReturnToApplication": "返回到應用程序", "PasswordReset": "重設密碼", "PasswordResetInfoInEmail": "我們收到了帳戶恢復請求!如果你發起了此請求,請單擊以下鏈接以重置密碼.", - "ResetMyPassword": "重置我的密碼" + "ResetMyPassword": "重置我的密碼", + "AccessDenied": "拒絕訪問!", + "AccessDeniedMessage": "您無權訪問此資源." } } diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs index 3d7381d2c3..4dab1f9761 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs @@ -103,9 +103,9 @@ namespace Volo.Abp.Account.Web.Pages.Account public override async Task OnPostAsync(string action) { + var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl); if (action == "Cancel") { - var context = await Interaction.GetAuthorizationContextAsync(ReturnUrl); if (context == null) { return Redirect("~/"); @@ -142,7 +142,8 @@ namespace Volo.Abp.Account.Web.Pages.Account { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = result.ToIdentitySecurityLogAction(), - UserName = LoginInput.UserNameOrEmailAddress + UserName = LoginInput.UserNameOrEmailAddress, + ClientId = context?.Client?.ClientId }); if (result.RequiresTwoFactor) diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs index 26ee0d4c34..94860e80c1 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLogoutModel.cs @@ -20,12 +20,6 @@ namespace Volo.Abp.Account.Web.Pages.Account public async override Task OnGetAsync() { - await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() - { - Identity = IdentitySecurityLogIdentityConsts.Identity, - Action = IdentitySecurityLogActionConsts.Logout - }); - await SignInManager.SignOutAsync(); var logoutId = Request.Query["logoutId"].ToString(); @@ -33,11 +27,14 @@ namespace Volo.Abp.Account.Web.Pages.Account if (!string.IsNullOrEmpty(logoutId)) { var logoutContext = await Interaction.GetLogoutContextAsync(logoutId); + + await SaveSecurityLogAsync(logoutContext?.ClientId); + await SignInManager.SignOutAsync(); HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity()); - LoggedOutModel vm = new LoggedOutModel() + var vm = new LoggedOutModel() { PostLogoutRedirectUri = logoutContext?.PostLogoutRedirectUri, ClientName = logoutContext?.ClientName, @@ -49,6 +46,8 @@ namespace Volo.Abp.Account.Web.Pages.Account return RedirectToPage("./LoggedOut", vm); } + await SaveSecurityLogAsync(); + if (ReturnUrl != null) { return LocalRedirect(ReturnUrl); @@ -58,5 +57,18 @@ namespace Volo.Abp.Account.Web.Pages.Account $"IdentityServerSupportedLogoutModel couldn't find postLogoutUri... Redirecting to:/Account/Login.."); return RedirectToPage("/Account/Login"); } + + protected virtual async Task SaveSecurityLogAsync(string clientId = null) + { + if (CurrentUser.IsAuthenticated) + { + await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext() + { + Identity = IdentitySecurityLogIdentityConsts.Identity, + Action = IdentitySecurityLogActionConsts.Logout, + ClientId = clientId + }); + } + } } } diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs index 303963d24f..4a806b9ae2 100644 --- a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs +++ b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs @@ -8,6 +8,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars; using Volo.Abp.AutoMapper; +using Volo.Abp.ExceptionHandling; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Modularity; using Volo.Abp.UI.Navigation; @@ -19,7 +20,8 @@ namespace Volo.Abp.Account.Web typeof(AbpAccountHttpApiModule), typeof(AbpIdentityAspNetCoreModule), typeof(AbpAutoMapperModule), - typeof(AbpAspNetCoreMvcUiThemeSharedModule) + typeof(AbpAspNetCoreMvcUiThemeSharedModule), + typeof(AbpExceptionHandlingModule) )] public class AbpAccountWebModule : AbpModule { diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccessDenied.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccessDenied.cshtml new file mode 100644 index 0000000000..28608ff883 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccessDenied.cshtml @@ -0,0 +1,15 @@ +@page +@model Volo.Abp.Account.Web.Pages.Account.AccessDeniedModel +@using Microsoft.AspNetCore.Mvc.Localization +@using Volo.Abp.Account.Localization +@inject IHtmlLocalizer L + +
+
+

@L["AccessDenied"]

+
+

@L["AccessDeniedMessage"]

+ ← @L["BackToLogin"] +
+
+
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccessDenied.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccessDenied.cshtml.cs new file mode 100644 index 0000000000..7ac9639d0b --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccessDenied.cshtml.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + public class AccessDeniedModel : AccountPageModel + { + [BindProperty(SupportsGet = true)] + public string ReturnUrl { get; set; } + + [BindProperty(SupportsGet = true)] + public string ReturnUrlHash { get; set; } + + public virtual Task OnGetAsync() + { + return Task.FromResult(Page()); + } + + public virtual Task OnPostAsync() + { + return Task.FromResult(Page()); + } + } +} diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs index 3e016c8bb5..c4461bd512 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs @@ -2,10 +2,11 @@ using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Volo.Abp.Account.Localization; +using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.ExceptionHandling; using Volo.Abp.Identity; using IdentityUser = Volo.Abp.Identity.IdentityUser; @@ -18,6 +19,7 @@ namespace Volo.Abp.Account.Web.Pages.Account public IdentityUserManager UserManager { get; set; } public IdentitySecurityLogManager IdentitySecurityLogManager { get; set; } public IOptions IdentityOptions { get; set; } + public IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; set; } protected AccountPageModel() { @@ -42,5 +44,15 @@ namespace Volo.Abp.Account.Web.Pages.Account throw new ApplicationException($"Current tenant is different than given tenant. CurrentTenant.Id: {CurrentTenant.Id}, given tenantId: {tenantId}"); } } + + protected virtual string GetLocalizeExceptionMessage(Exception exception) + { + if (exception is ILocalizeErrorMessage || exception is IHasErrorCode) + { + return ExceptionToErrorInfoConverter.Convert(exception, false).Message; + } + + return exception.Message; + } } } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml.cs index b66b53c913..1789a6ba2e 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ForgotPassword.cshtml.cs @@ -43,7 +43,7 @@ namespace Volo.Abp.Account.Web.Pages.Account } catch (UserFriendlyException e) { - Alerts.Danger(e.Message); + Alerts.Danger(GetLocalizeExceptionMessage(e)); return Page(); } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml index 056e69e895..57dec99889 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml @@ -56,7 +56,7 @@ @foreach (var provider in Model.VisibleExternalProviders) { - + } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs index 065573c137..eb6307e6b0 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -1,8 +1,6 @@ -using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Security.Claims; -using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -100,7 +98,7 @@ namespace Volo.Abp.Account.Web.Pages.Account } catch (BusinessException e) { - Alerts.Danger(e.Message); + Alerts.Danger(GetLocalizeExceptionMessage(e)); return Page(); } } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml.cs index 13370f5fbd..c961fe7e14 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPassword.cshtml.cs @@ -82,7 +82,7 @@ namespace Volo.Abp.Account.Web.Pages.Account { if (!string.IsNullOrWhiteSpace(e.Message)) { - Alerts.Warning(e.Message); + Alerts.Warning(GetLocalizeExceptionMessage(e)); return Page(); } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs index ad0e0d20c2..cd7e22b9a2 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.Domain/Volo/Abp/AuditLogging/IAuditLogRepository.cs @@ -44,9 +44,10 @@ namespace Volo.Abp.AuditLogging Task> GetAverageExecutionDurationPerDayAsync( DateTime startDate, - DateTime endDate); + DateTime endDate, + CancellationToken cancellationToken = default); - Task GetEntityChange(Guid entityChangeId); + Task GetEntityChange(Guid entityChangeId, CancellationToken cancellationToken = default); Task> GetEntityChangeListAsync( string sorting = null, @@ -70,8 +71,8 @@ namespace Volo.Abp.AuditLogging string entityTypeFullName = null, CancellationToken cancellationToken = default); - Task GetEntityChangeWithUsernameAsync(Guid entityChangeId); + Task GetEntityChangeWithUsernameAsync(Guid entityChangeId, CancellationToken cancellationToken = default); - Task> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName); + Task> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName, CancellationToken cancellationToken = default); } } diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs index edb1f3f599..acd1912925 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.EntityFrameworkCore/Volo/Abp/AuditLogging/EntityFrameworkCore/EfCoreAuditLogRepository.cs @@ -125,14 +125,17 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore .WhereIf(minExecutionDuration != null && minExecutionDuration.Value > 0, auditLog => auditLog.ExecutionDuration >= minExecutionDuration); } - public virtual async Task> GetAverageExecutionDurationPerDayAsync(DateTime startDate, DateTime endDate) + public virtual async Task> GetAverageExecutionDurationPerDayAsync( + DateTime startDate, + DateTime endDate, + CancellationToken cancellationToken = default) { var result = await (await GetDbSetAsync()).AsNoTracking() .Where(a => a.ExecutionTime < endDate.AddDays(1) && a.ExecutionTime > startDate) .OrderBy(t => t.ExecutionTime) .GroupBy(t => new { t.ExecutionTime.Date }) .Select(g => new { Day = g.Min(t => t.ExecutionTime), avgExecutionTime = g.Average(t => t.ExecutionDuration) }) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); return result.ToDictionary(element => element.Day.ClearTime(), element => element.avgExecutionTime); } @@ -148,14 +151,16 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore return (await GetQueryableAsync()).IncludeDetails(); } - public virtual async Task GetEntityChange(Guid entityChangeId) + public virtual async Task GetEntityChange( + Guid entityChangeId, + CancellationToken cancellationToken = default) { var entityChange = await (await GetDbContextAsync()).Set() .AsNoTracking() .IncludeDetails() .Where(x => x.Id == entityChangeId) .OrderBy(x => x.Id) - .FirstOrDefaultAsync(); + .FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); if (entityChange == null) { @@ -201,10 +206,12 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore return totalCount; } - public virtual async Task GetEntityChangeWithUsernameAsync(Guid entityChangeId) + public virtual async Task GetEntityChangeWithUsernameAsync( + Guid entityChangeId, + CancellationToken cancellationToken = default) { var auditLog = await (await GetDbSetAsync()).AsNoTracking().IncludeDetails() - .Where(x => x.EntityChanges.Any(y => y.Id == entityChangeId)).FirstAsync(); + .Where(x => x.EntityChanges.Any(y => y.Id == entityChangeId)).FirstAsync(GetCancellationToken(cancellationToken)); return new EntityChangeWithUsername() { @@ -213,7 +220,10 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore }; } - public virtual async Task> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName) + public virtual async Task> GetEntityChangesWithUsernameAsync( + string entityId, + string entityTypeFullName, + CancellationToken cancellationToken = default) { var dbContext = await GetDbContextAsync(); @@ -225,7 +235,7 @@ namespace Volo.Abp.AuditLogging.EntityFrameworkCore return await (from e in query join auditLog in dbContext.AuditLogs on e.AuditLogId equals auditLog.Id select new EntityChangeWithUsername {EntityChange = e, UserName = auditLog.UserName}) - .OrderByDescending(x => x.EntityChange.ChangeTime).ToListAsync(); + .OrderByDescending(x => x.EntityChange.ChangeTime).ToListAsync(GetCancellationToken(cancellationToken)); } protected virtual async Task> GetEntityChangeListQueryAsync( diff --git a/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs b/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs index 788b6995bb..e0ce5990b7 100644 --- a/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs +++ b/modules/audit-logging/src/Volo.Abp.AuditLogging.MongoDB/Volo/Abp/AuditLogging/MongoDB/MongoAuditLogRepository.cs @@ -52,7 +52,8 @@ namespace Volo.Abp.AuditLogging.MongoDB minDuration, hasException, httpStatusCode, - includeDetails + includeDetails, + cancellationToken ); return await query.OrderBy(sorting ?? "executionTime desc").As>() @@ -85,7 +86,8 @@ namespace Volo.Abp.AuditLogging.MongoDB maxDuration, minDuration, hasException, - httpStatusCode + httpStatusCode, + cancellationToken: cancellationToken ); var count = await query.As>() @@ -106,9 +108,10 @@ namespace Volo.Abp.AuditLogging.MongoDB int? minDuration = null, bool? hasException = null, HttpStatusCode? httpStatusCode = null, - bool includeDetails = false) + bool includeDetails = false, + CancellationToken cancellationToken = default) { - return (await GetMongoQueryableAsync()) + return (await GetMongoQueryableAsync(cancellationToken)) .WhereIf(startTime.HasValue, auditLog => auditLog.ExecutionTime >= startTime) .WhereIf(endTime.HasValue, auditLog => auditLog.ExecutionTime <= endTime) .WhereIf(hasException.HasValue && hasException.Value, auditLog => auditLog.Exceptions != null && auditLog.Exceptions != "") @@ -124,9 +127,12 @@ namespace Volo.Abp.AuditLogging.MongoDB } - public virtual async Task> GetAverageExecutionDurationPerDayAsync(DateTime startDate, DateTime endDate) + public virtual async Task> GetAverageExecutionDurationPerDayAsync( + DateTime startDate, + DateTime endDate, + CancellationToken cancellationToken = default) { - var result = await (await GetMongoQueryableAsync()) + var result = await (await GetMongoQueryableAsync(cancellationToken)) .Where(a => a.ExecutionTime < endDate.AddDays(1) && a.ExecutionTime > startDate) .OrderBy(t => t.ExecutionTime) .GroupBy(t => new @@ -136,17 +142,19 @@ namespace Volo.Abp.AuditLogging.MongoDB t.ExecutionTime.Day }) .Select(g => new { Day = g.Min(t => t.ExecutionTime), avgExecutionTime = g.Average(t => t.ExecutionDuration) }) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); return result.ToDictionary(element => element.Day.ClearTime(), element => element.avgExecutionTime); } - public virtual async Task GetEntityChange(Guid entityChangeId) + public virtual async Task GetEntityChange( + Guid entityChangeId, + CancellationToken cancellationToken = default) { - var entityChange = (await (await GetMongoQueryableAsync()) + var entityChange = (await (await GetMongoQueryableAsync(cancellationToken)) .Where(x => x.EntityChanges.Any(y => y.Id == entityChangeId)) .OrderBy(x => x.Id) - .FirstAsync()).EntityChanges.FirstOrDefault(x => x.Id == entityChangeId); + .FirstAsync(GetCancellationToken(cancellationToken))).EntityChanges.FirstOrDefault(x => x.Id == entityChangeId); if (entityChange == null) { @@ -169,7 +177,7 @@ namespace Volo.Abp.AuditLogging.MongoDB bool includeDetails = false, CancellationToken cancellationToken = default) { - var query = await GetEntityChangeListQueryAsync(auditLogId, startTime, endTime, changeType, entityId, entityTypeFullName); + var query = await GetEntityChangeListQueryAsync(auditLogId, startTime, endTime, changeType, entityId, entityTypeFullName, cancellationToken); return await query .OrderBy(sorting ?? "changeTime desc") @@ -187,18 +195,20 @@ namespace Volo.Abp.AuditLogging.MongoDB string entityTypeFullName = null, CancellationToken cancellationToken = default) { - var query = await GetEntityChangeListQueryAsync(auditLogId, startTime, endTime, changeType, entityId, entityTypeFullName); + var query = await GetEntityChangeListQueryAsync(auditLogId, startTime, endTime, changeType, entityId, entityTypeFullName, cancellationToken); var count = await query.As>().LongCountAsync(GetCancellationToken(cancellationToken)); return count; } - public virtual async Task GetEntityChangeWithUsernameAsync(Guid entityChangeId) + public virtual async Task GetEntityChangeWithUsernameAsync( + Guid entityChangeId, + CancellationToken cancellationToken = default) { - var auditLog = (await (await GetMongoQueryableAsync()) + var auditLog = await (await GetMongoQueryableAsync(cancellationToken)) .Where(x => x.EntityChanges.Any(y => y.Id == entityChangeId)) - .FirstAsync()); + .FirstAsync(GetCancellationToken(cancellationToken)); return new EntityChangeWithUsername() { @@ -207,13 +217,16 @@ namespace Volo.Abp.AuditLogging.MongoDB }; } - public virtual async Task> GetEntityChangesWithUsernameAsync(string entityId, string entityTypeFullName) + public virtual async Task> GetEntityChangesWithUsernameAsync( + string entityId, + string entityTypeFullName, + CancellationToken cancellationToken = default) { - var auditLogs = await (await GetMongoQueryableAsync()) + var auditLogs = await (await GetMongoQueryableAsync(cancellationToken)) .Where(x => x.EntityChanges.Any(y => y.EntityId == entityId && y.EntityTypeFullName == entityTypeFullName)) .As>() .OrderByDescending(x => x.ExecutionTime) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); var entityChanges = auditLogs.SelectMany(x => x.EntityChanges).ToList(); @@ -229,9 +242,10 @@ namespace Volo.Abp.AuditLogging.MongoDB DateTime? endTime = null, EntityChangeType? changeType = null, string entityId = null, - string entityTypeFullName = null) + string entityTypeFullName = null, + CancellationToken cancellationToken = default) { - return (await GetMongoQueryableAsync()) + return (await GetMongoQueryableAsync(cancellationToken)) .SelectMany(x => x.EntityChanges) .WhereIf(auditLogId.HasValue, e => e.Id == auditLogId) .WhereIf(startTime.HasValue, e => e.ChangeTime >= startTime) diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/IBackgroundJobRepository.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/IBackgroundJobRepository.cs index e12aeff70a..0fdf15de4b 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/IBackgroundJobRepository.cs +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.Domain/Volo/Abp/BackgroundJobs/IBackgroundJobRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -7,6 +8,6 @@ namespace Volo.Abp.BackgroundJobs { public interface IBackgroundJobRepository : IBasicRepository { - Task> GetWaitingListAsync(int maxResultCount); + Task> GetWaitingListAsync(int maxResultCount, CancellationToken cancellationToken = default); } -} \ No newline at end of file +} diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo/Abp/BackgroundJobs/EntityFrameworkCore/EfCoreBackgroundJobRepository.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo/Abp/BackgroundJobs/EntityFrameworkCore/EfCoreBackgroundJobRepository.cs index 1971faf380..3ae5d2a409 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo/Abp/BackgroundJobs/EntityFrameworkCore/EfCoreBackgroundJobRepository.cs +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.EntityFrameworkCore/Volo/Abp/BackgroundJobs/EntityFrameworkCore/EfCoreBackgroundJobRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; @@ -21,9 +22,11 @@ namespace Volo.Abp.BackgroundJobs.EntityFrameworkCore Clock = clock; } - public virtual async Task> GetWaitingListAsync(int maxResultCount) + public virtual async Task> GetWaitingListAsync( + int maxResultCount, + CancellationToken cancellationToken = default) { - return await (await GetWaitingListQueryAsync(maxResultCount)).ToListAsync(); + return await (await GetWaitingListQueryAsync(maxResultCount)).ToListAsync(GetCancellationToken(cancellationToken)); } protected virtual async Task> GetWaitingListQueryAsync(int maxResultCount) diff --git a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo/Abp/BackgroundJobs/MongoDB/MongoBackgroundJobRepository.cs b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo/Abp/BackgroundJobs/MongoDB/MongoBackgroundJobRepository.cs index 258c9310e2..56d0bfaff1 100644 --- a/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo/Abp/BackgroundJobs/MongoDB/MongoBackgroundJobRepository.cs +++ b/modules/background-jobs/src/Volo.Abp.BackgroundJobs.MongoDB/Volo/Abp/BackgroundJobs/MongoDB/MongoBackgroundJobRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -21,15 +22,17 @@ namespace Volo.Abp.BackgroundJobs.MongoDB Clock = clock; } - public virtual async Task> GetWaitingListAsync(int maxResultCount) + public virtual async Task> GetWaitingListAsync( + int maxResultCount, + CancellationToken cancellationToken = default) { - return await (await GetWaitingListQuery(maxResultCount)).ToListAsync(); + return await (await GetWaitingListQuery(maxResultCount)).ToListAsync(GetCancellationToken(cancellationToken)); } - protected virtual async Task> GetWaitingListQuery(int maxResultCount) + protected virtual async Task> GetWaitingListQuery(int maxResultCount, CancellationToken cancellationToken = default) { var now = Clock.Now; - return (await GetMongoQueryableAsync()) + return (await GetMongoQueryableAsync(cancellationToken)) .Where(t => !t.IsAbandoned && t.NextTryTime <= now) .OrderByDescending(t => t.Priority) .ThenBy(t => t.TryCount) diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/IBlogRepository.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/IBlogRepository.cs index ade8d1be0e..37dbf85ddd 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/IBlogRepository.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Blogs/IBlogRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -6,6 +7,6 @@ namespace Volo.Blogging.Blogs { public interface IBlogRepository : IBasicRepository { - Task FindByShortNameAsync(string shortName); + Task FindByShortNameAsync(string shortName, CancellationToken cancellationToken = default); } } diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Comments/ICommentRepository.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Comments/ICommentRepository.cs index 88ff91d516..65144cf5c2 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Comments/ICommentRepository.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Comments/ICommentRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -7,14 +8,12 @@ namespace Volo.Blogging.Comments { public interface ICommentRepository : IBasicRepository { - Task> GetListOfPostAsync( - Guid postId - ); + Task> GetListOfPostAsync(Guid postId, CancellationToken cancellationToken = default); - Task GetCommentCountOfPostAsync(Guid postId); + Task GetCommentCountOfPostAsync(Guid postId, CancellationToken cancellationToken = default); - Task> GetRepliesOfComment(Guid id); + Task> GetRepliesOfComment(Guid id, CancellationToken cancellationToken = default); - Task DeleteOfPost(Guid id); + Task DeleteOfPost(Guid id, CancellationToken cancellationToken = default); } } diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs index 9a49590015..b5446515bf 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -7,12 +8,12 @@ namespace Volo.Blogging.Posts { public interface IPostRepository : IBasicRepository { - Task> GetPostsByBlogId(Guid id); + Task> GetPostsByBlogId(Guid id, CancellationToken cancellationToken = default); - Task IsPostUrlInUseAsync(Guid blogId, string url, Guid? excludingPostId = null); + Task IsPostUrlInUseAsync(Guid blogId, string url, Guid? excludingPostId = null, CancellationToken cancellationToken = default); - Task GetPostByUrl(Guid blogId, string url); + Task GetPostByUrl(Guid blogId, string url, CancellationToken cancellationToken = default); - Task> GetOrderedList(Guid blogId,bool descending = false); + Task> GetOrderedList(Guid blogId,bool descending = false, CancellationToken cancellationToken = default); } } diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/ITagRepository.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/ITagRepository.cs index 8c076c49d7..12aa0031ed 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/ITagRepository.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Tagging/ITagRepository.cs @@ -8,13 +8,13 @@ namespace Volo.Blogging.Tagging { public interface ITagRepository : IBasicRepository { - Task> GetListAsync(Guid blogId); + Task> GetListAsync(Guid blogId, CancellationToken cancellationToken = default); - Task GetByNameAsync(Guid blogId, string name); + Task GetByNameAsync(Guid blogId, string name, CancellationToken cancellationToken = default); - Task FindByNameAsync(Guid blogId, string name); + Task FindByNameAsync(Guid blogId, string name, CancellationToken cancellationToken = default); - Task> GetListAsync(IEnumerable ids); + Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default); Task DecreaseUsageCountOfTagsAsync(List id, CancellationToken cancellationToken = default); } diff --git a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Blogs/EfCoreBlogRepository.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Blogs/EfCoreBlogRepository.cs index 564d45dc7a..d5397a66f0 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Blogs/EfCoreBlogRepository.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Blogs/EfCoreBlogRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; @@ -15,9 +16,9 @@ namespace Volo.Blogging.Blogs } - public async Task FindByShortNameAsync(string shortName) + public async Task FindByShortNameAsync(string shortName, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).FirstOrDefaultAsync(p => p.ShortName == shortName); + return await (await GetDbSetAsync()).FirstOrDefaultAsync(p => p.ShortName == shortName, GetCancellationToken(cancellationToken)); } } } diff --git a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Comments/EfCoreCommentRepository.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Comments/EfCoreCommentRepository.cs index 6c28b521aa..801ef07825 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Comments/EfCoreCommentRepository.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Comments/EfCoreCommentRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; @@ -16,29 +17,29 @@ namespace Volo.Blogging.Comments { } - public async Task> GetListOfPostAsync(Guid postId) + public async Task> GetListOfPostAsync(Guid postId, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .Where(a => a.PostId == postId) .OrderBy(a => a.CreationTime) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task GetCommentCountOfPostAsync(Guid postId) + public async Task GetCommentCountOfPostAsync(Guid postId, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) - .CountAsync(a => a.PostId == postId); + .CountAsync(a => a.PostId == postId, GetCancellationToken(cancellationToken)); } - public async Task> GetRepliesOfComment(Guid id) + public async Task> GetRepliesOfComment(Guid id, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) - .Where(a => a.RepliedCommentId == id).ToListAsync(); + .Where(a => a.RepliedCommentId == id).ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task DeleteOfPost(Guid id) + public async Task DeleteOfPost(Guid id, CancellationToken cancellationToken = default) { - var recordsToDelete = (await GetDbSetAsync()).Where(pt => pt.PostId == id); + var recordsToDelete = await (await GetDbSetAsync()).Where(pt => pt.PostId == id).ToListAsync(GetCancellationToken(cancellationToken)); (await GetDbSetAsync()).RemoveRange(recordsToDelete); } } diff --git a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs index a5a84fd340..a676816dee 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Entities; @@ -18,12 +19,12 @@ namespace Volo.Blogging.Posts } - public async Task> GetPostsByBlogId(Guid id) + public async Task> GetPostsByBlogId(Guid id, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).Where(p => p.BlogId == id).OrderByDescending(p=>p.CreationTime).ToListAsync(); + return await (await GetDbSetAsync()).Where(p => p.BlogId == id).OrderByDescending(p=>p.CreationTime).ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task IsPostUrlInUseAsync(Guid blogId, string url, Guid? excludingPostId = null) + public async Task IsPostUrlInUseAsync(Guid blogId, string url, Guid? excludingPostId = null, CancellationToken cancellationToken = default) { var query = (await GetDbSetAsync()).Where(p => blogId == p.BlogId && p.Url == url); @@ -32,12 +33,12 @@ namespace Volo.Blogging.Posts query = query.Where(p => excludingPostId != p.Id); } - return await query.AnyAsync(); + return await query.AnyAsync(GetCancellationToken(cancellationToken)); } - public async Task GetPostByUrl(Guid blogId, string url) + public async Task GetPostByUrl(Guid blogId, string url, CancellationToken cancellationToken = default) { - var post = await (await GetDbSetAsync()).FirstOrDefaultAsync(p => p.BlogId == blogId && p.Url == url); + var post = await (await GetDbSetAsync()).FirstOrDefaultAsync(p => p.BlogId == blogId && p.Url == url, GetCancellationToken(cancellationToken)); if (post == null) { @@ -47,15 +48,15 @@ namespace Volo.Blogging.Posts return post; } - public async Task> GetOrderedList(Guid blogId,bool descending = false) + public async Task> GetOrderedList(Guid blogId,bool descending = false, CancellationToken cancellationToken = default) { if (!descending) { - return await (await GetDbSetAsync()).Where(x=>x.BlogId==blogId).OrderByDescending(x => x.CreationTime).ToListAsync(); + return await (await GetDbSetAsync()).Where(x=>x.BlogId==blogId).OrderByDescending(x => x.CreationTime).ToListAsync(GetCancellationToken(cancellationToken)); } else { - return await (await GetDbSetAsync()).Where(x => x.BlogId == blogId).OrderBy(x => x.CreationTime).ToListAsync(); + return await (await GetDbSetAsync()).Where(x => x.BlogId == blogId).OrderBy(x => x.CreationTime).ToListAsync(GetCancellationToken(cancellationToken)); } } diff --git a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Tagging/EfCoreTagRepository.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Tagging/EfCoreTagRepository.cs index a92ae780da..0a33207374 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Tagging/EfCoreTagRepository.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Tagging/EfCoreTagRepository.cs @@ -17,24 +17,24 @@ namespace Volo.Blogging.Tagging { } - public async Task> GetListAsync(Guid blogId) + public async Task> GetListAsync(Guid blogId, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).Where(t=>t.BlogId == blogId).ToListAsync(); + return await (await GetDbSetAsync()).Where(t=>t.BlogId == blogId).ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task GetByNameAsync(Guid blogId, string name) + public async Task GetByNameAsync(Guid blogId, string name, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).FirstAsync(t=> t.BlogId == blogId && t.Name == name); + return await (await GetDbSetAsync()).FirstAsync(t=> t.BlogId == blogId && t.Name == name, GetCancellationToken(cancellationToken)); } - public async Task FindByNameAsync(Guid blogId, string name) + public async Task FindByNameAsync(Guid blogId, string name, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).FirstOrDefaultAsync(t => t.BlogId == blogId && t.Name == name); + return await (await GetDbSetAsync()).FirstOrDefaultAsync(t => t.BlogId == blogId && t.Name == name, GetCancellationToken(cancellationToken)); } - public async Task> GetListAsync(IEnumerable ids) + public async Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default) { - return await (await GetDbSetAsync()).Where(t => ids.Contains(t.Id)).ToListAsync(); + return await (await GetDbSetAsync()).Where(t => ids.Contains(t.Id)).ToListAsync(GetCancellationToken(cancellationToken)); } public async Task DecreaseUsageCountOfTagsAsync(List ids, CancellationToken cancellationToken = default) diff --git a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Blogs/MongoBlogRepository.cs b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Blogs/MongoBlogRepository.cs index 6025dfe3ca..2577b8d3a0 100644 --- a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Blogs/MongoBlogRepository.cs +++ b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Blogs/MongoBlogRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver.Linq; using Volo.Abp.Domain.Repositories.MongoDB; @@ -13,9 +14,9 @@ namespace Volo.Blogging.Blogs { } - public async Task FindByShortNameAsync(string shortName) + public async Task FindByShortNameAsync(string shortName, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()).FirstOrDefaultAsync(p => p.ShortName == shortName); + return await (await GetMongoQueryableAsync(cancellationToken)).FirstOrDefaultAsync(p => p.ShortName == shortName, GetCancellationToken(cancellationToken)); } } } diff --git a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Comments/MongoCommentRepository.cs b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Comments/MongoCommentRepository.cs index bbbe60345b..689e44abbd 100644 --- a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Comments/MongoCommentRepository.cs +++ b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Comments/MongoCommentRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -15,33 +16,33 @@ namespace Volo.Blogging.Comments { } - public async Task> GetListOfPostAsync(Guid postId) + public async Task> GetListOfPostAsync(Guid postId, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) + return await (await GetMongoQueryableAsync(cancellationToken)) .Where(a => a.PostId == postId) .OrderBy(a => a.CreationTime) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task GetCommentCountOfPostAsync(Guid postId) + public async Task GetCommentCountOfPostAsync(Guid postId, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) - .CountAsync(a => a.PostId == postId); + return await (await GetMongoQueryableAsync(cancellationToken)) + .CountAsync(a => a.PostId == postId, GetCancellationToken(cancellationToken)); } - public async Task> GetRepliesOfComment(Guid id) + public async Task> GetRepliesOfComment(Guid id, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) - .Where(a => a.RepliedCommentId == id).ToListAsync(); + return await (await GetMongoQueryableAsync(cancellationToken)) + .Where(a => a.RepliedCommentId == id).ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task DeleteOfPost(Guid id) + public async Task DeleteOfPost(Guid id, CancellationToken cancellationToken = default) { - var recordsToDelete = (await GetMongoQueryableAsync()).Where(pt => pt.PostId == id); + var recordsToDelete = (await GetMongoQueryableAsync(cancellationToken)).Where(pt => pt.PostId == id); foreach (var record in recordsToDelete) { - await DeleteAsync(record); + await DeleteAsync(record, cancellationToken: GetCancellationToken(cancellationToken)); } } } diff --git a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs index 27f4ad4348..a2d79c9466 100644 --- a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs +++ b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -16,27 +17,27 @@ namespace Volo.Blogging.Posts { } - public async Task> GetPostsByBlogId(Guid id) + public async Task> GetPostsByBlogId(Guid id, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()).Where(p => p.BlogId == id).OrderByDescending(p => p.CreationTime).ToListAsync(); + return await (await GetMongoQueryableAsync(cancellationToken)).Where(p => p.BlogId == id).OrderByDescending(p => p.CreationTime).ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task IsPostUrlInUseAsync(Guid blogId, string url, Guid? excludingPostId = null) + public async Task IsPostUrlInUseAsync(Guid blogId, string url, Guid? excludingPostId = null, CancellationToken cancellationToken = default) { - var query = (await GetMongoQueryableAsync()).Where(p => blogId == p.BlogId && p.Url == url); + var query = (await GetMongoQueryableAsync(cancellationToken)).Where(p => blogId == p.BlogId && p.Url == url); if (excludingPostId != null) { query = query.Where(p => excludingPostId != p.Id); } - return await query.AnyAsync(); + return await query.AnyAsync(GetCancellationToken(cancellationToken)); } - public async Task GetPostByUrl(Guid blogId, string url) + public async Task GetPostByUrl(Guid blogId, string url, CancellationToken cancellationToken = default) { - var post = await (await GetMongoQueryableAsync()).FirstOrDefaultAsync(p => p.BlogId == blogId && p.Url == url); + var post = await (await GetMongoQueryableAsync(cancellationToken)).FirstOrDefaultAsync(p => p.BlogId == blogId && p.Url == url, GetCancellationToken(cancellationToken)); if (post == null) { @@ -46,16 +47,16 @@ namespace Volo.Blogging.Posts return post; } - public async Task> GetOrderedList(Guid blogId, bool @descending = false) + public async Task> GetOrderedList(Guid blogId, bool @descending = false, CancellationToken cancellationToken = default) { - var query = (await GetMongoQueryableAsync()).Where(x => x.BlogId == blogId); + var query = (await GetMongoQueryableAsync(cancellationToken)).Where(x => x.BlogId == blogId); if (!descending) { - return await query.OrderBy(x => x.CreationTime).ToListAsync(); + return await query.OrderBy(x => x.CreationTime).ToListAsync(GetCancellationToken(cancellationToken)); } - return await query.OrderByDescending(x => x.CreationTime).ToListAsync(); + return await query.OrderByDescending(x => x.CreationTime).ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs index f0024b37d7..2030d47f2d 100644 --- a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs +++ b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Tagging/MongoTagRepository.cs @@ -18,29 +18,29 @@ namespace Volo.Blogging.Tagging { } - public async Task> GetListAsync(Guid blogId) + public async Task> GetListAsync(Guid blogId, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()).Where(t => t.BlogId == blogId).ToListAsync(); + return await (await GetMongoQueryableAsync(cancellationToken)).Where(t => t.BlogId == blogId).ToListAsync(GetCancellationToken(cancellationToken)); } - public async Task GetByNameAsync(Guid blogId, string name) + public async Task GetByNameAsync(Guid blogId, string name, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()).Where(t => t.BlogId == blogId && t.Name == name).FirstAsync(); + return await (await GetMongoQueryableAsync(cancellationToken)).Where(t => t.BlogId == blogId && t.Name == name).FirstAsync(GetCancellationToken(cancellationToken)); } - public async Task FindByNameAsync(Guid blogId, string name) + public async Task FindByNameAsync(Guid blogId, string name, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()).Where(t => t.BlogId == blogId && t.Name == name).FirstOrDefaultAsync(); + return await (await GetMongoQueryableAsync(cancellationToken)).Where(t => t.BlogId == blogId && t.Name == name).FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); } - public async Task> GetListAsync(IEnumerable ids) + public async Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()).Where(t => ids.Contains(t.Id)).ToListAsync(); + return await (await GetMongoQueryableAsync(cancellationToken)).Where(t => ids.Contains(t.Id)).ToListAsync(GetCancellationToken(cancellationToken)); } public async Task DecreaseUsageCountOfTagsAsync(List ids, CancellationToken cancellationToken = default) { - var tags = await (await GetMongoQueryableAsync()) + var tags = await (await GetMongoQueryableAsync(cancellationToken)) .Where(t => ids.Contains(t.Id)) .ToListAsync(GetCancellationToken(cancellationToken)); diff --git a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Users/MongoBlogUserRepository.cs b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Users/MongoBlogUserRepository.cs index fdcf4d0097..5523af8394 100644 --- a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Users/MongoBlogUserRepository.cs +++ b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Users/MongoBlogUserRepository.cs @@ -15,7 +15,7 @@ namespace Volo.Blogging.Users { } - public async Task> GetUsersAsync(int maxCount, string filter, CancellationToken cancellationToken) + public async Task> GetUsersAsync(int maxCount, string filter, CancellationToken cancellationToken = default) { var query = await GetMongoQueryableAsync(cancellationToken); @@ -24,7 +24,7 @@ namespace Volo.Blogging.Users query = query.Where(x => x.UserName.Contains(filter)); } - return await query.Take(maxCount).ToListAsync(cancellationToken); + return await query.Take(maxCount).ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListDto.cs new file mode 100644 index 0000000000..ce1ca1e86c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/ContentGetListDto.cs @@ -0,0 +1,12 @@ +using System; +using Volo.Abp.Application.Dtos; + +namespace Volo.CmsKit.Admin.Contents +{ + public class ContentGetListDto : EntityDto + { + public string EntityType { get; set; } + + public string EntityId { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs index 02b25aa16d..52bd07aa86 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Contents/IContentAdminAppService.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Volo.Abp.Application.Services; using Volo.CmsKit.Admin.Contents; @@ -7,10 +8,12 @@ namespace Volo.CmsKit.Admin.Contents public interface IContentAdminAppService : ICrudAppService< ContentDto, + ContentGetListDto, Guid, ContentGetListInput, ContentCreateDto, ContentUpdateDto> { + Task GetAsync(string entityType, string entityId); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs index e8750ce07b..3aebb76b51 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Pages/PageDto.cs @@ -3,7 +3,7 @@ using Volo.Abp.Application.Dtos; namespace Volo.CmsKit.Admin.Pages { - public class PageDto : EntityDto + public class PageDto : AuditedEntityDto { public string Title { get; set; } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagCreateDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagCreateDto.cs new file mode 100644 index 0000000000..92e83fecf4 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagCreateDto.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; + +namespace Volo.CmsKit.Admin.Tags +{ + public class EntityTagCreateDto + { + [Required] + public string TagName { get; set; } + + [Required] + public string EntityType { get; set; } + + [Required] + public string EntityId { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagRemoveDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagRemoveDto.cs new file mode 100644 index 0000000000..411d4c7157 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/EntityTagRemoveDto.cs @@ -0,0 +1,17 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Volo.CmsKit.Admin.Tags +{ + public class EntityTagRemoveDto + { + [Required] + public Guid TagId { get; set; } + + [Required] + public string EntityType { get; set; } + + [Required] + public string EntityId { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/IEntityTagAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/IEntityTagAdminAppService.cs new file mode 100644 index 0000000000..aece4d5c00 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/IEntityTagAdminAppService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Volo.CmsKit.Admin.Tags +{ + public interface IEntityTagAdminAppService + { + Task AddTagToEntityAsync(EntityTagCreateDto input); + + Task RemoveTagFromEntityAsync(EntityTagRemoveDto input); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/ITagAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/ITagAdminAppService.cs index e677a54c28..a5ae9d85d4 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/ITagAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/ITagAdminAppService.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Volo.Abp.Application.Services; using Volo.CmsKit.Tags; @@ -6,5 +8,6 @@ namespace Volo.CmsKit.Admin.Tags { public interface ITagAdminAppService : ICrudAppService { + Task> GetTagDefinitionsAsync(); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/TagDefinitionDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/TagDefinitionDto.cs new file mode 100644 index 0000000000..0fdfe04b00 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Tags/TagDefinitionDto.cs @@ -0,0 +1,18 @@ +namespace Volo.CmsKit.Admin.Tags +{ + public class TagDefinitionDto + { + public TagDefinitionDto() + { + } + public TagDefinitionDto(string entityType, string displayName) + { + EntityType = entityType; + DisplayName = displayName; + } + + public string EntityType { get; set; } + + public string DisplayName { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs index 4b7a6eb58f..a7ea1b5124 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/CmsKitAdminApplicationAutoMapperProfile.cs @@ -1,8 +1,10 @@ using AutoMapper; using Volo.CmsKit.Admin.Contents; using Volo.CmsKit.Admin.Pages; +using Volo.CmsKit.Admin.Tags; using Volo.CmsKit.Contents; using Volo.CmsKit.Pages; +using Volo.CmsKit.Tags; namespace Volo.CmsKit.Admin { @@ -13,8 +15,11 @@ namespace Volo.CmsKit.Admin CreateMap(); CreateMap(MemberList.Destination); + CreateMap(MemberList.Destination); CreateMap(MemberList.Source); CreateMap(MemberList.Source); + + CreateMap(MemberList.Destination); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs index 4695339fd7..300cf31181 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Contents/ContentAdminAppService.cs @@ -1,6 +1,8 @@ -using Microsoft.AspNetCore.Authorization; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; using System; using System.Threading.Tasks; +using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; using Volo.CmsKit.Contents; @@ -14,6 +16,7 @@ namespace Volo.CmsKit.Admin.Contents CrudAppService< Content, ContentDto, + ContentGetListDto, Guid, ContentGetListInput, ContentCreateDto, @@ -30,13 +33,13 @@ namespace Volo.CmsKit.Admin.Contents IContentRepository contentRepository) : base(repository) { ContentManager = contentManager; + ContentRepository = contentRepository; GetListPolicyName = CmsKitAdminPermissions.Contents.Default; GetPolicyName = CmsKitAdminPermissions.Contents.Default; CreatePolicyName = CmsKitAdminPermissions.Contents.Create; UpdatePolicyName = CmsKitAdminPermissions.Contents.Update; DeletePolicyName = CmsKitAdminPermissions.Contents.Delete; - ContentRepository = contentRepository; } [Authorize(CmsKitAdminPermissions.Contents.Create)] @@ -51,7 +54,19 @@ namespace Volo.CmsKit.Admin.Contents await ContentManager.InsertAsync(entity); - return MapToGetListOutputDto(entity); + return MapToGetOutputDto(entity); + } + + public async Task GetAsync( + [NotNull] string entityType, + [NotNull] string entityId) + { + Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); + Check.NotNullOrWhiteSpace(entityId, nameof(entityId)); + + var content = await ContentRepository.GetAsync(entityType, entityId, CurrentTenant?.Id); + + return ObjectMapper.Map(content); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs index 6223ed9c2e..3db45dac95 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Pages/PageAdminAppService.cs @@ -24,7 +24,6 @@ namespace Volo.CmsKit.Admin.Pages public virtual async Task GetAsync(Guid id) { var page = await PageRepository.GetAsync(id); - return ObjectMapper.Map(page); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs new file mode 100644 index 0000000000..1ef6a8bdf9 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/EntityTagAdminAppService.cs @@ -0,0 +1,52 @@ +using System; +using System.Threading.Tasks; +using Volo.CmsKit.Admin.Tags; +using Volo.CmsKit.Tags; + +namespace Volo.CmsKit.Admin.Application.Volo.CmsKit.Admin.Tags +{ + public class EntityTagAdminAppService : CmsKitAdminAppServiceBase, IEntityTagAdminAppService + { + protected ITagDefinitionStore _tagDefinitionStore; + protected IEntityTagManager _entityTagManager; + protected ITagManager _tagManager; + + public EntityTagAdminAppService( + ITagDefinitionStore tagDefinitionStore, + IEntityTagManager entityTagManager, + ITagManager tagManager) + { + _tagDefinitionStore = tagDefinitionStore; + _entityTagManager = entityTagManager; + _tagManager = tagManager; + } + + public async Task AddTagToEntityAsync(EntityTagCreateDto input) + { + var definition = await _tagDefinitionStore.GetTagEntityTypeDefinitionsAsync(input.EntityType); + + await CheckPolicyAsync(definition.CreatePolicy); + + var tag = await _tagManager.GetOrAddAsync(input.EntityType, input.TagName, CurrentTenant?.Id); + + await _entityTagManager.AddTagToEntityAsync( + tag.Id, + input.EntityType, + input.EntityId, + CurrentTenant?.Id); + } + + public async Task RemoveTagFromEntityAsync(EntityTagRemoveDto input) + { + var definition = await _tagDefinitionStore.GetTagEntityTypeDefinitionsAsync(input.EntityType); + + await CheckPolicyAsync(definition.DeletePolicy); + + await _entityTagManager.RemoveTagFromEntityAsync( + input.TagId, + input.EntityType, + input.EntityId, + CurrentTenant?.Id); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/TagAdminAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/TagAdminAppService.cs index cda45c23de..eccdaf82c0 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/TagAdminAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application/Volo/CmsKit/Admin/Tags/TagAdminAppService.cs @@ -1,12 +1,11 @@ using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Localization; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; -using Volo.CmsKit.Admin.Tags; using Volo.CmsKit.Permissions; using Volo.CmsKit.Tags; @@ -25,11 +24,15 @@ namespace Volo.CmsKit.Admin.Tags { protected ITagManager TagManager { get; } + protected IStringLocalizerFactory StringLocalizerFactory { get; } + public TagAdminAppService( IRepository repository, - ITagManager tagManager) : base(repository) + ITagManager tagManager, + IStringLocalizerFactory stringLocalizerFactory) : base(repository) { TagManager = tagManager; + StringLocalizerFactory = stringLocalizerFactory; GetListPolicyName = CmsKitAdminPermissions.Tags.Default; GetPolicyName = CmsKitAdminPermissions.Tags.Default; @@ -68,5 +71,17 @@ namespace Volo.CmsKit.Admin.Tags x.Name.ToLower().Contains(input.Filter) || x.EntityType.ToLower().Contains(input.Filter)); } + + public async Task> GetTagDefinitionsAsync() + { + var definitions = await TagManager.GetTagDefinitionsAsync(); + + return definitions + .Select(s => + new TagDefinitionDto( + s.EntityType, + s.DisplayName?.Localize(StringLocalizerFactory) ?? s.EntityType)) + .ToList(); + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs index 935606b686..4e3a56a8e2 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Contents/ContentAdminController.cs @@ -48,7 +48,7 @@ namespace Volo.CmsKit.Admin.Contents [HttpGet] [Authorize(CmsKitAdminPermissions.Contents.Default)] - public Task> GetListAsync(ContentGetListInput input) + public Task> GetListAsync(ContentGetListInput input) { return ContentAdminAppService.GetListAsync(input); } @@ -59,5 +59,13 @@ namespace Volo.CmsKit.Admin.Contents { return ContentAdminAppService.UpdateAsync(id, input); } + + [HttpGet] + [Route("{entityType}/{entityId}")] + [Authorize(CmsKitAdminPermissions.Contents.Default)] + public Task GetAsync(string entityType, string entityId) + { + return ContentAdminAppService.GetAsync(entityType, entityId); + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Tags/TagAdminController.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Tags/TagAdminController.cs index cc755fa63f..afc9a83fc8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Tags/TagAdminController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.HttpApi/Volo/CmsKit/Admin/Tags/TagAdminController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Application.Dtos; @@ -60,5 +61,10 @@ namespace Volo.CmsKit.Admin.Tags { return TagAdminAppService.UpdateAsync(id, input); } + + public Task> GetTagDefinitionsAsync() + { + return TagAdminAppService.GetTagDefinitionsAsync(); + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs index 67042b5cc6..35cbad6374 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs @@ -2,10 +2,14 @@ { public static class CmsKitErrorCodes { - public const string TagAlreadyExist = "CmsKit:0001"; - + public static class Tags + { + public const string TagAlreadyExist = "CmsKit:Tag:0001"; + public const string EntityNotTaggable = "CmsKit:Tag:0002"; + } + public const string ContentAlreadyExist = "CmsKit:0002"; - + public static class Pages { public const string UrlAlreadyExist = "CmsKit:Page:0001"; diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json index 7ed510da10..00eccab1be 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/en.json @@ -2,6 +2,7 @@ "culture": "en", "texts": { "CmsKit:0002": "Content already exists!", + "CmsKit:0003": "The entity {0} is not taggable.", "CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.", "Comments": "Comments", "Delete": "Delete", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json index 40494fbb39..1ebb0c126b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/tr.json @@ -2,6 +2,7 @@ "culture": "tr", "texts": { "CmsKit:0002": "İçerik zaten mevcut!", + "CmsKit:0003": "{0} ögesi etiketlenebilir değil.", "CommentAuthorizationExceptionMessage": "Bu yorumları görebilmek için yetki gerekir.", "Comments": "Yorumlar", "Delete": "Sil", diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs index b1937dd6e0..a60cb0d521 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitDomainModule.cs @@ -1,7 +1,14 @@ -using Volo.Abp.Domain; +using Microsoft.Extensions.Options; +using Volo.Abp.Domain; +using Volo.Abp.GlobalFeatures; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Users; +using Volo.CmsKit.GlobalFeatures; +using Volo.CmsKit.Localization; +using Volo.CmsKit.Pages; using Volo.CmsKit.Reactions; +using Volo.CmsKit.Tags; namespace Volo.CmsKit { @@ -28,7 +35,18 @@ namespace Volo.CmsKit options.Reactions.AddOrReplace(StandardReactions.HeartBroken); options.Reactions.AddOrReplace(StandardReactions.Rocket); options.Reactions.AddOrReplace(StandardReactions.Pray); + }); + + if (GlobalFeatureManager.Instance.IsEnabled()) + { + // TODO: Configure TagEntityTypes here... + } + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); } } -} +} \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs index cc69f715ce..8075895184 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/CmsKitOptions.cs @@ -1,16 +1,12 @@ using JetBrains.Annotations; using Volo.CmsKit.Reactions; +using Volo.CmsKit.Tags; namespace Volo.CmsKit { public class CmsKitOptions { [NotNull] - public ReactionDefinitionDictionary Reactions { get; } - - public CmsKitOptions() - { - Reactions = new ReactionDefinitionDictionary(); - } + public ReactionDefinitionDictionary Reactions { get; } = new ReactionDefinitionDictionary(); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs new file mode 100644 index 0000000000..6105beab97 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/PolicySpecifiedDefinition.cs @@ -0,0 +1,32 @@ +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Localization; + +namespace Volo.CmsKit.Domain.Volo.CmsKit +{ + public abstract class PolicySpecifiedDefinition + { + protected PolicySpecifiedDefinition() + { + } + + public PolicySpecifiedDefinition( + [CanBeNull] string createPolicy = null, + [CanBeNull] string updatePolicy = null, + [CanBeNull] string deletePolicy = null) + { + CreatePolicy = createPolicy; + DeletePolicy = deletePolicy; + UpdatePolicy = updatePolicy; + } + + [CanBeNull] + public virtual string CreatePolicy { get; set; } + + [CanBeNull] + public virtual string UpdatePolicy { get; set; } + + [CanBeNull] + public virtual string DeletePolicy { get; set; } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/CmsKitTagOptions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/CmsKitTagOptions.cs new file mode 100644 index 0000000000..8f9b073d6b --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/CmsKitTagOptions.cs @@ -0,0 +1,11 @@ +using JetBrains.Annotations; +using Volo.CmsKit.Tags; + +namespace Volo.CmsKit.Tags +{ + public class CmsKitTagOptions + { + [NotNull] + public TagEntityTypeDefinitionDictionary EntityTypes { get; } = new TagEntityTypeDefinitionDictionary(); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/DefaultTagDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/DefaultTagDefinitionStore.cs new file mode 100644 index 0000000000..6860e8c95c --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/DefaultTagDefinitionStore.cs @@ -0,0 +1,40 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.Options; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.DependencyInjection; + +namespace Volo.CmsKit.Tags +{ + public class DefaultTagDefinitionStore : ITagDefinitionStore, ITransientDependency + { + private readonly CmsKitTagOptions options; + + public DefaultTagDefinitionStore(IOptions options) + { + this.options = options.Value; + } + + public virtual Task GetTagEntityTypeDefinitionsAsync([NotNull] string entityType) + { + Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); + + var result = options.EntityTypes.GetOrDefault(entityType) ?? throw new EntityNotTaggableException(entityType); + + return Task.FromResult(result); + } + + public virtual Task> GetTagDefinitionsAsync() + { + return Task.FromResult(options.EntityTypes.Values.ToList()); + } + + public virtual Task IsDefinedAsync([NotNull] string entityType) + { + Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); + return Task.FromResult(options.EntityTypes.ContainsKey(entityType)); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityNotTaggableException.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityNotTaggableException.cs new file mode 100644 index 0000000000..8787fb16a5 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityNotTaggableException.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Logging; +using System; +using Volo.Abp; + +namespace Volo.CmsKit.Tags +{ + [Serializable] + public class EntityNotTaggableException : BusinessException + { + public EntityNotTaggableException( + string code = null, + string message = null, + string details = null, + Exception innerException = null, + LogLevel logLevel = LogLevel.Warning) + : base(code, message, details, innerException, logLevel) + { + } + + public EntityNotTaggableException(string entityType) + { + Code = CmsKitErrorCodes.Tags.EntityNotTaggable; + WithData(nameof(Tag.EntityType), entityType); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTag.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTag.cs index 5d27e8b15f..c49a9661ef 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTag.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTag.cs @@ -16,7 +16,7 @@ namespace Volo.CmsKit.Tags { } - public EntityTag(Guid tagId, string entityId, Guid? tenantId = null) + internal EntityTag(Guid tagId, string entityId, Guid? tenantId = null) { TagId = tagId; EntityId = entityId; @@ -27,6 +27,5 @@ namespace Volo.CmsKit.Tags { return new object[] { TagId, EntityId }; } - } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTagManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTagManager.cs new file mode 100644 index 0000000000..0de6328775 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/EntityTagManager.cs @@ -0,0 +1,52 @@ +using JetBrains.Annotations; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Services; +using Volo.CmsKit.Tags; + +namespace Volo.CmsKit.Tags +{ + public class EntityTagManager : DomainService, IEntityTagManager + { + protected readonly IEntityTagRepository _entityTagRepository; + protected readonly ITagDefinitionStore _tagDefinitionStore; + + public EntityTagManager( + IEntityTagRepository entityTagRepository, + ITagDefinitionStore tagDefinitionStore) + { + _entityTagRepository = entityTagRepository; + _tagDefinitionStore = tagDefinitionStore; + } + + public async Task AddTagToEntityAsync( + [NotNull] Guid tagId, + [NotNull] string entityType, + [NotNull] string entityId, + [CanBeNull] Guid? tenantId = null, + CancellationToken cancellationToken = default) + { + if (!await _tagDefinitionStore.IsDefinedAsync(entityType)) + { + throw new EntityNotTaggableException(entityType); + } + + var entityTag = new EntityTag(tagId, entityId, tenantId); + + return await _entityTagRepository.InsertAsync(entityTag, cancellationToken: cancellationToken); + } + + public async Task RemoveTagFromEntityAsync( + [NotNull] Guid tagId, + [NotNull] string entityType, + [NotNull] string entityId, + [CanBeNull] Guid? tenantId = null, + CancellationToken cancellationToken = default) + { + var entityTag = await _entityTagRepository.FindAsync(tagId, entityId, tenantId, cancellationToken); + + await _entityTagRepository.DeleteAsync(entityTag); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagManager.cs new file mode 100644 index 0000000000..704568619a --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagManager.cs @@ -0,0 +1,24 @@ +using JetBrains.Annotations; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.CmsKit.Tags +{ + public interface IEntityTagManager + { + Task AddTagToEntityAsync( + [NotNull] Guid tagId, + [NotNull] string entityType, + [NotNull] string entityId, + [CanBeNull] Guid? tenantId = null, + CancellationToken cancellationToken = default); + + Task RemoveTagFromEntityAsync( + [NotNull] Guid tagId, + [NotNull] string entityType, + [NotNull] string entityId, + [CanBeNull] Guid? tenantId = null, + CancellationToken cancellationToken = default); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs index 6ec0fd4b44..f2ed160671 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/IEntityTagRepository.cs @@ -1,9 +1,17 @@ -using Volo.Abp.Domain.Repositories; +using JetBrains.Annotations; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; namespace Volo.CmsKit.Tags { public interface IEntityTagRepository : IBasicRepository { - + Task FindAsync( + [NotNull] Guid tagId, + [NotNull] string entityId, + [CanBeNull] Guid? tenantId, + CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagDefinitionStore.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagDefinitionStore.cs new file mode 100644 index 0000000000..1221e3660d --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagDefinitionStore.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Volo.CmsKit.Tags +{ + public interface ITagDefinitionStore + { + Task> GetTagDefinitionsAsync(); + + Task GetTagEntityTypeDefinitionsAsync([NotNull] string entityType); + + Task IsDefinedAsync([NotNull] string entityType); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagManager.cs index 943fd6af00..4b9cef5fb8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagManager.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/ITagManager.cs @@ -1,5 +1,6 @@ using JetBrains.Annotations; using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Services; @@ -26,5 +27,7 @@ namespace Volo.CmsKit.Tags [NotNull] string name, Guid? tenantId = null, CancellationToken cancellationToken = default); + + Task> GetTagDefinitionsAsync(CancellationToken cancellationToken = default); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagAlreadyExistException.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagAlreadyExistException.cs index 6cae0dde92..2d87668043 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagAlreadyExistException.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagAlreadyExistException.cs @@ -9,7 +9,7 @@ namespace Volo.CmsKit.Tags { public TagAlreadyExistException([NotNull] string entityType, [NotNull] string name) { - Code = CmsKitErrorCodes.TagAlreadyExist; + Code = CmsKitErrorCodes.Tags.TagAlreadyExist; WithData(nameof(Tag.EntityType), entityType); WithData(nameof(Tag.Name), name); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefinitionDictionary.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefinitionDictionary.cs new file mode 100644 index 0000000000..669dbbb315 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefinitionDictionary.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; +using System.Collections.Generic; +using Volo.Abp.Localization; + +namespace Volo.CmsKit.Tags +{ + public class TagEntityTypeDefinitionDictionary : Dictionary + { + public void AddOrReplace( + [NotNull] string entityType, + [CanBeNull] ILocalizableString displayName = null, + [CanBeNull] string createPolicy = null, + [CanBeNull] string updatePolicy = null, + [CanBeNull] string deletePolicy = null + ) + { + this[entityType] = new TagEntityTypeDefiniton(entityType, displayName, createPolicy, updatePolicy, deletePolicy); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs new file mode 100644 index 0000000000..fee8815b97 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagEntityTypeDefiniton.cs @@ -0,0 +1,31 @@ +using JetBrains.Annotations; +using Volo.Abp; +using Volo.Abp.Localization; +using Volo.CmsKit.Domain.Volo.CmsKit; + +namespace Volo.CmsKit.Tags +{ + public class TagEntityTypeDefiniton : PolicySpecifiedDefinition + { + public string EntityType { get; } + + [CanBeNull] + public virtual ILocalizableString DisplayName { get; } + + protected TagEntityTypeDefiniton() + { + } + + public TagEntityTypeDefiniton( + [NotNull] string entityType, + [CanBeNull] ILocalizableString displayName = null, + [CanBeNull] string createPolicy = null, + [CanBeNull] string updatePolicy = null, + [CanBeNull] string deletePolicy = null) : base(createPolicy, updatePolicy, deletePolicy) + { + EntityType = Check.NotNullOrWhiteSpace(entityType, nameof(entityType)); + + DisplayName = displayName; + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagManager.cs index 042b4e27a9..9c881226fa 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagManager.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Tags/TagManager.cs @@ -1,5 +1,8 @@ using JetBrains.Annotations; +using Microsoft.Extensions.Options; using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Volo.Abp; @@ -10,10 +13,14 @@ namespace Volo.CmsKit.Tags public class TagManager : DomainService, ITagManager { private readonly ITagRepository _tagRepository; + private readonly ITagDefinitionStore _tagDefinitionStore; - public TagManager(ITagRepository tagRepository) + public TagManager( + ITagRepository tagRepository, + ITagDefinitionStore tagDefinitionStore) { _tagRepository = tagRepository; + _tagDefinitionStore = tagDefinitionStore; } public async Task GetOrAddAsync( @@ -44,6 +51,11 @@ namespace Volo.CmsKit.Tags throw new TagAlreadyExistException(entityType, name); } + if (!await _tagDefinitionStore.IsDefinedAsync(entityType)) + { + throw new EntityNotTaggableException(entityType); + } + return await _tagRepository.InsertAsync( new Tag( id, @@ -70,5 +82,10 @@ namespace Volo.CmsKit.Tags return await _tagRepository.UpdateAsync(entity, cancellationToken: cancellationToken); } + + public Task> GetTagDefinitionsAsync(CancellationToken cancellationToken = default) + { + return _tagDefinitionStore.GetTagDefinitionsAsync(); + } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs index 2cc5e8a52a..60cf1332f8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs @@ -62,6 +62,10 @@ namespace Volo.CmsKit.EntityFrameworkCore b.HasIndex(x => new {x.TenantId, x.CreatorId, x.EntityType, x.EntityId, x.ReactionName}); }); } + else + { + builder.Ignore(); + } if (GlobalFeatureManager.Instance.IsEnabled()) { @@ -80,6 +84,10 @@ namespace Volo.CmsKit.EntityFrameworkCore b.HasIndex(x => new {x.TenantId, x.RepliedCommentId}); }); } + else + { + builder.Ignore(); + } if (GlobalFeatureManager.Instance.IsEnabled()) { @@ -96,6 +104,10 @@ namespace Volo.CmsKit.EntityFrameworkCore r.HasIndex(x => new { x.TenantId, x.EntityType, x.EntityId, x.CreatorId }); }); } + else + { + builder.Ignore(); + } if (GlobalFeatureManager.Instance.IsEnabled()) { @@ -112,6 +124,10 @@ namespace Volo.CmsKit.EntityFrameworkCore b.HasIndex(x => new { x.TenantId, x.EntityType, x.EntityId }); }); } + else + { + builder.Ignore(); + } if (GlobalFeatureManager.Instance.IsEnabled()) { @@ -145,50 +161,10 @@ namespace Volo.CmsKit.EntityFrameworkCore b.HasIndex(x => new { x.TenantId, x.EntityId, x.TagId }); }); } - - if (GlobalFeatureManager.Instance.IsEnabled()) + else { - builder.Entity(b => - { - b.ToTable(options.TablePrefix + "Pages", options.Schema); - - b.ConfigureByConvention(); - - b.Property(x => x.Title).IsRequired().HasMaxLength(PageConsts.MaxTitleLength); - b.Property(x => x.Url).IsRequired().HasMaxLength(PageConsts.MaxUrlLength); - b.Property(x => x.Description).HasMaxLength(PageConsts.MaxDescriptionLength); - - b.HasIndex(x => new { x.TenantId, x.Url }); - }); - } - - if (GlobalFeatureManager.Instance.IsEnabled()) - { - builder.Entity(b => - { - b.ToTable(options.TablePrefix + "Tags", options.Schema); - - b.ConfigureByConvention(); - - b.Property(x => x.EntityType).IsRequired().HasMaxLength(TagConsts.MaxEntityTypeLength); - b.Property(x => x.Name).IsRequired().HasMaxLength(TagConsts.MaxNameLength); - - b.HasIndex(x => new {x.TenantId, x.Name}); - }); - - builder.Entity(b => - { - b.ToTable(options.TablePrefix + "EntityTags", options.Schema); - - b.ConfigureByConvention(); - - b.HasKey(x => new {x.EntityId, x.TagId}); - - b.Property(x => x.EntityId).IsRequired(); - b.Property(x => x.TagId).IsRequired(); - - b.HasIndex(x => new {x.TenantId, x.EntityId, x.TagId}); - }); + builder.Ignore(); + builder.Ignore(); } if (GlobalFeatureManager.Instance.IsEnabled()) @@ -206,6 +182,10 @@ namespace Volo.CmsKit.EntityFrameworkCore b.HasIndex(x => new {x.TenantId, x.Url}); }); } + else + { + builder.Ignore(); + } } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs index cca61b802b..a82fb7005a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Tags/EfCoreEntityTagRepository.cs @@ -1,4 +1,8 @@ -using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using JetBrains.Annotations; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.CmsKit.EntityFrameworkCore; @@ -9,5 +13,18 @@ namespace Volo.CmsKit.Tags public EfCoreEntityTagRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) { } + + public Task FindAsync( + [NotNull] Guid tagId, + [NotNull] string entityId, + [CanBeNull] Guid? tenantId, + CancellationToken cancellationToken = default) + { + return base.FindAsync(x => + x.TagId == tagId && + x.EntityId == entityId && + x.TenantId == tenantId, + cancellationToken: cancellationToken); + } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Contents/MongoContentRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Contents/MongoContentRepository.cs index f42b0890e7..1068c352d5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Contents/MongoContentRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Contents/MongoContentRepository.cs @@ -54,7 +54,7 @@ namespace Volo.CmsKit.MongoDB.Contents public async Task ExistsAsync([NotNull] string entityType, [NotNull] string entityId, Guid? tenantId = null, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()).AnyAsync(x => + return await (await GetMongoQueryableAsync(cancellationToken)).AnyAsync(x => x.EntityType == entityType && x.EntityId == entityId && x.TenantId == tenantId, diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs index cc07afcd4b..af72da56c8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Pages/MongoPageRepository.cs @@ -63,7 +63,7 @@ namespace Volo.CmsKit.MongoDB.Pages public virtual async Task ExistsAsync(string url, CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync(GetCancellationToken(cancellationToken))).AnyAsync(x => x.Url == url, GetCancellationToken(cancellationToken)); + return await (await GetMongoQueryableAsync(cancellationToken)).AnyAsync(x => x.Url == url, GetCancellationToken(cancellationToken)); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs index 5ac1a4b69c..3f9e1814fc 100644 --- a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Tags/MongoEntityTagRepository.cs @@ -1,4 +1,8 @@ -using Volo.Abp.Domain.Repositories.MongoDB; +using JetBrains.Annotations; +using System; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.MongoDB; using Volo.CmsKit.Tags; @@ -9,5 +13,18 @@ namespace Volo.CmsKit.MongoDB.Tags public MongoEntityTagRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) { } + + public Task FindAsync( + [NotNull] Guid tagId, + [NotNull] string entityId, + [CanBeNull] Guid? tenantId, + CancellationToken cancellationToken = default) + { + return base.FindAsync(x => + x.TagId == tagId && + x.EntityId == entityId && + x.TenantId == tenantId, + cancellationToken: cancellationToken); + } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPageAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPageAppService.cs index d8ea8f1751..7363d32a3c 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPageAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application.Contracts/Volo/CmsKit/Public/Pages/IPageAppService.cs @@ -5,6 +5,6 @@ namespace Volo.CmsKit.Public.Pages { public interface IPageAppService { - Task GetByUrlAsync([NotNull] string url); + Task FindByUrlAsync([NotNull] string url); } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PageAppService.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PageAppService.cs index 38f4406cab..1e1d63bf71 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PageAppService.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Application/Volo/CmsKit/Public/Pages/PageAppService.cs @@ -12,9 +12,14 @@ namespace Volo.CmsKit.Public.Pages PageRepository = pageRepository; } - public virtual async Task GetByUrlAsync(string url) + public virtual async Task FindByUrlAsync(string url) { - var page = await PageRepository.GetByUrlAsync(url); + var page = await PageRepository.FindByUrlAsync(url); + + if (page == null) + { + return null; + } return ObjectMapper.Map(page); } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs index 45dda25ab5..a9c6cd70e4 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.HttpApi/Volo/CmsKit/Public/Pages/PagesPublicController.cs @@ -7,7 +7,7 @@ namespace Volo.CmsKit.Public.Pages [RemoteService(Name = CmsKitPublicRemoteServiceConsts.RemoteServiceName)] [Area("cms-kit")] [Route("api/cms-kit-public/comments")] - public class PagesPublicController + public class PagesPublicController : IPageAppService { protected readonly IPageAppService PageAppService; @@ -18,9 +18,9 @@ namespace Volo.CmsKit.Public.Pages [HttpGet] [Route("url/{url}")] - public Task GetByUrlAsync(string url) + public Task FindByUrlAsync(string url) { - return PageAppService.GetByUrlAsync(url); + return PageAppService.FindByUrlAsync(url); } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs index 0149722671..8cb5644256 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/CmsKitPublicWebModule.cs @@ -1,10 +1,13 @@ -using Microsoft.AspNetCore.Mvc.RazorPages; +using System.Linq; +using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AutoMapper; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Modularity; using Volo.Abp.UI.Navigation; using Volo.Abp.VirtualFileSystem; +using Volo.CmsKit.GlobalFeatures; using Volo.CmsKit.Localization; using Volo.CmsKit.Public.Web.Menus; using Volo.CmsKit.Web; @@ -56,7 +59,11 @@ namespace Volo.CmsKit.Public.Web Configure(options => { - //... + if (GlobalFeatureManager.Instance.IsEnabled()) + { + // TODO: Work on this route logic. Blocks some routes with this logic. + options.Conventions.AddPageRoute("/CmsKit/Pages/Index", "/{*pageUrl}"); + } }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml index c31edaaba8..73c7637b01 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml @@ -1,4 +1,4 @@ -@page "{pageUrl}" +@page "{*pageUrl}" @using Microsoft.AspNetCore.Mvc.Localization @using Volo.CmsKit.Localization @using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml.cs index 1d5dca0940..5443608845 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Pages/Index.cshtml.cs @@ -19,9 +19,16 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Pages PageAppService = pageAppService; } - public async Task OnGetAsync() + public async Task OnGetAsync() { - Page = await PageAppService.GetByUrlAsync(PageUrl); + Page = await PageAppService.FindByUrlAsync(PageUrl); + + if (Page == null) + { + return NotFound(); + } + + return Page(); } } } \ No newline at end of file diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/ContentViewComponent.cs b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/ContentViewComponent.cs index 129bff61cf..6a2a4e5a13 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/ContentViewComponent.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/ContentViewComponent.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Domain.Entities; using Volo.CmsKit.Public.Contents; using Volo.CmsKit.Web.Contents; @@ -25,18 +26,26 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Contents string entityType, string entityId) { - var content = await contentAppService.GetAsync(new GetContentInput + var content = string.Empty; + + try { - EntityId = entityId, - EntityType = entityType - }); + var contentDto = await contentAppService.GetAsync(new GetContentInput + { + EntityId = entityId, + EntityType = entityType + }); + + content = contentDto.Value; + } + catch (EntityNotFoundException e) + { + // ContentDto can be null, we will render empty content. + } var viewModel = new ContentViewModel { - EntityId = entityId, - EntityType = entityType, - ContentId = content.Id, - Rendered = await contentRenderer.RenderAsync(content.Value) + Value = await contentRenderer.RenderAsync(content) }; return View("~/Pages/CmsKit/Shared/Components/Contents/Default.cshtml", viewModel); @@ -44,12 +53,7 @@ namespace Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Contents public class ContentViewModel { - public Guid ContentId { get; set; } - public string EntityType { get; set; } - - public string EntityId { get; set; } - - public string Rendered { get; set; } + public string Value { get; set; } } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/Default.cshtml index 29f7bfa6a3..0f1ddedfe5 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/Default.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Contents/Default.cshtml @@ -1,3 +1,3 @@ @model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Contents.ContentViewComponent.ContentViewModel -@Html.Raw(Model.Rendered) +@Html.Raw(Model.Value) diff --git a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml index 28af196e39..e10fa0754a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml +++ b/modules/cms-kit/src/Volo.CmsKit.Public.Web/Pages/CmsKit/Shared/Components/Pages/Default.cshtml @@ -1,16 +1,16 @@ @addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap @using Microsoft.AspNetCore.Mvc.RazorPages +@using Volo.Abp.AspNetCore.Mvc.UI.Layout @using Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Contents @model Volo.CmsKit.Public.Web.Pages.CmsKit.Shared.Components.Pages.PageViewModel +@inject IPageLayout PageLayout + +@{ + PageLayout.Content.Title = Model.Title; +} - -

- @Model.Title -

-
- @await Component.InvokeAsync(typeof(ContentViewComponent), new diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentAdminAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentAdminAppService_Tests.cs index d8c5d62a61..5c7e5969f1 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentAdminAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Contents/ContentAdminAppService_Tests.cs @@ -38,6 +38,7 @@ namespace Volo.CmsKit.Contents result.ShouldNotBeNull(); result.Items.ShouldNotBeEmpty(); result.Items.Count.ShouldBe(4); + result.Items.All(x => x.ShouldBeOfType() != null); } [Fact] @@ -46,6 +47,8 @@ namespace Volo.CmsKit.Contents var result = await _service.GetAsync(_data.Content_1_Id); result.ShouldNotBeNull(); + result.ShouldBeOfType(); + result.ShouldNotBeNull(result.Value); } [Fact] @@ -125,5 +128,15 @@ namespace Volo.CmsKit.Contents await Should.NotThrowAsync(async () => await _service.DeleteAsync(_data.Content_2_Id)); } + + [Fact] + public async Task ShouldGetByEntityAsync() + { + var entity = await _service.GetAsync(_data.Content_1_EntityType, _data.Content_1_EntityId); + + entity.ShouldNotBeNull(); + entity.EntityId.ShouldBe(_data.Content_1_EntityId); + entity.EntityType.ShouldBe(_data.Content_1_EntityType); + } } } diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs index 290f02caff..c0943a1279 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Pages/PagePublicAppService_Tests.cs @@ -18,15 +18,20 @@ namespace Volo.CmsKit.Pages } [Fact] - public async Task ShouldGetByUrlAsync() + public async Task ShouldFindByUrlAsync() { - await Should.NotThrowAsync(async () => await _pageAppService.GetByUrlAsync(_data.Page_1_Url)); + var page = await _pageAppService.FindByUrlAsync(_data.Page_1_Url); + + page.ShouldNotBeNull(); + page.Title.ShouldBe(_data.Page_1_Title); } [Fact] public async Task ShouldNotGetByUrlAsync() { - await Should.ThrowAsync(async () => await _pageAppService.GetByUrlAsync("not-exist-url")); + var page = await _pageAppService.FindByUrlAsync("not-exist-url"); + + page.ShouldBeNull(); } } } \ No newline at end of file diff --git a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagAdminAppService_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagAdminAppService_Tests.cs index 4ad0d43468..b2128bb385 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagAdminAppService_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Application.Tests/Tags/TagAdminAppService_Tests.cs @@ -32,8 +32,8 @@ namespace Volo.CmsKit.Tags { var list = await _tagAdminAppService.CreateAsync(new TagCreateDto { - EntityType = "any_new_type", - Name = "1", + EntityType = _cmsKitTestData.EntityType1, + Name = "My First Tag", }); list.Id.ShouldNotBe(Guid.Empty); diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/CmsKitDomainTestModule.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/CmsKitDomainTestModule.cs index fc41440757..b5af93fc74 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/CmsKitDomainTestModule.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/CmsKitDomainTestModule.cs @@ -12,6 +12,5 @@ namespace Volo.CmsKit )] public class CmsKitDomainTestModule : AbpModule { - } } diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/EntityTagManager_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/EntityTagManager_Tests.cs new file mode 100644 index 0000000000..5a6144733c --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/EntityTagManager_Tests.cs @@ -0,0 +1,60 @@ +using Shouldly; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Guids; +using Xunit; + +namespace Volo.CmsKit.Tags +{ + public class EntityTagManager_Tests : CmsKitDomainTestBase + { + private readonly CmsKitTestData _cmsKitTestData; + private readonly IEntityTagManager _entityTagManager; + private readonly ITagRepository _tagRepository; + private readonly IGuidGenerator _guidGenerator; + + public EntityTagManager_Tests() + { + _cmsKitTestData = GetRequiredService(); + _entityTagManager = GetRequiredService(); + _tagRepository = GetRequiredService(); + _guidGenerator = GetRequiredService(); + } + + [Fact] + public async Task AddTagToEntityAsync_ShouldAdd_WhenEverythingCorrect() + { + var tag = await _tagRepository.InsertAsync(new Tag(_guidGenerator.Create(), _cmsKitTestData.EntityType1, "My Test Tag #1")); + + var entityTag = await _entityTagManager.AddTagToEntityAsync(tag.Id, _cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1); + + entityTag.ShouldNotBeNull(); + } + + [Fact] + public async Task AddTagToEntity_ShouldThrowNotTaggable_WithNotConfiguredEntityType() + { + var entityType = "Not.Configured.EntityType"; + + var exception = Should.Throw(async () => + await _entityTagManager.AddTagToEntityAsync(_cmsKitTestData.TagId_1, entityType, _cmsKitTestData.EntityId1) + ); + + exception.ShouldNotBeNull(); + exception.Data[nameof(Tag.EntityType)].ShouldBe(entityType); + } + + [Fact] + public async Task RemoveTagFromEntityAsync_ShouldRemove_WhenEverythingCorrect() + { + var tagToDelete = (await _tagRepository.GetAllRelatedTagsAsync(_cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1)) + .First(); + + await _entityTagManager.RemoveTagFromEntityAsync(tagToDelete.Id, tagToDelete.EntityType, _cmsKitTestData.EntityId1); + + var tags = await _tagRepository.GetAllRelatedTagsAsync(_cmsKitTestData.EntityType1, _cmsKitTestData.EntityId1); + + tags.ShouldNotContain(x => x.Id == tagToDelete.Id); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagManager_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagManager_Tests.cs index aa31b015cc..7d7416c8ce 100644 --- a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagManager_Tests.cs +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Tags/TagManager_Tests.cs @@ -22,7 +22,7 @@ namespace Volo.CmsKit.Tags [Fact] public async Task ShouldAddWhenGettingAsync() { - var newTagEntityType = "testEntity"; + var newTagEntityType = _cmsKitTestData.EntityType1; var newTagName = "test_tag_2123"; var doesExist = await _tagRepository.AnyAsync(newTagEntityType, newTagName); @@ -53,16 +53,28 @@ namespace Volo.CmsKit.Tags } [Fact] - public async Task ShouldInsert() + public async Task ShouldInsertAsync() { - var tag = await _tagManager.InsertAsync(Guid.NewGuid(), "test", "test"); + var tagName = "Freshly Created New Tag"; + var tag = await _tagManager.InsertAsync(Guid.NewGuid(), _cmsKitTestData.EntityType1, tagName); tag.ShouldNotBeNull(); - var doesExist = await _tagRepository.AnyAsync("test", "test"); + var doesExist = await _tagRepository.AnyAsync(_cmsKitTestData.EntityType1, tagName); doesExist.ShouldBeTrue(); } + [Fact] + public async Task ShouldntInsertWithUnconfiguredEntityTypeAsync() + { + var notConfiguredEntityType = "My.Namespace.SomeEntity"; + + var exception = await Should.ThrowAsync(async () => + await _tagManager.InsertAsync(Guid.NewGuid(), notConfiguredEntityType, "test")); + + exception.ShouldNotBeNull(); + exception.Data[nameof(Tag.EntityType)].ShouldBe(notConfiguredEntityType); + } [Fact] public async Task ShouldNotInsert() @@ -100,5 +112,15 @@ namespace Volo.CmsKit.Tags Should.Throw(async () => await _tagManager.UpdateAsync(tag.Id, newName)); } + + [Fact] + public async Task ShouldGetTagDefinitionsProperly_WithoutParameter() + { + var definitions = await _tagManager.GetTagDefinitionsAsync(); + + definitions.ShouldNotBeNull(); + definitions.Count.ShouldBeGreaterThan(1); + definitions.ShouldContain(x => x.EntityType == _cmsKitTestData.TagDefinition_1_EntityType); + } } } \ No newline at end of file diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs index 46a552641b..3dc77fe1fc 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.Extensions.Options; +using System; using System.Linq; using System.Threading.Tasks; using Volo.Abp.Data; @@ -26,9 +27,12 @@ namespace Volo.CmsKit private readonly IRatingRepository _ratingRepository; private readonly ICurrentTenant _currentTenant; private readonly IContentRepository _contentRepository; - private readonly IEntityTagRepository _entityTagRepository; + private readonly IEntityTagManager _entityTagManager; private readonly ITagManager _tagManager; private readonly IPageRepository _pageRepository; + private readonly IOptions _options; + private readonly IOptions _tagOptions; + public CmsKitDataSeedContributor( IGuidGenerator guidGenerator, ICmsUserRepository cmsUserRepository, @@ -36,11 +40,13 @@ namespace Volo.CmsKit ICommentRepository commentRepository, ReactionManager reactionManager, IRatingRepository ratingRepository, - ICurrentTenant currentTenant, + ICurrentTenant currentTenant, IContentRepository contentRepository, - ITagManager tagManager, - IEntityTagRepository entityTagRepository, - IPageRepository pageRepository) + ITagManager tagManager, + IEntityTagManager entityTagManager, + IPageRepository pageRepository, + IOptions options, + IOptions tagOptions) { _guidGenerator = guidGenerator; _cmsUserRepository = cmsUserRepository; @@ -51,14 +57,18 @@ namespace Volo.CmsKit _currentTenant = currentTenant; _contentRepository = contentRepository; _tagManager = tagManager; - _entityTagRepository = entityTagRepository; + _entityTagManager = entityTagManager; _pageRepository = pageRepository; + _options = options; + _tagOptions = tagOptions; } public async Task SeedAsync(DataSeedContext context) { using (_currentTenant.Change(context?.TenantId)) { + await ConfigureCmsKitOptionsAsync(); + await SeedUsersAsync(); await SeedCommentsAsync(); @@ -75,6 +85,17 @@ namespace Volo.CmsKit } } + private Task ConfigureCmsKitOptionsAsync() + { + _tagOptions.Value.EntityTypes.AddOrReplace(_cmsKitTestData.EntityType1); + _tagOptions.Value.EntityTypes.AddOrReplace(_cmsKitTestData.EntityType2); + _tagOptions.Value.EntityTypes.AddOrReplace(_cmsKitTestData.Content_1_EntityType); + _tagOptions.Value.EntityTypes.AddOrReplace(_cmsKitTestData.Content_2_EntityType); + _tagOptions.Value.EntityTypes.AddOrReplace(_cmsKitTestData.TagDefinition_1_EntityType); + + return Task.CompletedTask; + } + private async Task SeedUsersAsync() { await _cmsUserRepository.InsertAsync(new CmsUser(new UserData(_cmsKitTestData.User1Id, "user1", @@ -208,14 +229,14 @@ namespace Volo.CmsKit _cmsKitTestData.Content_1_EntityId, _cmsKitTestData.Content_1 ); - + var content2 = new Content( _cmsKitTestData.Content_2_Id, _cmsKitTestData.Content_2_EntityType, _cmsKitTestData.Content_2_EntityId, _cmsKitTestData.Content_2 ); - + var content3 = new Content( Guid.NewGuid(), "deleted_entity_type", @@ -233,18 +254,26 @@ namespace Volo.CmsKit private async Task SeedTagsAsync() { + var created1 = await _tagManager.InsertAsync(_cmsKitTestData.TagId_1, _cmsKitTestData.EntityType1, _cmsKitTestData.TagName_1); + + await _entityTagManager.AddTagToEntityAsync(created1.Id, created1.EntityType, _cmsKitTestData.EntityId1); + + var created2 = await _tagManager.InsertAsync(_cmsKitTestData.TagId_2, _cmsKitTestData.EntityType2, _cmsKitTestData.TagName_2); + + await _entityTagManager.AddTagToEntityAsync(created2.Id, created2.EntityType, _cmsKitTestData.EntityId2); + foreach (var tag in _cmsKitTestData.Content_1_Tags) { var tagEntity = await _tagManager.InsertAsync(_guidGenerator.Create(), _cmsKitTestData.Content_1_EntityType, tag); - await _entityTagRepository.InsertAsync(new EntityTag(tagEntity.Id, _cmsKitTestData.Content_1_EntityId)); + await _entityTagManager.AddTagToEntityAsync(tagEntity.Id, _cmsKitTestData.Content_1_EntityType, _cmsKitTestData.Content_1_EntityId); } - + foreach (var tag in _cmsKitTestData.Content_2_Tags) { var tagEntity = await _tagManager.InsertAsync(_guidGenerator.Create(), _cmsKitTestData.Content_2_EntityType, tag); - - await _entityTagRepository.InsertAsync(new EntityTag(tagEntity.Id, _cmsKitTestData.Content_2_EntityId)); + + await _entityTagManager.AddTagToEntityAsync(tagEntity.Id, _cmsKitTestData.Content_2_EntityType, _cmsKitTestData.Content_2_EntityId); } } @@ -252,10 +281,10 @@ namespace Volo.CmsKit { var page1 = new Page(_cmsKitTestData.Page_1_Id, _cmsKitTestData.Page_1_Title, _cmsKitTestData.Page_1_Url, _cmsKitTestData.Page_1_Description); var page1Content = new Content(_guidGenerator.Create(), nameof(Page), page1.Id.ToString(), _cmsKitTestData.Page_1_Content); - + await _pageRepository.InsertAsync(page1); await _contentRepository.InsertAsync(page1Content); - + var page2 = new Page(_cmsKitTestData.Page_2_Id, _cmsKitTestData.Page_2_Title, _cmsKitTestData.Page_2_Url, _cmsKitTestData.Page_2_Description); var page2Content = new Content(_guidGenerator.Create(), nameof(Page), page2.Id.ToString(), _cmsKitTestData.Page_2_Content); diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs index 7433759f40..4415cd7eb3 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs @@ -12,6 +12,7 @@ namespace Volo.CmsKit public Guid CommentWithChildId { get; } = Guid.NewGuid(); + public string EntityType1 { get; } = "EntityName1"; public string EntityType2 { get; } = "EntityName2"; @@ -59,5 +60,15 @@ namespace Volo.CmsKit public Guid Page_2_Id { get; } = Guid.NewGuid(); public string Page_2_Content => Content_2; + + public string TagDefinition_1_EntityType => "My.Namespace.CustomType"; + + public Guid TagId_1 { get; } = Guid.NewGuid(); + + public string TagName_1 => "Awesome"; + + public Guid TagId_2 { get; } = Guid.NewGuid(); + + public string TagName_2 => "News"; } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs index add77749b4..4893ab4114 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -7,10 +8,10 @@ namespace Volo.Docs.Projects { public interface IProjectRepository : IBasicRepository { - Task> GetListAsync(string sorting, int maxResultCount, int skipCount); + Task> GetListAsync(string sorting, int maxResultCount, int skipCount, CancellationToken cancellationToken = default); - Task GetByShortNameAsync(string shortName); + Task GetByShortNameAsync(string shortName, CancellationToken cancellationToken = default); - Task ShortNameExistsAsync(string shortName); + Task ShortNameExistsAsync(string shortName, CancellationToken cancellationToken = default); } -} \ No newline at end of file +} diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs index bf92a8a795..294241c992 100644 --- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Entities; @@ -18,20 +19,20 @@ namespace Volo.Docs.Projects { } - public async Task> GetListAsync(string sorting, int maxResultCount, int skipCount) + public async Task> GetListAsync(string sorting, int maxResultCount, int skipCount, CancellationToken cancellationToken = default) { var projects = await (await GetDbSetAsync()).OrderBy(sorting ?? "Id desc") .PageBy(skipCount, maxResultCount) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); return projects; } - public async Task GetByShortNameAsync(string shortName) + public async Task GetByShortNameAsync(string shortName, CancellationToken cancellationToken = default) { var normalizeShortName = NormalizeShortName(shortName); - var project = await (await GetDbSetAsync()).FirstOrDefaultAsync(p => p.ShortName == normalizeShortName); + var project = await (await GetDbSetAsync()).FirstOrDefaultAsync(p => p.ShortName == normalizeShortName, GetCancellationToken(cancellationToken)); if (project == null) { @@ -41,11 +42,11 @@ namespace Volo.Docs.Projects return project; } - public async Task ShortNameExistsAsync(string shortName) + public async Task ShortNameExistsAsync(string shortName, CancellationToken cancellationToken = default) { var normalizeShortName = NormalizeShortName(shortName); - return await (await GetDbSetAsync()).AnyAsync(x => x.ShortName == normalizeShortName); + return await (await GetDbSetAsync()).AnyAsync(x => x.ShortName == normalizeShortName, GetCancellationToken(cancellationToken)); } private string NormalizeShortName(string shortName) diff --git a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs index 91f1af9b2a..99aa5a99dd 100644 --- a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs @@ -9,6 +9,7 @@ using Volo.Abp.MongoDB; using Volo.Docs.MongoDB; using System.Linq; using System.Linq.Dynamic.Core; +using System.Threading; namespace Volo.Docs.Projects { @@ -19,20 +20,20 @@ namespace Volo.Docs.Projects { } - public async Task> GetListAsync(string sorting, int maxResultCount, int skipCount) + public async Task> GetListAsync(string sorting, int maxResultCount, int skipCount, CancellationToken cancellationToken = default) { - var projects = await (await GetMongoQueryableAsync()).OrderBy(sorting ?? "Id desc").As>() + var projects = await (await GetMongoQueryableAsync(cancellationToken)).OrderBy(sorting ?? "Id desc").As>() .PageBy>(skipCount, maxResultCount) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); return projects; } - public async Task GetByShortNameAsync(string shortName) + public async Task GetByShortNameAsync(string shortName, CancellationToken cancellationToken = default) { var normalizeShortName = NormalizeShortName(shortName); - var project = await (await GetMongoQueryableAsync()).FirstOrDefaultAsync(p => p.ShortName == normalizeShortName); + var project = await (await GetMongoQueryableAsync(cancellationToken)).FirstOrDefaultAsync(p => p.ShortName == normalizeShortName, GetCancellationToken(cancellationToken)); if (project == null) { @@ -42,11 +43,11 @@ namespace Volo.Docs.Projects return project; } - public async Task ShortNameExistsAsync(string shortName) + public async Task ShortNameExistsAsync(string shortName, CancellationToken cancellationToken = default) { var normalizeShortName = NormalizeShortName(shortName); - return await (await GetMongoQueryableAsync()).AnyAsync(x => x.ShortName == normalizeShortName); + return await (await GetMongoQueryableAsync(cancellationToken)).AnyAsync(x => x.ShortName == normalizeShortName, GetCancellationToken(cancellationToken)); } private string NormalizeShortName(string shortName) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs index f5b5c85da4..800424e901 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -7,10 +8,21 @@ namespace Volo.Abp.FeatureManagement { public interface IFeatureValueRepository : IBasicRepository { - Task FindAsync(string name, string providerName, string providerKey); + Task FindAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default); - Task> FindAllAsync(string name, string providerName, string providerKey); + Task> FindAllAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default); - Task> GetListAsync(string providerName, string providerKey); + Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default); } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs index be75e600d8..8256b08c2d 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; @@ -15,29 +16,38 @@ namespace Volo.Abp.FeatureManagement.EntityFrameworkCore { } - public virtual async Task FindAsync(string name, string providerName, string providerKey) + public virtual async Task FindAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .OrderBy(x => x.Id) - .FirstOrDefaultAsync( - s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey - ); + .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, GetCancellationToken(cancellationToken)); } - public async Task> FindAllAsync(string name, string providerName, string providerKey) + public async Task> FindAllAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .Where( s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey - ).ToListAsync(); + ).ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string providerName, string providerKey) + public virtual async Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .Where( s => s.ProviderName == providerName && s.ProviderKey == providerKey - ).ToListAsync(); + ).ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs index 21d4784f6e..8f5a659fd8 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -16,24 +17,35 @@ namespace Volo.Abp.FeatureManagement.MongoDB } - public virtual async Task FindAsync(string name, string providerName, string providerKey) + public virtual async Task FindAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) + return await (await GetMongoQueryableAsync(cancellationToken)) .OrderBy(x => x.Id) - .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey); + .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, GetCancellationToken(cancellationToken)); } - public async Task> FindAllAsync(string name, string providerName, string providerKey) + public async Task> FindAllAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) - .Where(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey).ToListAsync(); + return await (await GetMongoQueryableAsync(cancellationToken)) + .Where(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey).ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string providerName, string providerKey) + public virtual async Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) + return await (await GetMongoQueryableAsync(cancellationToken)) .Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml index f3080b7275..aa8d7313d9 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml @@ -26,7 +26,7 @@ { var featureGroups = Model.FeatureListResultDto.Groups; - + diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/ProfileAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/ProfileAppService.cs index 9097bbc144..ef3b5fd35d 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/ProfileAppService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/ProfileAppService.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; @@ -37,17 +38,26 @@ namespace Volo.Abp.Identity var user = await UserManager.GetByIdAsync(CurrentUser.GetId()); - if (await SettingProvider.IsTrueAsync(IdentitySettingNames.User.IsUserNameUpdateEnabled)) + if (!string.Equals(user.UserName, input.UserName, StringComparison.InvariantCultureIgnoreCase)) { - (await UserManager.SetUserNameAsync(user, input.UserName)).CheckErrors(); + if (await SettingProvider.IsTrueAsync(IdentitySettingNames.User.IsUserNameUpdateEnabled)) + { + (await UserManager.SetUserNameAsync(user, input.UserName)).CheckErrors(); + } } - if (await SettingProvider.IsTrueAsync(IdentitySettingNames.User.IsEmailUpdateEnabled)) + if (!string.Equals(user.Email, input.Email, StringComparison.InvariantCultureIgnoreCase)) { - (await UserManager.SetEmailAsync(user, input.Email)).CheckErrors(); + if (await SettingProvider.IsTrueAsync(IdentitySettingNames.User.IsEmailUpdateEnabled)) + { + (await UserManager.SetEmailAsync(user, input.Email)).CheckErrors(); + } } - (await UserManager.SetPhoneNumberAsync(user, input.PhoneNumber)).CheckErrors(); + if (!string.Equals(user.PhoneNumber, input.PhoneNumber, StringComparison.InvariantCultureIgnoreCase)) + { + (await UserManager.SetPhoneNumberAsync(user, input.PhoneNumber)).CheckErrors(); + } user.Name = input.Name; user.Surname = input.Surname; diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs index f71f8f76e9..cb1ffc9d8a 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs @@ -1,17 +1,50 @@ using System; -using System.Collections; using System.Linq; using System.Collections.Generic; using System.Globalization; +using System.Resources; using Microsoft.Extensions.Localization; +using Volo.Abp; using Volo.Abp.Identity; -using Volo.Abp.Localization; using Volo.Abp.Text.Formatting; namespace Microsoft.AspNetCore.Identity { public static class AbpIdentityResultExtensions { + private static readonly Dictionary IdentityStrings = new Dictionary(); + + static AbpIdentityResultExtensions() + { + var identityResourceManager = new ResourceManager("Microsoft.Extensions.Identity.Core.Resources", typeof(UserManager<>).Assembly); + var resourceSet = identityResourceManager.GetResourceSet(CultureInfo.InvariantCulture, true, false); + if (resourceSet == null) + { + throw new AbpException("Can't get the ResourceSet of Identity."); + } + + var iterator = resourceSet.GetEnumerator(); + while (true) + { + if (!iterator.MoveNext()) + { + break; + } + + var key = iterator.Key?.ToString(); + var value = iterator.Value?.ToString(); + if (key != null && value != null) + { + IdentityStrings.Add(key, value); + } + } + + if (!IdentityStrings.Any()) + { + throw new AbpException("ResourceSet values of Identity is empty."); + } + } + public static void CheckErrors(this IdentityResult identityResult) { if (identityResult.Succeeded) @@ -41,25 +74,19 @@ namespace Microsoft.AspNetCore.Identity } var error = identityResult.Errors.First(); - var key = $"Volo.Abp.Identity:{error.Code}"; + var englishString = IdentityStrings.GetOrDefault(error.Code); - using (CultureHelper.Use(CultureInfo.GetCultureInfo("en"))) + if (englishString == null) { - var englishLocalizedString = localizer[key]; - - if (englishLocalizedString.ResourceNotFound) - { - return Array.Empty(); - } - - if (FormattedStringValueExtracter.IsMatch(error.Description, englishLocalizedString.Value, - out var values)) - { - return values; - } - return Array.Empty(); } + + if (FormattedStringValueExtracter.IsMatch(error.Description, englishString, out var values)) + { + return values; + } + + return Array.Empty(); } public static string LocalizeErrors(this IdentityResult identityResult, IStringLocalizer localizer) @@ -85,16 +112,12 @@ namespace Microsoft.AspNetCore.Identity if (!localizedString.ResourceNotFound) { - using (CultureHelper.Use(CultureInfo.GetCultureInfo("en"))) + var englishString = IdentityStrings.GetOrDefault(error.Code); + if (englishString != null) { - var englishLocalizedString = localizer[key]; - if (!englishLocalizedString.ResourceNotFound) + if (FormattedStringValueExtracter.IsMatch(error.Description, englishString, out var values)) { - if (FormattedStringValueExtracter.IsMatch(error.Description, englishLocalizedString.Value, - out var values)) - { - return string.Format(localizedString.Value, values.Cast().ToArray()); - } + return string.Format(localizedString.Value, values.Cast().ToArray()); } } } diff --git a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentitySecurityLogRepository.cs b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentitySecurityLogRepository.cs index cc926db139..1ac54cfc82 100644 --- a/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentitySecurityLogRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentitySecurityLogRepository.cs @@ -44,7 +44,8 @@ namespace Volo.Abp.Identity.MongoDB userId, userName, clientId, - correlationId + correlationId, + cancellationToken ); return await query.OrderBy(sorting ?? nameof(IdentitySecurityLog.CreationTime) + " desc") @@ -74,7 +75,8 @@ namespace Volo.Abp.Identity.MongoDB userId, userName, clientId, - correlationId + correlationId, + cancellationToken ); return await query.As>() @@ -98,9 +100,10 @@ namespace Volo.Abp.Identity.MongoDB Guid? userId = null, string userName = null, string clientId = null, - string correlationId = null) + string correlationId = null, + CancellationToken cancellationToken = default) { - return (await GetMongoQueryableAsync()) + return (await GetMongoQueryableAsync(cancellationToken)) .WhereIf(startTime.HasValue, securityLog => securityLog.CreationTime >= startTime.Value) .WhereIf(endTime.HasValue, securityLog => securityLog.CreationTime < endTime.Value.AddDays(1).Date) .WhereIf(!applicationName.IsNullOrWhiteSpace(), diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj index 1771b68a5c..3413ff430c 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo.Abp.Identity.Domain.Tests.csproj @@ -19,6 +19,12 @@ + + + + + + diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs index ccfbb85359..a6228a7cd6 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityDomainTestModule.cs @@ -2,9 +2,12 @@ using Volo.Abp.Authorization.Permissions; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Identity.EntityFrameworkCore; +using Volo.Abp.Identity.Localization; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.Identity; using Volo.Abp.Threading; +using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.Identity { @@ -21,6 +24,18 @@ namespace Volo.Abp.Identity { options.AutoEventSelectors.Add(); }); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/Volo/Abp/Identity/LocalizationExtensions"); + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs index cc349fd3f3..306ea16c91 100644 --- a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/AbpIdentityResultException_Tests.cs @@ -28,10 +28,18 @@ namespace Volo.Abp.Identity using (CultureHelper.Use("tr")) { var localizeMessage = exception.LocalizeMessage(new LocalizationContext(ServiceProvider)); - + localizeMessage.ShouldContain("Şifre en az 6 karakter uzunluğunda olmalı."); localizeMessage.ShouldContain("Şifre en az bir sayı ya da harf olmayan karakter içermeli."); } + + using (CultureHelper.Use("en")) + { + var localizeMessage = exception.LocalizeMessage(new LocalizationContext(ServiceProvider)); + + localizeMessage.ShouldContain("Password length must be greater than 6 characters."); + localizeMessage.ShouldContain("Password must contain at least one non-alphanumeric character."); + } } } } diff --git a/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/LocalizationExtensions/en.json b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/LocalizationExtensions/en.json new file mode 100644 index 0000000000..e8f68fb950 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.Domain.Tests/Volo/Abp/Identity/LocalizationExtensions/en.json @@ -0,0 +1,7 @@ +{ + "culture": "en", + "texts": { + "Volo.Abp.Identity:PasswordTooShort": "Password length must be greater than {0} characters.", + "Volo.Abp.Identity:PasswordRequiresNonAlphanumeric": "Password must contain at least one non-alphanumeric character." + } +} diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs index 2d4728a7e1..9dd4f24af6 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.MongoDB/Volo/Abp/IdentityServer/MongoDB/MongoPersistedGrantRepository.cs @@ -21,7 +21,7 @@ namespace Volo.Abp.IdentityServer.MongoDB public async Task> GetListAsync(string subjectId, string sessionId, string clientId, string type, bool includeDetails = false, CancellationToken cancellationToken = default) { - return await (await FilterAsync(subjectId, sessionId, clientId, type)) + return await (await FilterAsync(subjectId, sessionId, clientId, type, cancellationToken)) .ToListAsync(GetCancellationToken(cancellationToken)); } @@ -57,7 +57,7 @@ namespace Volo.Abp.IdentityServer.MongoDB string type = null, CancellationToken cancellationToken = default) { - var persistedGrants = await (await FilterAsync(subjectId, sessionId, clientId, type)) + var persistedGrants = await (await FilterAsync(subjectId, sessionId, clientId, type, cancellationToken)) .ToListAsync(GetCancellationToken(cancellationToken)); foreach (var persistedGrant in persistedGrants) @@ -86,9 +86,10 @@ namespace Volo.Abp.IdentityServer.MongoDB string subjectId, string sessionId, string clientId, - string type) + string type, + CancellationToken cancellationToken = default) { - return (await GetMongoQueryableAsync()) + return (await GetMongoQueryableAsync(cancellationToken)) .WhereIf>(!subjectId.IsNullOrWhiteSpace(), x => x.SubjectId == subjectId) .WhereIf>(!sessionId.IsNullOrWhiteSpace(), x => x.SessionId == sessionId) .WhereIf>(!clientId.IsNullOrWhiteSpace(), x => x.ClientId == clientId) diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs deleted file mode 100644 index 4b16d7de65..0000000000 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System.Threading.Tasks; -using IdentityServer4.Models; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Guids; -using Volo.Abp.IdentityServer.ApiResources; -using Volo.Abp.IdentityServer.ApiScopes; -using Volo.Abp.IdentityServer.Clients; -using Volo.Abp.IdentityServer.Grants; -using Volo.Abp.IdentityServer.IdentityResources; -using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource; -using ApiScope = Volo.Abp.IdentityServer.ApiScopes.ApiScope; -using Client = Volo.Abp.IdentityServer.Clients.Client; -using IdentityResource = Volo.Abp.IdentityServer.IdentityResources.IdentityResource; -using PersistedGrant = Volo.Abp.IdentityServer.Grants.PersistedGrant; - -namespace Volo.Abp.IdentityServer -{ - //TODO: There are two data builders (see AbpIdentityServerTestDataBuilder in Volo.Abp.IdentityServer.TestBase). It should be somehow unified! - - public class AbpIdentityServerTestDataBuilder : ITransientDependency - { - private readonly IGuidGenerator _guidGenerator; - private readonly IClientRepository _clientRepository; - private readonly IPersistentGrantRepository _persistentGrantRepository; - private readonly IApiResourceRepository _apiResourceRepository; - private readonly IApiScopeRepository _apiScopeRepository; - private readonly IIdentityResourceRepository _identityResourceRepository; - - public AbpIdentityServerTestDataBuilder( - IClientRepository clientRepository, - IGuidGenerator guidGenerator, - IPersistentGrantRepository persistentGrantRepository, - IApiResourceRepository apiResourceRepository, - IIdentityResourceRepository identityResourceRepository, - IApiScopeRepository apiScopeRepository) - { - _clientRepository = clientRepository; - _guidGenerator = guidGenerator; - _persistentGrantRepository = persistentGrantRepository; - _apiResourceRepository = apiResourceRepository; - _identityResourceRepository = identityResourceRepository; - _apiScopeRepository = apiScopeRepository; - } - - public async Task BuildAsync() - { - await AddApiResources(); - await AddApiScopes(); - await AddIdentityResources(); - await AddClients(); - await AddPersistentGrants(); - } - - private async Task AddApiResources() - { - var apiResource = new ApiResource(_guidGenerator.Create(), "Test-ApiResource-Name-1") - { - Enabled = true, - Description = "Test-ApiResource-Description-1", - DisplayName = "Test-ApiResource-DisplayName-1" - }; - - apiResource.AddSecret("secret".Sha256()); - apiResource.AddScope("Test-ApiResource-ApiScope-Name-1"); - apiResource.AddScope("Test-ApiResource-ApiScope-DisplayName-1"); - apiResource.AddUserClaim("Test-ApiResource-Claim-Type-1"); - - await _apiResourceRepository.InsertAsync(apiResource); - } - - private async Task AddApiScopes() - { - var apiScope = new ApiScope(_guidGenerator.Create(), "Test-ApiScope-Name-1"); - - apiScope.AddUserClaim("Test-ApiScope-Claim-Type-1"); - await _apiScopeRepository.InsertAsync(apiScope); - } - - private async Task AddIdentityResources() - { - var identityResource = new IdentityResource(_guidGenerator.Create(), "Test-Identity-Resource-Name-1") - { - Description = "Test-Identity-Resource-Description-1", - DisplayName = "Test-Identity-Resource-DisplayName-1", - Required = true, - Emphasize = true - }; - - identityResource.AddUserClaim("Test-Identity-Resource-1-IdentityClaim-Type-1"); - - await _identityResourceRepository.InsertAsync(identityResource); - } - - private async Task AddClients() - { - var client42 = new Client(_guidGenerator.Create(), "42") - { - ProtocolType = "TestProtocol-42" - }; - - client42.AddCorsOrigin("Origin1"); - - client42.AddScope("Test-ApiScope-Name-1"); - - await _clientRepository.InsertAsync(client42); - } - - private async Task AddPersistentGrants() - { - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) - { - Key = "38", - ClientId = "TestClientId-38", - Type = "TestType-38", - SubjectId = "TestSubject", - Data = "TestData-38" - }); - - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) - { - Key = "37", - ClientId = "TestClientId-37", - Type = "TestType-37", - SubjectId = "TestSubject", - Data = "TestData-37" - }); - - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) - { - Key = "36", - ClientId = "TestClientId-X", - Type = "TestType-36", - SubjectId = "TestSubject-X", - Data = "TestData-36" - }); - - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) - { - Key = "35", - ClientId = "TestClientId-X", - Type = "TestType-35", - SubjectId = "TestSubject-X", - Data = "TestData-35" - }); - } - - } -} diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs index 5abc8ad2a2..0c3dd27e53 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestEntityFrameworkCoreModule.cs @@ -1,4 +1,4 @@ -using Microsoft.Data.Sqlite; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; @@ -33,11 +33,6 @@ namespace Volo.Abp.IdentityServer }); } - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - SeedTestData(context); - } - private static SqliteConnection CreateDatabaseAndGetConnection() { var connection = new SqliteConnection("Data Source=:memory:"); @@ -53,15 +48,5 @@ namespace Volo.Abp.IdentityServer return connection; } - - private static void SeedTestData(ApplicationInitializationContext context) - { - using (var scope = context.ServiceProvider.CreateScope()) - { - AsyncHelper.RunSync(() => scope.ServiceProvider - .GetRequiredService() - .BuildAsync()); - } - } } -} \ No newline at end of file +} diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs index 436bef1246..721caa1a7f 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.TestBase/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; using Volo.Abp.Guids; @@ -11,6 +10,13 @@ using Volo.Abp.IdentityServer.Devices; using Volo.Abp.IdentityServer.Grants; using Volo.Abp.IdentityServer.IdentityResources; using Volo.Abp.Timing; +using IdentityServer4.Models; +using ApiResource = Volo.Abp.IdentityServer.ApiResources.ApiResource; +using ApiScope = Volo.Abp.IdentityServer.ApiScopes.ApiScope; +using Client = Volo.Abp.IdentityServer.Clients.Client; +using ClientClaim = Volo.Abp.IdentityServer.Clients.ClientClaim; +using IdentityResource = Volo.Abp.IdentityServer.IdentityResources.IdentityResource; +using PersistedGrant = Volo.Abp.IdentityServer.Grants.PersistedGrant; namespace Volo.Abp.IdentityServer { @@ -18,6 +24,7 @@ namespace Volo.Abp.IdentityServer { private readonly IGuidGenerator _guidGenerator; private readonly IApiResourceRepository _apiResourceRepository; + private readonly IApiScopeRepository _apiScopeRepository; private readonly IClientRepository _clientRepository; private readonly IIdentityResourceRepository _identityResourceRepository; private readonly IIdentityClaimTypeRepository _identityClaimTypeRepository; @@ -29,6 +36,7 @@ namespace Volo.Abp.IdentityServer public AbpIdentityServerTestDataBuilder( IGuidGenerator guidGenerator, IApiResourceRepository apiResourceRepository, + IApiScopeRepository apiScopeRepository, IClientRepository clientRepository, IIdentityResourceRepository identityResourceRepository, IIdentityClaimTypeRepository identityClaimTypeRepository, @@ -40,125 +48,91 @@ namespace Volo.Abp.IdentityServer _testData = testData; _guidGenerator = guidGenerator; _apiResourceRepository = apiResourceRepository; + _apiScopeRepository = apiScopeRepository; _clientRepository = clientRepository; _identityResourceRepository = identityResourceRepository; _identityClaimTypeRepository = identityClaimTypeRepository; _persistentGrantRepository = persistentGrantRepository; - _clock = clock; _deviceFlowCodesRepository = deviceFlowCodesRepository; + _clock = clock; } public async Task BuildAsync() { - await AddDeviceFlowCodes(); - await AddPersistedGrants(); - await AddIdentityResources(); + await AddApiScopes(); await AddApiResources(); + await AddIdentityResources(); await AddClients(); + await AddPersistentGrants(); + await AddDeviceFlowCodes(); + await AddPersistedGrants(); await AddClaimTypes(); } - private async Task AddDeviceFlowCodes() + private async Task AddApiScopes() { - await _deviceFlowCodesRepository.InsertAsync( - new DeviceFlowCodes(_guidGenerator.Create()) - { - ClientId = "c1", - DeviceCode = "DeviceCode1", - Expiration = _clock.Now.AddDays(1), - Data = "{\"Lifetime\":\"42\"}", - UserCode = "DeviceFlowCodesUserCode1", - SubjectId = "DeviceFlowCodesSubjectId1" - } - ); - - await _deviceFlowCodesRepository.InsertAsync( - new DeviceFlowCodes(_guidGenerator.Create()) - { - ClientId = "c1", - DeviceCode = "DeviceCode2", - Expiration = _clock.Now.AddDays(-1), - Data = "", - UserCode = "DeviceFlowCodesUserCode2", - SubjectId = "DeviceFlowCodesSubjectId2" - } - ); + var apiScope = new ApiScope(_guidGenerator.Create(), "Test-ApiScope-Name-1"); + apiScope.AddUserClaim("Test-ApiScope-Claim-Type-1"); + await _apiScopeRepository.InsertAsync(apiScope); } - private async Task AddPersistedGrants() + private async Task AddApiResources() { - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) - { - Key = "PersistedGrantKey1", - SubjectId = "PersistedGrantSubjectId1", - SessionId = "PersistedGrantSessionId1", - ClientId = "PersistedGrantClientId1", - Type = "PersistedGrantType1", - Data = "" - }); + var apiResource = new ApiResource(_testData.ApiResource1Id, "NewApiResource1"); + apiResource.Description = nameof(apiResource.Description); + apiResource.DisplayName = nameof(apiResource.DisplayName); - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) - { - Key = "PersistedGrantKey2", - SubjectId = "PersistedGrantSubjectId2", - ClientId = "c1", - Type = "c1type", - Data = "" - }); + apiResource.AddScope(nameof(ApiResourceScope.Scope)); + apiResource.AddUserClaim(nameof(ApiResourceClaim.Type)); + apiResource.AddSecret(nameof(ApiResourceSecret.Value)); - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) - { - Key = "PersistedGrantKey3", - SubjectId = "PersistedGrantSubjectId3", - ClientId = "c1", - Type = "c1type", - Data = "", - Expiration = _clock.Now.AddDays(1), - }); + await _apiResourceRepository.InsertAsync(apiResource); + await _apiResourceRepository.InsertAsync(new ApiResource(_guidGenerator.Create(), "NewApiResource2")); + await _apiResourceRepository.InsertAsync(new ApiResource(_guidGenerator.Create(), "NewApiResource3")); - await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + var apiResource2 = new ApiResource(_guidGenerator.Create(), "Test-ApiResource-Name-1") { - Key = "PersistedGrantKey_Expired1", - SubjectId = "PersistedGrantSubjectId_Expired1", - ClientId = "c1", - Type = "c1type", - Data = "", - Expiration = _clock.Now.AddDays(-1) - }); + Enabled = true, + Description = "Test-ApiResource-Description-1", + DisplayName = "Test-ApiResource-DisplayName-1" + }; + + apiResource2.AddSecret("secret".Sha256()); + apiResource2.AddScope("Test-ApiResource-ApiScope-Name-1"); + apiResource2.AddScope("Test-ApiResource-ApiScope-DisplayName-1"); + apiResource2.AddUserClaim("Test-ApiResource-Claim-Type-1"); + + await _apiResourceRepository.InsertAsync(apiResource2); } private async Task AddIdentityResources() { - var identityResource = new IdentityResource(_testData.IdentityResource1Id, "NewIdentityResource1") + var identityResource1 = new IdentityResource(_testData.IdentityResource1Id, "NewIdentityResource1") { Description = nameof(Client.Description), DisplayName = nameof(IdentityResource.DisplayName) }; - identityResource.AddUserClaim(nameof(ApiResourceClaim.Type)); + identityResource1.AddUserClaim(nameof(ApiResourceClaim.Type)); - await _identityResourceRepository.InsertAsync(identityResource); + await _identityResourceRepository.InsertAsync(identityResource1); await _identityResourceRepository.InsertAsync(new IdentityResource(_guidGenerator.Create(), "NewIdentityResource2")); await _identityResourceRepository.InsertAsync(new IdentityResource(_guidGenerator.Create(), "NewIdentityResource3")); - } - private async Task AddApiResources() - { - var apiResource = new ApiResource(_testData.ApiResource1Id, "NewApiResource1"); - apiResource.Description = nameof(apiResource.Description); - apiResource.DisplayName = nameof(apiResource.DisplayName); - - apiResource.AddScope(nameof(ApiResourceScope.Scope)); - apiResource.AddUserClaim(nameof(ApiResourceClaim.Type)); - apiResource.AddSecret(nameof(ApiResourceSecret.Value)); + var identityResource2 = new IdentityResource(_guidGenerator.Create(), "Test-Identity-Resource-Name-1") + { + Description = "Test-Identity-Resource-Description-1", + DisplayName = "Test-Identity-Resource-DisplayName-1", + Required = true, + Emphasize = true + }; - await _apiResourceRepository.InsertAsync(apiResource); - await _apiResourceRepository.InsertAsync(new ApiResource(_guidGenerator.Create(), "NewApiResource2")); - await _apiResourceRepository.InsertAsync(new ApiResource(_guidGenerator.Create(), "NewApiResource3")); + identityResource2.AddUserClaim("Test-Identity-Resource-1-IdentityClaim-Type-1"); + await _identityResourceRepository.InsertAsync(identityResource2); } - private async Task AddClients() + private async Task AddClients() { var client = new Client(_testData.Client1Id, "ClientId1") { @@ -184,6 +158,125 @@ namespace Volo.Abp.IdentityServer await _clientRepository.InsertAsync(new Client(_guidGenerator.Create(), "ClientId2")); await _clientRepository.InsertAsync(new Client(_guidGenerator.Create(), "ClientId3")); + + + var client42 = new Client(_guidGenerator.Create(), "42") + { + ProtocolType = "TestProtocol-42" + }; + + client42.AddCorsOrigin("Origin1"); + client42.AddScope("Test-ApiScope-Name-1"); + await _clientRepository.InsertAsync(client42); + } + + private async Task AddPersistentGrants() + { + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "38", + ClientId = "TestClientId-38", + Type = "TestType-38", + SubjectId = "TestSubject", + Data = "TestData-38" + }); + + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "37", + ClientId = "TestClientId-37", + Type = "TestType-37", + SubjectId = "TestSubject", + Data = "TestData-37" + }); + + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "36", + ClientId = "TestClientId-X", + Type = "TestType-36", + SubjectId = "TestSubject-X", + Data = "TestData-36" + }); + + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "35", + ClientId = "TestClientId-X", + Type = "TestType-35", + SubjectId = "TestSubject-X", + Data = "TestData-35" + }); + } + + private async Task AddPersistedGrants() + { + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "PersistedGrantKey1", + SubjectId = "PersistedGrantSubjectId1", + SessionId = "PersistedGrantSessionId1", + ClientId = "PersistedGrantClientId1", + Type = "PersistedGrantType1", + Data = "" + }); + + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "PersistedGrantKey2", + SubjectId = "PersistedGrantSubjectId2", + ClientId = "c1", + Type = "c1type", + Data = "" + }); + + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "PersistedGrantKey3", + SubjectId = "PersistedGrantSubjectId3", + ClientId = "c1", + Type = "c1type", + Data = "", + Expiration = _clock.Now.AddDays(1), + }); + + await _persistentGrantRepository.InsertAsync(new PersistedGrant(_guidGenerator.Create()) + { + Key = "PersistedGrantKey_Expired1", + SubjectId = "PersistedGrantSubjectId_Expired1", + ClientId = "c1", + Type = "c1type", + Data = "", + Expiration = _clock.Now.AddDays(-1) + }); + } + + private async Task AddDeviceFlowCodes() + { + await _deviceFlowCodesRepository.InsertAsync( + new DeviceFlowCodes(_guidGenerator.Create()) + { + ClientId = "c1", + DeviceCode = "DeviceCode1", + Expiration = _clock.Now.AddDays(1), + Data = "{\"Lifetime\":\"42\"}", + UserCode = "DeviceFlowCodesUserCode1", + SubjectId = "DeviceFlowCodesSubjectId1" + } + ); + + await _deviceFlowCodesRepository.InsertAsync( + new DeviceFlowCodes(_guidGenerator.Create()) + { + ClientId = "c1", + DeviceCode = "DeviceCode2", + Expiration = _clock.Now.AddDays(-1), + Data = "", + UserCode = "DeviceFlowCodesUserCode2", + SubjectId = "DeviceFlowCodesSubjectId2" + } + ); + } private async Task AddClaimTypes() diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/ISettingRepository.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/ISettingRepository.cs index a1e40f3a14..ee8cfa64a8 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/ISettingRepository.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/ISettingRepository.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -7,10 +8,21 @@ namespace Volo.Abp.SettingManagement { public interface ISettingRepository : IBasicRepository { - Task FindAsync(string name, string providerName, string providerKey); + Task FindAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default); - Task> GetListAsync(string providerName, string providerKey); + Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default); - Task> GetListAsync(string[] names, string providerName, string providerKey); + Task> GetListAsync( + string[] names, + string providerName, + string providerKey, + CancellationToken cancellationToken = default); } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/EfCoreSettingRepository.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/EfCoreSettingRepository.cs index e90d2fc63a..4bab46315a 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/EfCoreSettingRepository.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.EntityFrameworkCore/Volo/Abp/SettingManagement/EntityFrameworkCore/EfCoreSettingRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; @@ -8,36 +9,48 @@ using Volo.Abp.EntityFrameworkCore; namespace Volo.Abp.SettingManagement.EntityFrameworkCore { - public class EfCoreSettingRepository : EfCoreRepository, ISettingRepository + public class EfCoreSettingRepository : EfCoreRepository, + ISettingRepository { public EfCoreSettingRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) { } - public virtual async Task FindAsync(string name, string providerName, string providerKey) + public virtual async Task FindAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .OrderBy(x => x.Id) .FirstOrDefaultAsync( - s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey - ); + s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, + GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string providerName, string providerKey) + public virtual async Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .Where( s => s.ProviderName == providerName && s.ProviderKey == providerKey - ).ToListAsync(); + ).ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string[] names, string providerName, string providerKey) + public virtual async Task> GetListAsync( + string[] names, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .Where( s => names.Contains(s.Name) && s.ProviderName == providerName && s.ProviderKey == providerKey - ).ToListAsync(); + ).ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/MongoSettingRepository.cs b/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/MongoSettingRepository.cs index e81e76dfcc..aef9624a4d 100644 --- a/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/MongoSettingRepository.cs +++ b/modules/setting-management/src/Volo.Abp.SettingManagement.MongoDB/Volo/Abp/SettingManagement/MongoDB/MongoSettingRepository.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; @@ -9,33 +10,46 @@ using Volo.Abp.MongoDB; namespace Volo.Abp.SettingManagement.MongoDB { - public class MongoSettingRepository : MongoDbRepository, ISettingRepository + public class MongoSettingRepository : MongoDbRepository, + ISettingRepository { public MongoSettingRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) { - } - public virtual async Task FindAsync(string name, string providerName, string providerKey) + public virtual async Task FindAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) + return await (await GetMongoQueryableAsync(cancellationToken)) .OrderBy(x => x.Id) - .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey); + .FirstOrDefaultAsync( + s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, + GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string providerName, string providerKey) + public virtual async Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) + return await (await GetMongoQueryableAsync(cancellationToken)) .Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } - public virtual async Task> GetListAsync(string[] names, string providerName, string providerKey) + public virtual async Task> GetListAsync( + string[] names, + string providerName, + string providerKey, + CancellationToken cancellationToken = default) { - return await (await GetMongoQueryableAsync()) + return await (await GetMongoQueryableAsync(cancellationToken)) .Where(s => names.Contains(s.Name) && s.ProviderName == providerName && s.ProviderKey == providerKey) - .ToListAsync(); + .ToListAsync(GetCancellationToken(cancellationToken)); } } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj index dc168af444..5de64b7a1d 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo.Abp.TenantManagement.Domain.csproj @@ -21,6 +21,7 @@ + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs index 764e9f7209..076c8b7a67 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/AbpTenantManagementDomainModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; +using Volo.Abp.Caching; using Volo.Abp.Data; using Volo.Abp.Domain; using Volo.Abp.Domain.Entities.Events.Distributed; @@ -16,6 +17,7 @@ namespace Volo.Abp.TenantManagement [DependsOn(typeof(AbpDataModule))] [DependsOn(typeof(AbpDddDomainModule))] [DependsOn(typeof(AbpAutoMapperModule))] + [DependsOn(typeof(AbpCachingModule))] public class AbpTenantManagementDomainModule : AbpModule { private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantCacheItem.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantCacheItem.cs new file mode 100644 index 0000000000..94f898bbe5 --- /dev/null +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantCacheItem.cs @@ -0,0 +1,36 @@ +using System; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.TenantManagement +{ + [Serializable] + [IgnoreMultiTenancy] + public class TenantCacheItem + { + private const string CacheKeyFormat = "i:{0},n:{1}"; + + public TenantConfiguration Value { get; set; } + + public TenantCacheItem() + { + + } + + public TenantCacheItem(TenantConfiguration value) + { + Value = value; + } + + public static string CalculateCacheKey(Guid? id, string name) + { + if (id == null && name.IsNullOrWhiteSpace()) + { + throw new AbpException("Both id and name can't be invalid."); + } + + return string.Format(CacheKeyFormat, + id?.ToString() ?? "null", + (name.IsNullOrWhiteSpace() ? "null" : name)); + } + } +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantCacheItemInvalidator.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantCacheItemInvalidator.cs new file mode 100644 index 0000000000..0416dd8df2 --- /dev/null +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantCacheItemInvalidator.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; + +namespace Volo.Abp.TenantManagement +{ + public class TenantCacheItemInvalidator : ILocalEventHandler>, ITransientDependency + { + protected IDistributedCache Cache { get; } + + public TenantCacheItemInvalidator(IDistributedCache cache) + { + Cache = cache; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + await Cache.RemoveAsync(TenantCacheItem.CalculateCacheKey(eventData.Entity.Id, null)); + await Cache.RemoveAsync(TenantCacheItem.CalculateCacheKey(null, eventData.Entity.Name)); + } + } +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantStore.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantStore.cs index 00b9ef0963..e43e8a86fa 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantStore.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain/Volo/Abp/TenantManagement/TenantStore.cs @@ -1,85 +1,137 @@ using System; using System.Threading.Tasks; +using JetBrains.Annotations; +using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; namespace Volo.Abp.TenantManagement { - //TODO: This class should use caching instead of querying everytime! - public class TenantStore : ITenantStore, ITransientDependency { protected ITenantRepository TenantRepository { get; } protected IObjectMapper ObjectMapper { get; } protected ICurrentTenant CurrentTenant { get; } + protected IDistributedCache Cache { get; } public TenantStore( ITenantRepository tenantRepository, IObjectMapper objectMapper, - ICurrentTenant currentTenant) + ICurrentTenant currentTenant, + IDistributedCache cache) { TenantRepository = tenantRepository; ObjectMapper = objectMapper; CurrentTenant = currentTenant; + Cache = cache; } public virtual async Task FindAsync(string name) { - using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! - { - var tenant = await TenantRepository.FindByNameAsync(name); - if (tenant == null) - { - return null; - } - - return ObjectMapper.Map(tenant); - } + return (await GetCacheItemAsync(null, name)).Value; } public virtual async Task FindAsync(Guid id) { - using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! - { - var tenant = await TenantRepository.FindAsync(id); - if (tenant == null) - { - return null; - } - - return ObjectMapper.Map(tenant); - } + return (await GetCacheItemAsync(id, null)).Value; } [Obsolete("Use FindAsync method.")] public virtual TenantConfiguration Find(string name) { - using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! + return (GetCacheItem(null, name)).Value; + } + + [Obsolete("Use FindAsync method.")] + public virtual TenantConfiguration Find(Guid id) + { + return (GetCacheItem(id, null)).Value; + } + + protected virtual async Task GetCacheItemAsync(Guid? id, string name) + { + var cacheKey = CalculateCacheKey(id, name); + + var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true); + if (cacheItem != null) + { + return cacheItem; + } + + if (id.HasValue) { - var tenant = TenantRepository.FindByName(name); - if (tenant == null) + using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! { - return null; + var tenant = await TenantRepository.FindAsync(id.Value); + return await SetCacheAsync(cacheKey, tenant); } + } - return ObjectMapper.Map(tenant); + if (!name.IsNullOrWhiteSpace()) + { + using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! + { + var tenant = await TenantRepository.FindByNameAsync(name); + return await SetCacheAsync(cacheKey, tenant); + } } + + throw new AbpException("Both id and name can't be invalid."); } - [Obsolete("Use FindAsync method.")] - public virtual TenantConfiguration Find(Guid id) + protected virtual async Task SetCacheAsync(string cacheKey, [CanBeNull]Tenant tenant) { - using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! + var tenantConfiguration = tenant != null ? ObjectMapper.Map(tenant) : null; + var cacheItem = new TenantCacheItem(tenantConfiguration); + await Cache.SetAsync(cacheKey, cacheItem, considerUow: true); + return cacheItem; + } + + [Obsolete("Use GetCacheItemAsync method.")] + protected virtual TenantCacheItem GetCacheItem(Guid? id, string name) + { + var cacheKey = CalculateCacheKey(id, name); + + var cacheItem = Cache.Get(cacheKey, considerUow: true); + if (cacheItem != null) + { + return cacheItem; + } + + if (id.HasValue) { - var tenant = TenantRepository.FindById(id); - if (tenant == null) + using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! { - return null; + var tenant = TenantRepository.FindById(id.Value); + return SetCache(cacheKey, tenant); } + } - return ObjectMapper.Map(tenant); + if (!name.IsNullOrWhiteSpace()) + { + using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities! + { + var tenant = TenantRepository.FindByName(name); + return SetCache(cacheKey, tenant); + } } + + throw new AbpException("Both id and name can't be invalid."); + } + + [Obsolete("Use SetCacheAsync method.")] + protected virtual TenantCacheItem SetCache(string cacheKey, [CanBeNull]Tenant tenant) + { + var tenantConfiguration = tenant != null ? ObjectMapper.Map(tenant) : null; + var cacheItem = new TenantCacheItem(tenantConfiguration); + Cache.Set(cacheKey, cacheItem, considerUow: true); + return cacheItem; + } + + protected virtual string CalculateCacheKey(Guid? id, string name) + { + return TenantCacheItem.CalculateCacheKey(id, name); } } } diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantCacheItemInvalidator_Tests.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantCacheItemInvalidator_Tests.cs new file mode 100644 index 0000000000..4c2a8e3c3f --- /dev/null +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.Domain.Tests/Volo/Abp/TenantManagement/TenantCacheItemInvalidator_Tests.cs @@ -0,0 +1,86 @@ +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Caching; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.TenantManagement +{ + public class TenantCacheItemInvalidator_Tests : AbpTenantManagementDomainTestBase + { + private readonly IDistributedCache _cache; + private readonly ITenantStore _tenantStore; + private readonly ITenantRepository _tenantRepository; + + public TenantCacheItemInvalidator_Tests() + { + _cache = GetRequiredService>(); + _tenantStore = GetRequiredService(); + _tenantRepository = GetRequiredService(); + } + + [Fact] + public async Task Get_Tenant_Should_Cached() + { + var acme = await _tenantRepository.FindByNameAsync("acme"); + acme.ShouldNotBeNull(); + + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(acme.Id, null))).ShouldBeNull(); + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(null, acme.Name))).ShouldBeNull(); + + await _tenantStore.FindAsync(acme.Id); + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(acme.Id, null))).ShouldNotBeNull(); + + await _tenantStore.FindAsync(acme.Name); + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(null, acme.Name))).ShouldNotBeNull(); + + + var volosoft = _tenantRepository.FindByName("volosoft"); + volosoft.ShouldNotBeNull(); + + (_cache.Get(TenantCacheItem.CalculateCacheKey(volosoft.Id, null))).ShouldBeNull(); + (_cache.Get(TenantCacheItem.CalculateCacheKey(null, volosoft.Name))).ShouldBeNull(); + + _tenantStore.Find(volosoft.Id); + (_cache.Get(TenantCacheItem.CalculateCacheKey(volosoft.Id, null))).ShouldNotBeNull(); + + _tenantStore.Find(volosoft.Name); + (_cache.Get(TenantCacheItem.CalculateCacheKey(null, volosoft.Name))).ShouldNotBeNull(); + } + + [Fact] + public async Task Cache_Should_Invalidator_When_Tenant_Changed() + { + var acme = await _tenantRepository.FindByNameAsync("acme"); + acme.ShouldNotBeNull(); + + // FindAsync will cache tenant. + await _tenantStore.FindAsync(acme.Id); + await _tenantStore.FindAsync(acme.Name); + + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(acme.Id, null))).ShouldNotBeNull(); + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(null, acme.Name))).ShouldNotBeNull(); + + await _tenantRepository.DeleteAsync(acme); + + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(acme.Id, null))).ShouldBeNull(); + (await _cache.GetAsync(TenantCacheItem.CalculateCacheKey(null, acme.Name))).ShouldBeNull(); + + + var volosoft = await _tenantRepository.FindByNameAsync("volosoft"); + volosoft.ShouldNotBeNull(); + + // Find will cache tenant. + _tenantStore.Find(volosoft.Id); + _tenantStore.Find(volosoft.Name); + + (_cache.Get(TenantCacheItem.CalculateCacheKey(volosoft.Id, null))).ShouldNotBeNull(); + (_cache.Get(TenantCacheItem.CalculateCacheKey(null, volosoft.Name))).ShouldNotBeNull(); + + await _tenantRepository.DeleteAsync(volosoft); + + (_cache.Get(TenantCacheItem.CalculateCacheKey(volosoft.Id, null))).ShouldBeNull(); + (_cache.Get(TenantCacheItem.CalculateCacheKey(null, volosoft.Name))).ShouldBeNull(); + } + } +} diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 560d4d2937..394cdac655 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -37,21 +37,21 @@ "@abp/ng.theme.shared": "~4.2.0-rc.2", "@abp/utils": "^4.2.0-rc.2", "@angular-builders/jest": "^10.0.0", - "@angular-devkit/build-angular": "~0.1100.0", + "@angular-devkit/build-angular": "~0.1101.0", "@angular-devkit/build-ng-packagr": "~0.1001.2", "@angular-devkit/schematics-cli": "^0.1001.1", - "@angular/animations": "~11.0.0", - "@angular/cli": "~11.0.0", - "@angular/common": "~11.0.0", - "@angular/compiler": "11.0.0", - "@angular/compiler-cli": "11.0.0", - "@angular/core": "~11.0.0", - "@angular/forms": "~11.0.0", - "@angular/language-service": "~11.0.0", - "@angular/localize": "~11.0.0", - "@angular/platform-browser": "~11.0.0", - "@angular/platform-browser-dynamic": "~11.0.0", - "@angular/router": "~11.0.0", + "@angular/animations": "~11.1.0", + "@angular/cli": "~11.1.0", + "@angular/common": "~11.1.0", + "@angular/compiler": "11.1.0", + "@angular/compiler-cli": "11.1.0", + "@angular/core": "~11.1.0", + "@angular/forms": "~11.1.0", + "@angular/language-service": "~11.1.0", + "@angular/localize": "~11.1.0", + "@angular/platform-browser": "~11.1.0", + "@angular/platform-browser-dynamic": "~11.1.0", + "@angular/router": "~11.1.0", "@fortawesome/fontawesome-free": "^5.14.0", "@ng-bootstrap/ng-bootstrap": "^7.0.0", "@ngneat/inspector": "^1.0.0", @@ -91,7 +91,7 @@ "ts-toolbelt": "6.15.4", "tsickle": "^0.39.1", "tslint": "~6.1.0", - "typescript": "~4.0.3", + "typescript": "~4.1.3", "zone.js": "~0.10.2" }, "dependencies": { @@ -102,4 +102,4 @@ "path": "cz-conventional-changelog" } } -} +} \ No newline at end of file diff --git a/npm/ng-packs/packages/core/src/lib/core.module.ts b/npm/ng-packs/packages/core/src/lib/core.module.ts index 260cffd4d3..36d59c929b 100644 --- a/npm/ng-packs/packages/core/src/lib/core.module.ts +++ b/npm/ng-packs/packages/core/src/lib/core.module.ts @@ -11,7 +11,6 @@ import { ReplaceableRouteContainerComponent } from './components/replaceable-rou import { RouterOutletComponent } from './components/router-outlet.component'; import { AutofocusDirective } from './directives/autofocus.directive'; import { InputEventDebounceDirective } from './directives/debounce.directive'; -import { EllipsisDirective } from './directives/ellipsis.directive'; import { ForDirective } from './directives/for.directive'; import { FormSubmitDirective } from './directives/form-submit.directive'; import { InitDirective } from './directives/init.directive'; @@ -53,11 +52,9 @@ export function storageFactory(): OAuthStorage { ReactiveFormsModule, RouterModule, LocalizationModule, - AbstractNgModelComponent, AutofocusDirective, DynamicLayoutComponent, - EllipsisDirective, ForDirective, FormSubmitDirective, InitDirective, @@ -83,7 +80,6 @@ export function storageFactory(): OAuthStorage { AbstractNgModelComponent, AutofocusDirective, DynamicLayoutComponent, - EllipsisDirective, ForDirective, FormSubmitDirective, InitDirective, diff --git a/npm/ng-packs/packages/core/src/lib/directives/index.ts b/npm/ng-packs/packages/core/src/lib/directives/index.ts index a297faa14b..3322b14257 100644 --- a/npm/ng-packs/packages/core/src/lib/directives/index.ts +++ b/npm/ng-packs/packages/core/src/lib/directives/index.ts @@ -1,6 +1,5 @@ export * from './autofocus.directive'; export * from './debounce.directive'; -export * from './ellipsis.directive'; export * from './for.directive'; export * from './form-submit.directive'; export * from './init.directive'; diff --git a/npm/ng-packs/packages/core/src/lib/tests/tree-utils.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/tree-utils.spec.ts index 6115114872..b4f8721f47 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/tree-utils.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/tree-utils.spec.ts @@ -1,4 +1,9 @@ -import { BaseTreeNode, createTreeFromList, TreeNode } from '../utils/tree-utils'; +import { + BaseTreeNode, + createTreeFromList, + createTreeNodeFilterCreator, + TreeNode, +} from '../utils/tree-utils'; const LIST_1 = [ { id: 1, pid: null }, @@ -38,6 +43,42 @@ const TREE_3 = [ ], }, ]; +const SOURCE_TREE: TreeNode[] = [ + { + id: 1, + pid: null, + isLeaf: false, + name: 'foo', + children: [ + { + id: 2, + pid: 1, + name: 'bar', + isLeaf: false, + children: [{ id: 3, pid: 2, name: 'qux', isLeaf: true, children: [] }], + }, + { id: 4, pid: 1, name: 'baz', isLeaf: true, children: [] }, + { id: 5, pid: 1, name: 'quux', isLeaf: true, children: [] }, + ], + }, +]; +const RESULT_TREE_1 = [ + { id: 3, pid: 2, name: 'qux', isLeaf: true, children: [] }, + { id: 5, pid: 1, name: 'quux', isLeaf: true, children: [] }, +]; +const RESULT_TREE_2 = [{ id: 5, pid: 1, name: 'quux', isLeaf: true, children: [] }]; +const RESULT_TREE_3 = [ + { + id: 2, + pid: 1, + name: 'bar', + isLeaf: false, + children: [{ id: 3, pid: 2, name: 'qux', isLeaf: true, children: [] }], + }, + { id: 4, pid: 1, name: 'baz', isLeaf: true, children: [] }, +]; +const RESULT_TREE_4 = [{ id: 4, pid: 1, name: 'baz', isLeaf: true, children: [] }]; + describe('Tree Utils', () => { describe('createTreeFromList', () => { test.each` @@ -56,6 +97,23 @@ describe('Tree Utils', () => { expect(removeParents(tree)).toEqual(expected); }); }); + + describe('createTreeNodeFilterCreator', () => { + test.each` + search | expected + ${'qu'} | ${RESULT_TREE_1} + ${'quu'} | ${RESULT_TREE_2} + ${'ba'} | ${RESULT_TREE_3} + ${'baz'} | ${RESULT_TREE_4} + `( + 'should return $expected when $search is searched', + ({ search, expected }: TestCreateTreeNodeFilter) => { + const filter = createTreeNodeFilterCreator('name', String)(search); + + expect(filter(SOURCE_TREE)).toEqual(expected); + }, + ); + }); }); function removeParents(tree: TreeNode[]) { @@ -72,6 +130,17 @@ interface TestCreateTreeFromList { } interface Model { - id: 1; - pid: null; + id: number; + pid?: number; +} + +interface TestCreateTreeNodeFilter { + search: string; + expected: TreeNode[]; +} + +interface SearchModel { + id: number; + pid?: number; + name: string; } diff --git a/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts index ba6e3a5b90..235820abd6 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/tree-utils.ts @@ -54,6 +54,25 @@ export function createMapFromList( return map; } +export function createTreeNodeFilterCreator( + key: keyof T, + mapperFn: (value: any) => string, +) { + return (search: string) => { + const regex = new RegExp('.*' + search + '.*', 'i'); + + return function collectNodes(nodes: TreeNode[], matches = []) { + for (const node of nodes) { + if (regex.test(mapperFn(node[key]))) matches.push(node); + + if (node.children.length) collectNodes(node.children, matches); + } + + return matches; + }; + }; +} + export type TreeNode = { [K in keyof T]: T[K]; } & { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal-container.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal-container.component.ts index bd24bd84d2..1b07955270 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal-container.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal-container.component.ts @@ -1,10 +1,11 @@ import { Component, ViewChild, ViewContainerRef } from '@angular/core'; +/** + * @deprecated To be removed in v5.0 + */ @Component({ selector: 'abp-modal-container', - template: ` - - `, + template: '', }) export class ModalContainerComponent { @ViewChild('container', { static: true, read: ViewContainerRef }) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html index b37f5b6ce3..bc70c79fc1 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.html @@ -1,43 +1,23 @@ - - + + + + + - - diff --git a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts index b1fbef79be..0142adeb25 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/components/modal/modal.component.ts @@ -9,17 +9,14 @@ import { OnDestroy, Optional, Output, - Renderer2, TemplateRef, ViewChild, - ViewChildren, } from '@angular/core'; +import { NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { fromEvent, Subject } from 'rxjs'; import { debounceTime, distinctUntilChanged, filter, takeUntil } from 'rxjs/operators'; -import { fadeAnimation } from '../../animations/modal.animations'; import { Confirmation } from '../../models/confirmation'; import { ConfirmationService } from '../../services/confirmation.service'; -import { ModalService } from '../../services/modal.service'; import { SUPPRESS_UNSAVED_CHANGES_WARNING } from '../../tokens/suppress-unsaved-changes-warning.token'; import { ButtonComponent } from '../button/button.component'; @@ -28,11 +25,23 @@ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl'; @Component({ selector: 'abp-modal', templateUrl: './modal.component.html', - animations: [fadeAnimation], styleUrls: ['./modal.component.scss'], - providers: [ModalService, SubscriptionService], + providers: [SubscriptionService], }) export class ModalComponent implements OnDestroy { + /** + * @deprecated Use centered property of options input instead. To be deleted in v5.0. + */ + @Input() centered = false; + /** + * @deprecated Use windowClass property of options input instead. To be deleted in v5.0. + */ + @Input() modalClass = ''; + /** + * @deprecated Use size property of options input instead. To be deleted in v5.0. + */ + @Input() size: ModalSize = 'lg'; + @Input() get visible(): boolean { return this._visible; @@ -54,16 +63,11 @@ export class ModalComponent implements OnDestroy { this._busy = value; } - @Input() centered = false; - - @Input() modalClass = ''; - - @Input() size: ModalSize = 'lg'; + @Input() options: NgbModalOptions = {}; @Input() suppressUnsavedChangesWarning = this.suppressUnsavedChangesWarningToken; - @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) - abpSubmit: ButtonComponent; + @ViewChild('modalContent') modalContent: TemplateRef; @ContentChild('abpHeader', { static: false }) abpHeader: TemplateRef; @@ -71,28 +75,25 @@ export class ModalComponent implements OnDestroy { @ContentChild('abpFooter', { static: false }) abpFooter: TemplateRef; + @ContentChild(ButtonComponent, { static: false, read: ButtonComponent }) + abpSubmit: ButtonComponent; + @ContentChild('abpClose', { static: false, read: ElementRef }) abpClose: ElementRef; - @ViewChild('template', { static: false }) template: TemplateRef; - - @ViewChild('abpModalContent', { static: false }) modalContent: ElementRef; - - @ViewChildren('abp-button') abpButtons; - @Output() readonly visibleChange = new EventEmitter(); @Output() readonly init = new EventEmitter(); - @Output() readonly appear = new EventEmitter(); + @Output() readonly appear = new EventEmitter(); - @Output() readonly disappear = new EventEmitter(); + @Output() readonly disappear = new EventEmitter(); _visible = false; _busy = false; - isModalOpen = false; + modalRef: NgbModalRef; isConfirmationOpen = false; @@ -105,13 +106,12 @@ export class ModalComponent implements OnDestroy { } constructor( - private renderer: Renderer2, private confirmationService: ConfirmationService, - private modalService: ModalService, private subscription: SubscriptionService, @Optional() @Inject(SUPPRESS_UNSAVED_CHANGES_WARNING) private suppressUnsavedChangesWarningToken: boolean, + private modal: NgbModal, ) { this.initToggleStream(); } @@ -123,21 +123,34 @@ export class ModalComponent implements OnDestroy { } private toggle(value: boolean) { - this.isModalOpen = value; this._visible = value; this.visibleChange.emit(value); - if (value) { - this.modalService.renderTemplate(this.template); - setTimeout(() => this.listen(), 0); - this.renderer.addClass(document.body, 'modal-open'); - this.appear.emit(); - } else { - this.modalService.clearModal(); - this.renderer.removeClass(document.body, 'modal-open'); + if (!value) { + this.modalRef?.dismiss(); this.disappear.emit(); this.destroy$.next(); + return; } + + setTimeout(() => this.listen(), 0); + this.modalRef = this.modal.open(this.modalContent, { + // TODO: set size to 'lg' when removed the size variable + size: this.size, + windowClass: this.modalClass, + centered: this.centered, + keyboard: false, + scrollable: true, + beforeDismiss: () => { + if (!this.visible) return true; + + this.close(); + return !this.visible; + }, + ...this.options, + }); + + this.appear.emit(); } ngOnDestroy(): void { @@ -190,10 +203,7 @@ export class ModalComponent implements OnDestroy { setTimeout(() => { if (!this.abpClose) return; fromEvent(this.abpClose.nativeElement, 'click') - .pipe( - takeUntil(this.destroy$), - filter(() => !!this.modalContent), - ) + .pipe(takeUntil(this.destroy$)) .subscribe(() => this.close()); }, 0); diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/ellipsis.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/ellipsis.directive.ts new file mode 100644 index 0000000000..6a2ea1ea09 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/ellipsis.directive.ts @@ -0,0 +1,52 @@ +import { + AfterViewInit, + ChangeDetectorRef, + Directive, + ElementRef, + HostBinding, + Input, + NgModule, +} from '@angular/core'; + +@Directive({ + selector: '[abpEllipsis]', +}) +export class EllipsisDirective implements AfterViewInit { + @Input('abpEllipsis') + width: string; + + @HostBinding('title') + @Input() + title: string; + + @Input('abpEllipsisEnabled') + enabled = true; + + @HostBinding('class.abp-ellipsis-inline') + get inlineClass() { + return this.enabled && this.width; + } + + @HostBinding('class.abp-ellipsis') + get class() { + return this.enabled && !this.width; + } + + @HostBinding('style.max-width') + get maxWidth() { + return this.enabled && this.width ? this.width || '170px' : undefined; + } + + constructor(private cdRef: ChangeDetectorRef, private elRef: ElementRef) {} + + ngAfterViewInit() { + this.title = this.title || (this.elRef.nativeElement as HTMLElement).innerText; + this.cdRef.detectChanges(); + } +} + +@NgModule({ + exports: [EllipsisDirective], + declarations: [EllipsisDirective], +}) +export class EllipsisModule {} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts index a6b666be3b..64b849d4fe 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/index.ts @@ -2,3 +2,4 @@ export * from './loading.directive'; export * from './ngx-datatable-default.directive'; export * from './ngx-datatable-list.directive'; export * from './table-sort.directive'; +export * from './ellipsis.directive'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/services/modal.service.ts b/npm/ng-packs/packages/theme-shared/src/lib/services/modal.service.ts index de63164810..a75cfe5089 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/services/modal.service.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/services/modal.service.ts @@ -2,6 +2,9 @@ import { ContentProjectionService, PROJECTION_STRATEGY } from '@abp/ng.core'; import { ComponentRef, Injectable, TemplateRef, ViewContainerRef, OnDestroy } from '@angular/core'; import { ModalContainerComponent } from '../components/modal/modal-container.component'; +/** + * @deprecated Use ng-bootstrap modal. To be deleted in v5.0. + */ @Injectable({ providedIn: 'root', }) diff --git a/npm/ng-packs/packages/core/src/lib/tests/ellipsis.directive.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts similarity index 100% rename from npm/ng-packs/packages/core/src/lib/tests/ellipsis.directive.spec.ts rename to npm/ng-packs/packages/theme-shared/src/lib/tests/ellipsis.directive.spec.ts diff --git a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts index b321ee4542..6dd3f8d6db 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/tests/modal.component.spec.ts @@ -1,13 +1,13 @@ import { LocalizationPipe } from '@abp/ng.core'; import { RouterTestingModule } from '@angular/router/testing'; +import { NgbModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; import { Store } from '@ngxs/store'; import { fromEvent, Subject, timer } from 'rxjs'; import { delay, reduce, take } from 'rxjs/operators'; import { ButtonComponent, ConfirmationComponent, ModalComponent } from '../components'; -import { ModalContainerComponent } from '../components/modal/modal-container.component'; import { Confirmation } from '../models'; -import { ConfirmationService, ModalService } from '../services'; +import { ConfirmationService } from '../services'; describe('ModalComponent', () => { let spectator: SpectatorHost< @@ -19,14 +19,8 @@ describe('ModalComponent', () => { let mockConfirmation$: Subject; const createHost = createHostFactory({ component: ModalComponent, - imports: [RouterTestingModule], - declarations: [ - ConfirmationComponent, - LocalizationPipe, - ButtonComponent, - ModalContainerComponent, - ], - entryComponents: [ModalContainerComponent], + imports: [RouterTestingModule, NgbModalModule], + declarations: [ConfirmationComponent, LocalizationPipe, ButtonComponent], providers: [ { provide: ConfirmationService, @@ -46,7 +40,7 @@ describe('ModalComponent', () => { disappearFn = jest.fn(); spectator = createHost( - ` + `
@@ -78,15 +72,14 @@ describe('ModalComponent', () => { }); afterEach(() => { - const modalService = spectator.inject(ModalService); - modalService.clearModal(); + const modalService = spectator.inject(NgbModal); + modalService.dismissAll(); }); - it('should project its template to abp-modal-container', () => { + it('should open the ngb-modal with backdrop', () => { const modal = selectModal(); expect(modal).toBeTruthy(); - expect(modal.querySelector('div.modal-backdrop')).toBeTruthy(); - expect(modal.querySelector('div#abp-modal-dialog')).toBeTruthy(); + expect(document.querySelector('ngb-modal-backdrop')).toBeTruthy(); }); it('should reflect its input properties to the template', () => { @@ -155,10 +148,10 @@ describe('ModalComponent', () => { warnSpy.mockClear(); mockConfirmation$.next(Confirmation.Status.confirm); - await wait0ms(); - expect(selectModal()).toBeNull(); + // TODO: There is presumably a problem with change detection + // expect(selectModal()).toBeNull(); expect(disappearFn).toHaveBeenCalledTimes(1); }); @@ -209,7 +202,7 @@ describe('ModalComponent', () => { }); function selectModal(modalSelector = ''): Element { - return document.querySelector(`abp-modal-container div.modal${modalSelector}`); + return document.querySelector(`ngb-modal-window.modal${modalSelector}`); } async function wait0ms() { diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index 8d64febdbf..8d8f08ea6b 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -25,6 +25,7 @@ import { TableComponent } from './components/table/table.component'; import { ToastContainerComponent } from './components/toast-container/toast-container.component'; import { ToastComponent } from './components/toast/toast.component'; import { DEFAULT_VALIDATION_BLUEPRINTS } from './constants/validation'; +import { EllipsisModule } from './directives/ellipsis.directive'; import { LoadingDirective } from './directives/loading.directive'; import { NgxDatatableDefaultDirective } from './directives/ngx-datatable-default.directive'; import { NgxDatatableListDirective } from './directives/ngx-datatable-list.directive'; @@ -57,9 +58,15 @@ const declarationsWithExports = [ ]; @NgModule({ - imports: [CoreModule, NgxDatatableModule, NgxValidateCoreModule, NgbPaginationModule], + imports: [ + CoreModule, + NgxDatatableModule, + NgxValidateCoreModule, + NgbPaginationModule, + EllipsisModule, + ], declarations: [...declarationsWithExports, HttpErrorWrapperComponent, ModalContainerComponent], - exports: [NgxDatatableModule, ...declarationsWithExports], + exports: [NgxDatatableModule, EllipsisModule, ...declarationsWithExports], providers: [DatePipe], entryComponents: [ HttpErrorWrapperComponent, diff --git a/npm/ng-packs/yarn.lock b/npm/ng-packs/yarn.lock index 94635e72ca..c87d4f36e2 100644 --- a/npm/ng-packs/yarn.lock +++ b/npm/ng-packs/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@abp/ng.core@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-4.2.0-rc.1.tgz#14bb772ea184153c5cb3fe0da538ff921892330a" - integrity sha512-2qyoGaU+eKwULenCG3sEhlztxA1vhECCs1gkGzsxIpZquFQCSdpRGAZNM4EUbLpBASXAYU/CGDWBgqYIIK/8Kw== +"@abp/ng.core@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.core/-/ng.core-4.2.0-rc.2.tgz#d5ae888cd6beba0ab7cc92a9759e8f3b4095f5dc" + integrity sha512-uPsxulybSdGyBpR08d9XR3i/YdG/ILvo1Gzyz2wTOG/gay12hh/p83S50kWeBuPkh8uOiT3ezO9BY/GPVrRI+Q== dependencies: - "@abp/utils" "^4.1.1" + "@abp/utils" "^4.2.0-rc.1" "@angular/localize" "~10.0.10" "@ngxs/store" "^3.7.0" angular-oauth2-oidc "^10.0.0" @@ -17,35 +17,35 @@ ts-toolbelt "6.15.4" tslib "^2.0.0" -"@abp/ng.feature-management@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-4.2.0-rc.1.tgz#08f85b70ea4d57e92a1e011e88cf1be8e6592c36" - integrity sha512-exZyx28Njt7x+70n1Okk8CGCAQf479TGWziMnZ6HaOoDTFxcDnk0jT62aO3AALMFr2cUmGUxxyWFo4Tan2CwDA== +"@abp/ng.feature-management@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.feature-management/-/ng.feature-management-4.2.0-rc.2.tgz#c0273a07f73d95e7133ec90c1facd5de51121430" + integrity sha512-sGQfh0plQGtm3qCGYs1qmCtWt2EOwVgRyUcNd2ipzorH55S5/dX7or9dYKZofPz7GF55FohbfpxBqnKnBo0WOQ== dependencies: - "@abp/ng.theme.shared" "~4.2.0-rc.1" + "@abp/ng.theme.shared" "~4.2.0-rc.2" tslib "^2.0.0" -"@abp/ng.identity@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-4.2.0-rc.1.tgz#9fdbafb53918ff36e64e1fbb685098b40e55acfd" - integrity sha512-/ftw1z6mEnmg//lWUns8r7GLVFBbRKvZX9sJ9gProwR/E6fnRuifv/q0p4Nsq+eOgudnQb/RlVg1JyTIDBt6YA== +"@abp/ng.identity@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.identity/-/ng.identity-4.2.0-rc.2.tgz#fe20f05b60980561218f8357df55c00d85af8eb2" + integrity sha512-zrY5oPhDq8lPcvDvxYM/hS5CtRj71Zlu04Slz4f+ljlStWHBOBZx1VVBl4OQvbDGxDGC04Pf2Sj3PtppEU28gw== dependencies: - "@abp/ng.permission-management" "~4.2.0-rc.1" - "@abp/ng.theme.shared" "~4.2.0-rc.1" + "@abp/ng.permission-management" "~4.2.0-rc.2" + "@abp/ng.theme.shared" "~4.2.0-rc.2" tslib "^2.0.0" -"@abp/ng.permission-management@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-4.2.0-rc.1.tgz#f4aaf505056d0238681ea32b6c7079a019120b39" - integrity sha512-9w3FZ4ztvoXRAT9FdhIYUnpXmvagDDPk9X9xKNT8Md5TX4ivynSs3pBHC2baqhR1Xc2FHHEbfVjN+FFMmDQRlg== +"@abp/ng.permission-management@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.permission-management/-/ng.permission-management-4.2.0-rc.2.tgz#771285283bf3c82dcdd6a07ef444ada73b918d4a" + integrity sha512-FSMSiXGhalTMZIJzQCS6fYRdFH2eVmk73aPeFn1NnMxNdhzCXfbsHpX/lVes+UMw7CddEEgriv0GQYi8dYMnDw== dependencies: - "@abp/ng.theme.shared" "~4.2.0-rc.1" + "@abp/ng.theme.shared" "~4.2.0-rc.2" tslib "^2.0.0" -"@abp/ng.schematics@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.2.0-rc.1.tgz#b62dcc2bd65e05810b46d40e46c4b871ed729196" - integrity sha512-2vcMuyxVziZDaFQbz1/DhaIErv6yVJMZILVOzbk1zCgIFtvoXIY/+XkHfChzUAeFkEe/rKGdZsXIG6LHsg7RuQ== +"@abp/ng.schematics@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.schematics/-/ng.schematics-4.2.0-rc.2.tgz#93774148f57964c72498fa060c04eb99f6c3987d" + integrity sha512-Ul/+nPTnGpNhi0NMfeYY9fDnsTVzVxP4iSIdqXevPxugeY6pqCefC84i0XHdkVsxIi1UR7VP84tqE9SCv7KnCw== dependencies: "@angular-devkit/core" "~11.0.2" "@angular-devkit/schematics" "~11.0.2" @@ -53,37 +53,37 @@ jsonc-parser "^2.3.0" typescript "~3.9.2" -"@abp/ng.setting-management@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-4.2.0-rc.1.tgz#74f9577193554997a47fef8c0f4e4d4b3228f6dc" - integrity sha512-T18tkvyIsER22fGWh0KRmxobhuFxuxbDB8vroV0k0/Zd4ULDrTuSoKXgWa/TiHe7RuO/tWKv0EEf/T4xaiWBGw== +"@abp/ng.setting-management@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.setting-management/-/ng.setting-management-4.2.0-rc.2.tgz#a7c5bb30e881f8efe702fcc8f3b95bf442b0289a" + integrity sha512-5sOk19EzFBIIdeTAx6EEmgh2pYOj5oRXq6uuAOmGVzZiX7arFoyqvTR4OAypIBQdMlZ0Iv4EApvJLD6/1IIrqw== dependencies: - "@abp/ng.theme.shared" "~4.2.0-rc.1" + "@abp/ng.theme.shared" "~4.2.0-rc.2" tslib "^2.0.0" -"@abp/ng.tenant-management@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-4.2.0-rc.1.tgz#94b8a0ff7987b8e7602be310b3f91742e1d6b0e8" - integrity sha512-SO3cxFCY6p0i/BywlHoAFeMEtJ0w9Q61jqW/1T8jQR8HDXNDeMeHdiUsAzjv9ILggy7OPFmGZQ8pcniwK3IQpQ== +"@abp/ng.tenant-management@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.tenant-management/-/ng.tenant-management-4.2.0-rc.2.tgz#dc5c019f16ac679589dbeac85317628ec6682096" + integrity sha512-O2Lb0Fe+L11yC69DAcgX4hY6KwI9ojaDnPTFxGtvvTjaUVKnxxchQrJwP0ITsudZe5cWKbF7cME3b3uH4jorBA== dependencies: - "@abp/ng.feature-management" "~4.2.0-rc.1" - "@abp/ng.theme.shared" "~4.2.0-rc.1" + "@abp/ng.feature-management" "~4.2.0-rc.2" + "@abp/ng.theme.shared" "~4.2.0-rc.2" tslib "^2.0.0" -"@abp/ng.theme.basic@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-4.2.0-rc.1.tgz#dbc1a47e7d311fc7ca5d212d6736fde49f4586f2" - integrity sha512-3fMeIbz4cotGqGty9QWyw4vrhkYJgLqgfunw+F+YxuLaXFq2JljlfZvzwDCw7EZ53N5atHT/um5RVAveWvKKmQ== +"@abp/ng.theme.basic@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.theme.basic/-/ng.theme.basic-4.2.0-rc.2.tgz#83c6ae4d0d95fc0271386a02c2f22531325c3b26" + integrity sha512-VGEDWIIRnmHPrhcjIhhBcqILI8NUsuVIX7K5XfJUuf3WmEzH9Ru9Yyi81AYZNuBNXNAjyjRH/nIxwljnHxO8bA== dependencies: - "@abp/ng.theme.shared" "~4.2.0-rc.1" + "@abp/ng.theme.shared" "~4.2.0-rc.2" tslib "^2.0.0" -"@abp/ng.theme.shared@~4.2.0-rc.1": - version "4.2.0-rc.1" - resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.2.0-rc.1.tgz#e3edecefd2a332740e80a7f6c6dbfa06f7580c89" - integrity sha512-/EOHL9WmtxJ6xgzUbNjc50p9oStycmpsz9Z/RZTffaUXUnVSDgqeArvBuPTGTuMJVipnbVfpYjC98eOixj5Ong== +"@abp/ng.theme.shared@~4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/ng.theme.shared/-/ng.theme.shared-4.2.0-rc.2.tgz#935ca3aa32d8ced85d277139ce790f3cc3faa184" + integrity sha512-UjYPd6KL3bO9PN9XEmBlKQSc0C3EJsLHhifoV+rgtqAHvortOygSHHknOthRA4PN+cg2/PiuWA5gpvMHP1K7Ow== dependencies: - "@abp/ng.core" "~4.2.0-rc.1" + "@abp/ng.core" "~4.2.0-rc.2" "@fortawesome/fontawesome-free" "^5.14.0" "@ng-bootstrap/ng-bootstrap" "^7.0.0" "@ngx-validate/core" "^0.0.13" @@ -92,13 +92,6 @@ chart.js "^2.9.3" tslib "^2.0.0" -"@abp/utils@^4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.1.1.tgz#87f453602d8d8381f730f720eff206f08f218de9" - integrity sha512-WtVKkXAW5bC6XtG/yjkChUM9Z8j+f4idc92CVQxUDOzXhQKGqNsi/3N+qacmD0o+dQVIokDgNmS10R1OaYKtcA== - dependencies: - just-compare "^1.3.0" - "@abp/utils@^4.2.0-rc.1": version "4.2.0-rc.1" resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.2.0-rc.1.tgz#397615ec208150f46ae626b67ea71053750f612f" @@ -106,6 +99,13 @@ dependencies: just-compare "^1.3.0" +"@abp/utils@^4.2.0-rc.2": + version "4.2.0-rc.2" + resolved "https://registry.yarnpkg.com/@abp/utils/-/utils-4.2.0-rc.2.tgz#43c27d421da8e58a5d74c0a2df8742efa827c842" + integrity sha512-6gSLTP9s88aFjxm+fU2iZMzv5Eoe5/va2PHTAoQJA4BaP/SpFRGts3cT4HYWPfAbmzZVZ+tvjuYd3P6d3FCAhw== + dependencies: + just-compare "^1.3.0" + "@angular-builders/jest@^10.0.0": version "10.0.1" resolved "https://registry.yarnpkg.com/@angular-builders/jest/-/jest-10.0.1.tgz#a1a6fb5d11b5d54c051bdaa2012b5f046371560c" @@ -124,12 +124,12 @@ "@angular-devkit/core" "10.1.7" rxjs "6.6.2" -"@angular-devkit/architect@0.1100.6": - version "0.1100.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1100.6.tgz#ce90ffb78d1d945cafc339d4cfc63b3582cb8e6a" - integrity sha512-4O+cg3AimI2bNAxxdu5NrqSf4Oa8r8xL0+G2Ycd3jLoFv0h0ecJiNKEG5F6IpTprb4aexZD6pcxBJCqQ8MmzWQ== +"@angular-devkit/architect@0.1101.0": + version "0.1101.0" + resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1101.0.tgz#bf9649e43f17c06c6c9bde7fc9ea21603a949e9f" + integrity sha512-Lj1STrxPXvvZVIk9RSTOPEiJeE/PGbBSclz12u92F3DsmbhcCxpiZ8AU8bUvJJ8gsZhGRB0BjPN0gCSWr9Po7w== dependencies: - "@angular-devkit/core" "11.0.6" + "@angular-devkit/core" "11.1.0" rxjs "6.6.3" "@angular-devkit/architect@>=0.1000.0 < 0.1100.0": @@ -140,79 +140,80 @@ "@angular-devkit/core" "10.2.1" rxjs "6.6.2" -"@angular-devkit/build-angular@~0.1100.0": - version "0.1100.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1100.6.tgz#4aa7635ab8fc1c6435b2b93954c08f2a7d7a8dd9" - integrity sha512-HcqsWiSIUxExGg3HRQScLOmF+ckVkCKolfpPcNOCCpBYxH/i8n4wDGLBP5Rtxky+0Qz+3nnAaFIpNb9p9aUmbg== - dependencies: - "@angular-devkit/architect" "0.1100.6" - "@angular-devkit/build-optimizer" "0.1100.6" - "@angular-devkit/build-webpack" "0.1100.6" - "@angular-devkit/core" "11.0.6" - "@babel/core" "7.12.3" - "@babel/generator" "7.12.1" - "@babel/plugin-transform-runtime" "7.12.1" - "@babel/preset-env" "7.12.1" - "@babel/runtime" "7.12.1" - "@babel/template" "7.10.4" +"@angular-devkit/build-angular@~0.1101.0": + version "0.1101.0" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1101.0.tgz#71a0f424c8ba5a1eb12012cb6cde30482afa8c7c" + integrity sha512-oTCdHHyP/Z1cPnvJSHsQFD1FbLHI9AV7wYbDQ2UHpQFXg7OFzRWSro2aG54FMe6nD+gbXhQUmRryRsiNkCG71A== + dependencies: + "@angular-devkit/architect" "0.1101.0" + "@angular-devkit/build-optimizer" "0.1101.0" + "@angular-devkit/build-webpack" "0.1101.0" + "@angular-devkit/core" "11.1.0" + "@babel/core" "7.12.10" + "@babel/generator" "7.12.11" + "@babel/plugin-transform-runtime" "7.12.10" + "@babel/preset-env" "7.12.11" + "@babel/runtime" "7.12.5" + "@babel/template" "7.12.7" "@jsdevtools/coverage-istanbul-loader" "3.0.5" - "@ngtools/webpack" "11.0.6" + "@ngtools/webpack" "11.1.0" ansi-colors "4.1.1" - autoprefixer "9.8.6" - babel-loader "8.1.0" + autoprefixer "10.2.1" + babel-loader "8.2.2" browserslist "^4.9.1" cacache "15.0.5" caniuse-lite "^1.0.30001032" - circular-dependency-plugin "5.2.0" - copy-webpack-plugin "6.2.1" - core-js "3.6.5" - css-loader "4.3.0" + circular-dependency-plugin "5.2.2" + copy-webpack-plugin "6.3.2" + core-js "3.8.2" + critters "0.0.6" + css-loader "5.0.1" cssnano "4.1.10" - file-loader "6.1.1" + file-loader "6.2.0" find-cache-dir "3.3.1" glob "7.1.6" inquirer "7.3.3" - jest-worker "26.5.0" + jest-worker "26.6.2" karma-source-map-support "1.4.0" - less "3.12.2" - less-loader "7.0.2" - license-webpack-plugin "2.3.1" + less "4.1.0" + less-loader "7.2.1" + license-webpack-plugin "2.3.11" loader-utils "2.0.0" - mini-css-extract-plugin "1.2.1" + mini-css-extract-plugin "1.3.3" minimatch "3.0.4" - open "7.3.0" - ora "5.1.0" + open "7.3.1" + ora "5.2.0" parse5-html-rewriting-stream "6.0.1" pnp-webpack-plugin "1.6.4" - postcss "7.0.32" - postcss-import "12.0.1" - postcss-loader "4.0.4" + postcss "8.2.4" + postcss-import "14.0.0" + postcss-loader "4.1.0" raw-loader "4.0.2" regenerator-runtime "0.13.7" resolve-url-loader "3.1.2" rimraf "3.0.2" - rollup "2.32.1" + rollup "2.36.1" rxjs "6.6.3" - sass "1.27.0" - sass-loader "10.0.5" - semver "7.3.2" + sass "1.32.4" + sass-loader "10.1.1" + semver "7.3.4" source-map "0.7.3" - source-map-loader "1.1.2" + source-map-loader "1.1.3" source-map-support "0.5.19" speed-measure-webpack-plugin "1.3.3" style-loader "2.0.0" stylus "0.54.8" - stylus-loader "4.3.1" - terser "5.3.7" + stylus-loader "4.3.2" + terser "5.5.1" terser-webpack-plugin "4.2.3" text-table "0.2.0" tree-kill "1.2.2" webpack "4.44.2" webpack-dev-middleware "3.7.2" - webpack-dev-server "3.11.0" - webpack-merge "5.2.0" - webpack-sources "2.0.1" - webpack-subresource-integrity "1.5.1" + webpack-dev-server "3.11.1" + webpack-merge "5.7.3" + webpack-sources "2.2.0" + webpack-subresource-integrity "1.5.2" worker-plugin "5.0.0" "@angular-devkit/build-ng-packagr@~0.1001.2": @@ -223,24 +224,24 @@ "@angular-devkit/architect" "0.1001.7" rxjs "6.6.2" -"@angular-devkit/build-optimizer@0.1100.6": - version "0.1100.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.6.tgz#4d6712ae75eeae71d74fd161a0a18c08402dc527" - integrity sha512-Qkq7n6510N+nXmfZqpqpI0I6Td+b+06RRNmS7KftSNJntU1z5QYh4FggwlthZ5P0QUT92cnBQsnT8OgYqGnwbg== +"@angular-devkit/build-optimizer@0.1101.0": + version "0.1101.0" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1101.0.tgz#92f949a4384105eccc87da8fcb2dd40590e48ab0" + integrity sha512-0hkb7fVBDOMBmLA0NC394PAAZmQ1xo12UeiDwfNN2LF9pYdASVj/OSCcZ3yEfnxzBZm5qeNLJG2c4l2xB5NFPQ== dependencies: loader-utils "2.0.0" source-map "0.7.3" - tslib "2.0.3" - typescript "4.0.5" - webpack-sources "2.0.1" + tslib "2.1.0" + typescript "4.1.3" + webpack-sources "2.2.0" -"@angular-devkit/build-webpack@0.1100.6": - version "0.1100.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1100.6.tgz#301caf71bebed6e841cb15fb3af5147c3b2d9c97" - integrity sha512-kK0FlpYJHP25o1yzIGHQqIvO5kp+p6V5OwGpD2GGRZLlJqd3WdjY5DxnyZoX3/IofO6KsTnmm76fzTRqc62z/Q== +"@angular-devkit/build-webpack@0.1101.0": + version "0.1101.0" + resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1101.0.tgz#1c9a406076f0f568e6dbcccec142ea0adab88b7f" + integrity sha512-oIjRo/zRNqWQbKH/N/S8FZz6u1ADnX9IDML5mu8IucquGnZIqQVrrKeBnldtHQsIbN3TYYskO9xr9OS/W6VHqg== dependencies: - "@angular-devkit/architect" "0.1100.6" - "@angular-devkit/core" "11.0.6" + "@angular-devkit/architect" "0.1101.0" + "@angular-devkit/core" "11.1.0" rxjs "6.6.3" "@angular-devkit/core@10.0.8": @@ -287,6 +288,17 @@ rxjs "6.6.3" source-map "0.7.3" +"@angular-devkit/core@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.1.0.tgz#dec7967df922414f9935365f5ff7938ef6e54a52" + integrity sha512-O2oIcqpQKGvYJH88d/NCgLYZGc9laA1eo2d1s0FH1Udu4c2L+bAsviQqtTKNmzyaqODHrlkt+eKx7uakdwWtnQ== + dependencies: + ajv "6.12.6" + fast-json-stable-stringify "2.1.0" + magic-string "0.25.7" + rxjs "6.6.3" + source-map "0.7.3" + "@angular-devkit/core@8.3.29", "@angular-devkit/core@^8.0.3": version "8.3.29" resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-8.3.29.tgz#3477edd6458653f83e6d78684b100c1bef81382f" @@ -329,13 +341,13 @@ ora "5.0.0" rxjs "6.6.2" -"@angular-devkit/schematics@11.0.6", "@angular-devkit/schematics@~11.0.2": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.0.6.tgz#06631190cb22609462597cd6659080fc3313582a" - integrity sha512-hCyu/SSSiC6dKl/NxdWctknIrBqKR6pRe7DMArWowrZX6P9oi36LpKEFnKutE8+tXjsOqQj8XMBq9L64sXZWqg== +"@angular-devkit/schematics@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.1.0.tgz#451ed0a5c4fe9ac3cf367f4ecf7adac15430c2c8" + integrity sha512-6qfR5w1jyk8MC+5Tfimz+Czsq3WlsVoB57dpxSZfhGGsv1Vxc8Q41y5f3BrAyEqHYjcH7NtaoLQoJjtra5KaAg== dependencies: - "@angular-devkit/core" "11.0.6" - ora "5.1.0" + "@angular-devkit/core" "11.1.0" + ora "5.2.0" rxjs "6.6.3" "@angular-devkit/schematics@^8.0.6": @@ -346,10 +358,19 @@ "@angular-devkit/core" "8.3.29" rxjs "6.4.0" -"@angular/animations@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.0.7.tgz#d71ebb581da4e2805df1b35c75a331192063846a" - integrity sha512-P3cluDGIsaj7vqvqIGW7xFCIXWa1lJDsHsmY3Fexk+ZVCncokftp5ZUANb2+DwOD3BPgd/WjBdXVjwzFQFsoVA== +"@angular-devkit/schematics@~11.0.2": + version "11.0.6" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.0.6.tgz#06631190cb22609462597cd6659080fc3313582a" + integrity sha512-hCyu/SSSiC6dKl/NxdWctknIrBqKR6pRe7DMArWowrZX6P9oi36LpKEFnKutE8+tXjsOqQj8XMBq9L64sXZWqg== + dependencies: + "@angular-devkit/core" "11.0.6" + ora "5.1.0" + rxjs "6.6.3" + +"@angular/animations@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.1.0.tgz#d71c6b11dc79bafdf927e69f6222f3899aa911d8" + integrity sha512-VgpknW33WJiqnNtQwNVWrpiSxkgoChIZLpYLlijSTvFwZOHiraFKApohaW8X61mwL0HuK1RB7Z36B+Q11cw3aw== dependencies: tslib "^2.0.0" @@ -362,43 +383,44 @@ optionalDependencies: parse5 "^5.0.0" -"@angular/cli@~11.0.0": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-11.0.6.tgz#8d65d3ad3841aabe23ff38a41fa6c4f38dd12f66" - integrity sha512-bwrXXyU23HjUlFl0CNCU+XMGa/enooqpMLcTAA15StVpKFHyaA4c57il/aqu+1IuB+zR6rGDzhAABuvRcHd+mQ== +"@angular/cli@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-11.1.0.tgz#caf11ebe9c93ca68d99721829beda5c6138cfa4d" + integrity sha512-9QirgfU7+scdi2UASSlEdqkp1Jva3IiesIUIuxeF7scrFCnk/rZIJ9iSvPJ9qUXYpFYyxpX0aPEBvM/HDExeFQ== dependencies: - "@angular-devkit/architect" "0.1100.6" - "@angular-devkit/core" "11.0.6" - "@angular-devkit/schematics" "11.0.6" - "@schematics/angular" "11.0.6" - "@schematics/update" "0.1100.6" + "@angular-devkit/architect" "0.1101.0" + "@angular-devkit/core" "11.1.0" + "@angular-devkit/schematics" "11.1.0" + "@schematics/angular" "11.1.0" + "@schematics/update" "0.1101.0" "@yarnpkg/lockfile" "1.1.0" ansi-colors "4.1.1" - debug "4.2.0" - ini "1.3.6" + debug "4.3.1" + ini "2.0.0" inquirer "7.3.3" + jsonc-parser "3.0.0" npm-package-arg "8.1.0" npm-pick-manifest "6.1.0" - open "7.3.0" - pacote "9.5.12" - resolve "1.18.1" + open "7.3.1" + pacote "11.1.14" + resolve "1.19.0" rimraf "3.0.2" - semver "7.3.2" - symbol-observable "2.0.3" + semver "7.3.4" + symbol-observable "3.0.0" universal-analytics "0.4.23" - uuid "8.3.1" + uuid "8.3.2" -"@angular/common@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.0.7.tgz#bc76452161bc0728563bbf05fe10cac492dcfdad" - integrity sha512-9VuT9qrSP7Q91Wp276DDieCIZiTBrpLNoJzK/RygQShTymCVPg4Dsl3tQUKaHBPx9MexeqRG/HjN02DVpeqtsA== +"@angular/common@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.1.0.tgz#c1163981c79e34c72ab0bbc385edf4c30abcc98e" + integrity sha512-jR9fnhzvvpdilyhPnyRlRRFRJ9vf/OhUFJrL42Knaj7uknmjgeu168JhwVdq6uj+v1208suXW+nOXhKNIpH38Q== dependencies: tslib "^2.0.0" -"@angular/compiler-cli@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-11.0.0.tgz#ff4c2c16284a31a4f8ff1d224f593f64a1458234" - integrity sha512-zrd/cU9syZ8XuQ3ItfIGaKDn1ZBCWyiqdLVRH9VDmyNqQFiCc/VWQ9Th9z8qpLptgdpzE9+lKFgeZJTDtbcveQ== +"@angular/compiler-cli@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-11.1.0.tgz#111f420a9ab9274947f805b9f41ddce8f24070ea" + integrity sha512-PLeVrqBpn43G7DeBkDQqH38Y+VMlCIbxiP4Vv1rFAmKVNIm9J8m8jdC3EQSTXVV+L3oDCVP5/ERSCZ8Jqx6UoA== dependencies: "@babel/core" "^7.8.6" "@babel/types" "^7.8.6" @@ -414,12 +436,12 @@ source-map "^0.6.1" sourcemap-codec "^1.4.8" tslib "^2.0.0" - yargs "15.3.0" + yargs "^16.1.1" -"@angular/compiler@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.0.0.tgz#b49997d0130e7c8cfe84fa73e5610892f4a772af" - integrity sha512-I7wVhdqvhtBTQTtW61z0lwPb1LiQQ0NOwjsbfN5sAc7/uwxw7em+Kyb/XJgBwgaTKtAL8bZEzdoQGLdsSKQF2g== +"@angular/compiler@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.1.0.tgz#0e2e18fe0cb64ec696986f12f80debcbdccc8d20" + integrity sha512-XW+McH/RVjpLtNkft6UYZQbjhXwX/hvLgUa9jGlTuIFM5o7W4XRPnq5sfn3+QvzdROF0j8S5sy47mGVNQOYMNg== dependencies: tslib "^2.0.0" @@ -433,24 +455,24 @@ resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.0.0.tgz#227dc53e1ac81824f998c6e76000b7efc522641e" integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w== -"@angular/core@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.0.7.tgz#4d81b1a49d3d4aaeb0ef9908695a3cc06707cfd9" - integrity sha512-Kj5uRZoK5+xfMTjkP3tw8oIF5hKTnoF9Bwh5m9GUKqg1wHVKOJcT5JBIEMc8qPyiFgALREA01reIzQdGMjX36A== +"@angular/core@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.1.0.tgz#724c13c53e4afb6d4ef4d9236d50f0be956f57ca" + integrity sha512-VhiRWZEj9Q/OvbbSDcgQ4f53oVcMnDB4uNL8xaWnK0Sb3lZA4aQW3VOlROBITS5n2g7D1zRhvUzdfzVuyuMIaQ== dependencies: tslib "^2.0.0" -"@angular/forms@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.0.7.tgz#958558b92d2e524e08b447c16b17b2ae42717657" - integrity sha512-+3A+SciMyHTdUwkKUz4XzC1DSYexQEbFLe0PKQIFSFOROmbssjnWJv7yO2HbzCpGa7oGKPYNlE5twYWyLxpvFg== +"@angular/forms@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.1.0.tgz#6ab2e81df80dc9a9d0898d2a57270de5d9eb30e5" + integrity sha512-pHwLPGDHk3JOoK2nA3wJoDCJF2bn8NmVqv8Lh5Pd8NYqLFRIIDiHSjNkqr1eM0JUmExqfU5tCrLrPz4YChdYBA== dependencies: tslib "^2.0.0" -"@angular/language-service@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-11.0.7.tgz#78a912eaf025c2d01181dc72ff4fb99ca598ae49" - integrity sha512-1IiJNwy/phjpYfqLVlhOp4Gr/A89joydwqPB7Nf7hbhl3xFnT98GOp/nsoZCwMBKotXYNk93m025LbJ++augfQ== +"@angular/language-service@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-11.1.0.tgz#d5a435f10ee8b79ceaf32352baaa104c089171c7" + integrity sha512-7NQcwNHgUGOdqQsyp1Xw/WFbYvC4WA+Et2DJJvkitmg2ejndtm45FALUu1Z2X6bbKzdJOuNGU5vNh1ZJ/IyGRQ== "@angular/localize@~10.0.10": version "10.0.14" @@ -461,33 +483,33 @@ glob "7.1.2" yargs "15.3.0" -"@angular/localize@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-11.0.7.tgz#0335f1fc4852d6d36d99d239ef946645b34c78fb" - integrity sha512-NDs08oAELLn7tA/hHLuW8APULg25C7iINYTA168QzOdFTEsJ2MoLf3SiVQExUV65h3MnB24xNbhaNodmBKUNPg== +"@angular/localize@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/localize/-/localize-11.1.0.tgz#96acfd2078387bb7dca10b88275e1f5263f6da33" + integrity sha512-hF0c+EeorSWiGTB+rzQn+KSewLb7LTyCN4IjezFF05pIAwyw1cLN+3fhiTmJ/KNp8PFpR7dbW3gPwUKkLwn3rg== dependencies: "@babel/core" "7.8.3" glob "7.1.2" yargs "^16.1.1" -"@angular/platform-browser-dynamic@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.7.tgz#8ee8c72799a96eee3967596ae52b69ee90ac3df0" - integrity sha512-pUXCum1Z2DZV/34WR4Vfmkc5nWxbmVdwAA9pXbAarwAYqHIqOzX8rpRaHsuHBAR+SK+VH+xjproeLgsVfV8FSA== +"@angular/platform-browser-dynamic@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.1.0.tgz#dc0d5a669c3cbe40b9f7442fca9c5335fc735f80" + integrity sha512-1MFRvjbkogtEQO/bWkNm2xOIl8CeIJuRWoXYE00VKShmq4o+2kTHBRQD0NydPQYwqo9o4XpgmIrJXHgwp3S2Qw== dependencies: tslib "^2.0.0" -"@angular/platform-browser@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.0.7.tgz#1475540b38d1e2d19dbd9242e0a9ddf6d9bf7873" - integrity sha512-W8Wt8jMUjcbpqGtqrNWAj0p7CLdjOxgVlbrgBXTbaoqdchvXH85YzGr7ohA3MuE61H90OcVK9OhfYQk5o6joSg== +"@angular/platform-browser@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.1.0.tgz#4c538d0bffdac02d8529bdb6247ae06a7685c3bf" + integrity sha512-wdinsRiKC5mGWWSA5RqferFvpe3Wr9YIVK2Gaj50DlJGOJ/8yWvux3BYjsCd5B44PC8+6dxUEZMgvA6CmhXgpw== dependencies: tslib "^2.0.0" -"@angular/router@~11.0.0": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.0.7.tgz#c5d0cacb927018eb495a4538ab1f81c088f5b7f1" - integrity sha512-oh/MOPRSOCLRPsM/3CVUNYZ3pz3g+CzLOk5Vad/zFJmnGwjA/lQGJo2pl7VXVq3RF7MieaHlDWG5TexGlXAP5w== +"@angular/router@~11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.1.0.tgz#62523ab838cf37ce911edaf3bfd3e2a4edb11675" + integrity sha512-jsGuyt/QNxtN2eHrkk6lqRnTf3NeuaxBWJSrwuoqrjLCZH2elg3r1GXDTII1Ih3E1zIwuOlK59O78dXW2eQVBg== dependencies: tslib "^2.0.0" @@ -513,30 +535,29 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.12.5": +"@babel/compat-data@^7.12.5", "@babel/compat-data@^7.12.7": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.7.tgz#9329b4782a7d6bbd7eef57e11addf91ee3ef1e41" integrity sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw== -"@babel/core@7.12.3": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" - integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== +"@babel/core@7.12.10", "@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.8.6": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd" + integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w== dependencies: "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" + "@babel/generator" "^7.12.10" "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.1" - "@babel/parser" "^7.12.3" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" json5 "^2.1.2" lodash "^4.17.19" - resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" @@ -561,37 +582,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.8.6": - version "7.12.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd" - integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.10" - "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.5" - "@babel/parser" "^7.12.10" - "@babel/template" "^7.12.7" - "@babel/traverse" "^7.12.10" - "@babel/types" "^7.12.10" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/generator@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" - integrity sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg== - dependencies: - "@babel/types" "^7.12.1" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.12.1", "@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.8.3": +"@babel/generator@7.12.11", "@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.8.3": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af" integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA== @@ -615,7 +606,7 @@ "@babel/helper-explode-assignable-expression" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helper-compilation-targets@^7.12.1": +"@babel/helper-compilation-targets@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz#cb470c76198db6a24e9dbc8987275631e5d29831" integrity sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw== @@ -690,7 +681,7 @@ dependencies: "@babel/types" "^7.12.7" -"@babel/helper-module-imports@^7.12.1": +"@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.5": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== @@ -769,7 +760,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== -"@babel/helper-validator-option@^7.12.1": +"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f" integrity sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw== @@ -784,7 +775,7 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/helpers@^7.12.1", "@babel/helpers@^7.12.5", "@babel/helpers@^7.8.3": +"@babel/helpers@^7.12.5", "@babel/helpers@^7.8.3": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== @@ -802,7 +793,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.3", "@babel/parser@^7.12.7", "@babel/parser@^7.8.3": +"@babel/parser@^7.1.0", "@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.8.3": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79" integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg== @@ -864,7 +855,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" -"@babel/plugin-proposal-numeric-separator@^7.12.1": +"@babel/plugin-proposal-numeric-separator@^7.12.7": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz#8bf253de8139099fea193b297d23a9d406ef056b" integrity sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ== @@ -889,7 +880,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" -"@babel/plugin-proposal-optional-chaining@^7.12.1": +"@babel/plugin-proposal-optional-chaining@^7.12.7": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz#e02f0ea1b5dc59d401ec16fb824679f683d3303c" integrity sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA== @@ -1035,7 +1026,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-block-scoping@^7.12.1": +"@babel/plugin-transform-block-scoping@^7.12.11": version "7.12.12" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz#d93a567a152c22aea3b1929bb118d1d0a175cdca" integrity sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ== @@ -1210,14 +1201,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz#04b792057eb460389ff6a4198e377614ea1e7ba5" - integrity sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg== +"@babel/plugin-transform-runtime@7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.10.tgz#af0fded4e846c4b37078e8e5d06deac6cd848562" + integrity sha512-xOrUfzPxw7+WDm9igMgQCbO3cJKymX7dFdsgRr1eu9n3KjjyU4pptIXbXPseQDquw+W+RuJEJMHKHNsPNNm3CA== dependencies: - "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-module-imports" "^7.12.5" "@babel/helper-plugin-utils" "^7.10.4" - resolve "^1.8.1" semver "^5.5.1" "@babel/plugin-transform-shorthand-properties@^7.12.1": @@ -1235,7 +1225,7 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" -"@babel/plugin-transform-sticky-regex@^7.12.1": +"@babel/plugin-transform-sticky-regex@^7.12.7": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz#560224613ab23987453948ed21d0b0b193fa7fad" integrity sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg== @@ -1249,7 +1239,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-transform-typeof-symbol@^7.12.1": +"@babel/plugin-transform-typeof-symbol@^7.12.10": version "7.12.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz#de01c4c8f96580bd00f183072b0d0ecdcf0dec4b" integrity sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA== @@ -1271,16 +1261,16 @@ "@babel/helper-create-regexp-features-plugin" "^7.12.1" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/preset-env@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" - integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== +"@babel/preset-env@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.11.tgz#55d5f7981487365c93dbbc84507b1c7215e857f9" + integrity sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw== dependencies: - "@babel/compat-data" "^7.12.1" - "@babel/helper-compilation-targets" "^7.12.1" - "@babel/helper-module-imports" "^7.12.1" + "@babel/compat-data" "^7.12.7" + "@babel/helper-compilation-targets" "^7.12.5" + "@babel/helper-module-imports" "^7.12.5" "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-validator-option" "^7.12.1" + "@babel/helper-validator-option" "^7.12.11" "@babel/plugin-proposal-async-generator-functions" "^7.12.1" "@babel/plugin-proposal-class-properties" "^7.12.1" "@babel/plugin-proposal-dynamic-import" "^7.12.1" @@ -1288,10 +1278,10 @@ "@babel/plugin-proposal-json-strings" "^7.12.1" "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" - "@babel/plugin-proposal-numeric-separator" "^7.12.1" + "@babel/plugin-proposal-numeric-separator" "^7.12.7" "@babel/plugin-proposal-object-rest-spread" "^7.12.1" "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" - "@babel/plugin-proposal-optional-chaining" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.7" "@babel/plugin-proposal-private-methods" "^7.12.1" "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" "@babel/plugin-syntax-async-generators" "^7.8.0" @@ -1309,7 +1299,7 @@ "@babel/plugin-transform-arrow-functions" "^7.12.1" "@babel/plugin-transform-async-to-generator" "^7.12.1" "@babel/plugin-transform-block-scoped-functions" "^7.12.1" - "@babel/plugin-transform-block-scoping" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.11" "@babel/plugin-transform-classes" "^7.12.1" "@babel/plugin-transform-computed-properties" "^7.12.1" "@babel/plugin-transform-destructuring" "^7.12.1" @@ -1333,14 +1323,14 @@ "@babel/plugin-transform-reserved-words" "^7.12.1" "@babel/plugin-transform-shorthand-properties" "^7.12.1" "@babel/plugin-transform-spread" "^7.12.1" - "@babel/plugin-transform-sticky-regex" "^7.12.1" + "@babel/plugin-transform-sticky-regex" "^7.12.7" "@babel/plugin-transform-template-literals" "^7.12.1" - "@babel/plugin-transform-typeof-symbol" "^7.12.1" + "@babel/plugin-transform-typeof-symbol" "^7.12.10" "@babel/plugin-transform-unicode-escapes" "^7.12.1" "@babel/plugin-transform-unicode-regex" "^7.12.1" "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.12.1" - core-js-compat "^3.6.2" + "@babel/types" "^7.12.11" + core-js-compat "^3.8.0" semver "^5.5.0" "@babel/preset-modules@^0.1.3": @@ -1354,30 +1344,14 @@ "@babel/types" "^7.4.4" esutils "^2.0.2" -"@babel/runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" - integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": +"@babel/runtime@7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.3.3", "@babel/template@^7.8.3": +"@babel/template@7.12.7", "@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.3.3", "@babel/template@^7.8.3": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== @@ -2473,14 +2447,14 @@ jquery "3.5.0" replace-in-file "^4.1.3" -"@ngtools/webpack@11.0.6": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.0.6.tgz#1a1d7775022e7e6263f8d9ee2872d995163b3fc0" - integrity sha512-vf5YNEpXWRa0fKC/BRq5sVVj2WnEqW8jn14YQRHwVt5ppUeyu8IKUF69p6W1MwZMgMqMaw/vPQ8LI5cFbyf3uw== +"@ngtools/webpack@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.1.0.tgz#fceca46969e3963c17dc0f42bba92a93c4296d6d" + integrity sha512-6KRuSCwDEtwht3mdo9jps01u00675sv9lovlIQ9eIluPq32GM0BweDtxpS/CPgON/Hp9I5Aqtb3z2obnU3EQ7Q== dependencies: - "@angular-devkit/core" "11.0.6" - enhanced-resolve "5.3.1" - webpack-sources "2.0.1" + "@angular-devkit/core" "11.1.0" + enhanced-resolve "5.6.0" + webpack-sources "2.2.0" "@ngx-validate/core@^0.0.13": version "0.0.13" @@ -2536,6 +2510,36 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" +"@npmcli/ci-detect@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a" + integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q== + +"@npmcli/git@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.0.4.tgz#725f5e32864f3849420e84baf130e426a707cbb7" + integrity sha512-OJZCmJ9DNn1cz9HPXXsPmUBnqaArot3CGYo63CyajHQk+g87rPXVOJByGsskQJhPsUUEXJcsZ2Q6bWd2jSwnBA== + dependencies: + "@npmcli/promise-spawn" "^1.1.0" + lru-cache "^6.0.0" + mkdirp "^1.0.3" + npm-pick-manifest "^6.0.0" + promise-inflight "^1.0.1" + promise-retry "^1.1.1" + semver "^7.3.2" + unique-filename "^1.1.1" + which "^2.0.2" + +"@npmcli/installed-package-contents@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.5.tgz#cc78565e55d9f14d46acf46a96f70934e516fa3d" + integrity sha512-aKIwguaaqb6ViwSOFytniGvLPb9SMCUm39TgM3SfUo7n0TxUMbwoXfpwyvQ4blm10lzbAwTsvjr7QZ85LvTi4A== + dependencies: + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + read-package-json-fast "^1.1.1" + readdir-scoped-modules "^1.1.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" @@ -2543,6 +2547,30 @@ dependencies: mkdirp "^1.0.4" +"@npmcli/node-gyp@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.1.tgz#dedc4ea9b3c6ef207081ebcd82c053ef60edc478" + integrity sha512-pBqoKPWmuk9iaEcXlLBVRIA6I1kG9JiICU+sG0NuD6NAR461F+02elHJS4WkQxHW2W5rnsfvP/ClKwmsZ9RaaA== + +"@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.0": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" + integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== + dependencies: + infer-owner "^1.0.4" + +"@npmcli/run-script@^1.3.0": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-1.8.1.tgz#729c5ac7293f250b654504d263952703af6da39c" + integrity sha512-G8c86g9cQHyRINosIcpovzv0BkXQc3urhL1ORf3KTe4TS4UBsg2O4Z2feca/W3pfzdHEJzc83ETBW4aKbb3SaA== + dependencies: + "@npmcli/node-gyp" "^1.0.0" + "@npmcli/promise-spawn" "^1.3.0" + infer-owner "^1.0.4" + node-gyp "^7.1.0" + puka "^1.0.1" + read-package-json-fast "^1.1.3" + "@octokit/auth-token@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.4.tgz#ee31c69b01d0378c12fd3ffe406030f3d94d3b56" @@ -2699,14 +2727,14 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@schematics/angular@11.0.6": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.0.6.tgz#5e52f8396e66138df0d6062130399fab830ee79e" - integrity sha512-XUcpOrlcp55PBHrgpIVx69lnhDY6ro35BSRmqNmjXik56qcOkfvdki8vvyW9EsWvu9/sfBSsVDdparlbVois7w== +"@schematics/angular@11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.1.0.tgz#a0aa7bccd395be9c336089e423399f68cc6054af" + integrity sha512-g04TcC1gLS1ptFYdIO7qZ+lJARBXoK9g+m5KzQoogdrDkCum+wN15bfYKc5hF8cDYQOOqYjRF5GBdS9Uvc4ulQ== dependencies: - "@angular-devkit/core" "11.0.6" - "@angular-devkit/schematics" "11.0.6" - jsonc-parser "2.3.1" + "@angular-devkit/core" "11.1.0" + "@angular-devkit/schematics" "11.1.0" + jsonc-parser "3.0.0" "@schematics/angular@~10.0.5": version "10.0.8" @@ -2724,18 +2752,18 @@ "@angular-devkit/core" "10.1.7" "@angular-devkit/schematics" "10.1.7" -"@schematics/update@0.1100.6": - version "0.1100.6" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1100.6.tgz#8e76276a3daecfd698b39e7643bc21f3abb3a4d0" - integrity sha512-+B8n+k+zZ3VYOhjNBsLqzjp8O9ZdUWgdpf9L8XAA7mh/oPwufXpExyEc66uAS07imvUMmjz6i8E2eNWV/IjBJg== +"@schematics/update@0.1101.0": + version "0.1101.0" + resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1101.0.tgz#7aceee250f0bb917ea567c7fc9145c33182eaea0" + integrity sha512-WcbiTcn+Rr1uYllTLMbYcMfiz5IRZ7OAapISA82DpDkBeYIUf86ANjkEK1qsuU3zoz5i3CbiWvPq/KTlrzh6dg== dependencies: - "@angular-devkit/core" "11.0.6" - "@angular-devkit/schematics" "11.0.6" + "@angular-devkit/core" "11.1.0" + "@angular-devkit/schematics" "11.1.0" "@yarnpkg/lockfile" "1.1.0" - ini "1.3.6" + ini "2.0.0" npm-package-arg "^8.0.0" - pacote "9.5.12" - semver "7.3.2" + pacote "11.1.14" + semver "7.3.4" semver-intersect "1.4.0" "@sheerun/mutationobserver-shim@^0.3.2": @@ -2786,6 +2814,11 @@ pretty-format "^24.8.0" wait-for-expect "^1.3.0" +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + "@types/babel__core@^7.1.7": version "7.1.12" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d" @@ -3281,6 +3314,13 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + agent-base@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" @@ -3295,6 +3335,15 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +agentkeepalive@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.3.tgz#360a09d743a1f4fde749f9ba07caa6575d08259a" + integrity sha512-wn8fw19xKZwdGPO47jivonaHRTd+nGOMP1z11sgGeQzDy2xd5FG0R67dIMcKHDE2cJ5y+YXV30XVGUBPRSY7Hg== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -3649,7 +3698,19 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@9.8.6, autoprefixer@^9.6.5: +autoprefixer@10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.1.tgz#ce96870df6ddd9ba4c9bbba56c54b9ef4b00a962" + integrity sha512-dwP0UjyYvROUvtU+boBx8ff5pPWami1NGTrJs9YUsS/oZVbRAcdNHOOuXSA1fc46tgKqe072cVaKD69rvCc3QQ== + dependencies: + browserslist "^4.16.1" + caniuse-lite "^1.0.30001173" + colorette "^1.2.1" + fraction.js "^4.0.13" + normalize-range "^0.1.2" + postcss-value-parser "^4.1.0" + +autoprefixer@^9.6.5: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== @@ -3693,15 +3754,14 @@ babel-jest@^25.5.1: graceful-fs "^4.2.4" slash "^3.0.0" -babel-loader@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" - integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== +babel-loader@8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" + integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g== dependencies: - find-cache-dir "^2.1.0" + find-cache-dir "^3.3.1" loader-utils "^1.4.0" - mkdirp "^0.5.3" - pify "^4.0.1" + make-dir "^3.1.0" schema-utils "^2.6.5" babel-plugin-dynamic-import-node@^2.3.3: @@ -3996,7 +4056,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.7.0, browserslist@^4.9.1: +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.1, browserslist@^4.7.0, browserslist@^4.9.1: version "4.16.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.1.tgz#bf757a2da376b3447b800a16f0f1c96358138766" integrity sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA== @@ -4265,7 +4325,7 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -camelcase@^6.0.0: +camelcase@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== @@ -4430,10 +4490,10 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -circular-dependency-plugin@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz#e09dbc2dd3e2928442403e2d45b41cea06bc0a93" - integrity sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw== +circular-dependency-plugin@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600" + integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ== class-utils@^0.3.5: version "0.3.6" @@ -5031,10 +5091,10 @@ 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@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz#8015e4d5c5e637ab7b39c76daa9e03c7a4bf1ae5" - integrity sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q== +copy-webpack-plugin@6.3.2: + version "6.3.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.3.2.tgz#0e920a6c181a5052aa6e2861b164bda03f83afeb" + integrity sha512-MgJ1uouLIbDg4ST1GzqrGQyKoXY5iPqi6fghFqarijam7FQcBa/r6Rg0VkoIuzx75Xq8iAMghyOueMkWUQ5OaA== dependencies: cacache "^15.0.5" fast-glob "^3.2.4" @@ -5048,18 +5108,18 @@ copy-webpack-plugin@6.2.1: serialize-javascript "^5.0.1" webpack-sources "^1.4.3" -core-js-compat@^3.6.2: - version "3.8.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.8.2.tgz#3717f51f6c3d2ebba8cbf27619b57160029d1d4c" - integrity sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ== +core-js-compat@^3.8.0: + version "3.8.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.8.3.tgz#9123fb6b9cad30f0651332dc77deba48ef9b0b3f" + integrity sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog== dependencies: - browserslist "^4.16.0" + browserslist "^4.16.1" semver "7.0.0" -core-js@3.6.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" - integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== +core-js@3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.2.tgz#0a1fd6709246da9ca8eff5bb0cbd15fba9ac7044" + integrity sha512-FfApuSRgrR6G5s58casCBd9M2k+4ikuu4wbW6pJyYU7bd9zvFc9qf7vr5xmrZOhT9nn+8uwlH1oRR9jTnFoA3A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -5118,6 +5178,17 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +critters@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/critters/-/critters-0.0.6.tgz#b71384113d8b5f5c82f3aeba80c122437f195d8c" + integrity sha512-NUB3Om7tkf+XWi9+2kJ2A3l4/tHORDI1UT+nHxUqay2B/tJvMpiXcklDDLBH3fPn9Pe23uu0we/08Ukjy4cLCQ== + dependencies: + chalk "^4.1.0" + css "^3.0.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + pretty-bytes "^5.3.0" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -5168,22 +5239,22 @@ css-declaration-sorter@^4.0.1: postcss "^7.0.1" timsort "^0.3.0" -css-loader@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.3.0.tgz#c888af64b2a5b2e85462c72c0f4a85c7e2e0821e" - integrity sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg== +css-loader@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.0.1.tgz#9e4de0d6636a6266a585bd0900b422c85539d25f" + integrity sha512-cXc2ti9V234cq7rJzFKhirb2L2iPy8ZjALeVJAozXYz9te3r4eqLSixNAbMDJSgJEQywqXzs8gonxaboeKqwiw== dependencies: - camelcase "^6.0.0" + camelcase "^6.2.0" cssesc "^3.0.0" - icss-utils "^4.1.1" + icss-utils "^5.0.0" loader-utils "^2.0.0" - postcss "^7.0.32" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.3" - postcss-modules-scope "^2.2.0" - postcss-modules-values "^3.0.0" + postcss "^8.1.4" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" postcss-value-parser "^4.1.0" - schema-utils "^2.7.1" + schema-utils "^3.0.0" semver "^7.3.2" css-parse@~2.0.0: @@ -5247,6 +5318,15 @@ css@^2.0.0: source-map-resolve "^0.5.2" urix "^0.1.0" +css@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== + dependencies: + inherits "^2.0.4" + source-map "^0.6.1" + source-map-resolve "^0.6.0" + cssauron@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8" @@ -5467,27 +5547,20 @@ debug@3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== +debug@4, debug@4.3.1, debug@^4.1.0, debug@^4.1.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" -debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: +debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -5630,7 +5703,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@~1.1.2: +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -5882,7 +5955,7 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encoding@^0.1.11: +encoding@^0.1.11, encoding@^0.1.12: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -5896,13 +5969,13 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz#3f988d0d7775bdc2d96ede321dc81f8249492f57" - integrity sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w== +enhanced-resolve@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.6.0.tgz#ad19a1665f230a6e384724a30acf3f7332b2b3f0" + integrity sha512-C3GGDfFZmqUa21o10YRKbZN60DPl0HyXKXxoEnQMWso9u7KMU23L7CBHfr/rVxORddY/8YQZaU2MZ1ewTS8Pcw== dependencies: graceful-fs "^4.2.4" - tapable "^2.0.0" + tapable "^2.2.0" enhanced-resolve@^4.3.0: version "4.3.0" @@ -6384,14 +6457,7 @@ fastq@^1.6.0: 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" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - -faye-websocket@~0.11.1: +faye-websocket@^0.11.3: version "0.11.3" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== @@ -6429,10 +6495,10 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" -file-loader@6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.1.tgz#a6f29dfb3f5933a1c350b2dbaa20ac5be0539baa" - integrity sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw== +file-loader@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" @@ -6590,6 +6656,11 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +fraction.js@^4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.13.tgz#3c1c315fa16b35c85fffa95725a36fa729c69dfe" + integrity sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -6645,7 +6716,7 @@ fs-minipass@^1.2.5: dependencies: minipass "^2.6.0" -fs-minipass@^2.0.0: +fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== @@ -7002,7 +7073,7 @@ got@^11.5.2: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -7209,7 +7280,7 @@ http-cache-semantics@^3.8.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -7264,6 +7335,15 @@ http-proxy-agent@^2.1.0: agent-base "4" debug "3.1.0" +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" @@ -7313,6 +7393,14 @@ https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -7325,7 +7413,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -7339,12 +7427,10 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" +icss-utils@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" @@ -7356,7 +7442,7 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -ignore-walk@^3.0.1: +ignore-walk@^3.0.1, ignore-walk@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== @@ -7470,10 +7556,10 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.6.tgz#f1c46a2a93a253e7b3905115e74d527cd23061a1" - integrity sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== ini@^1.3.2, ini@^1.3.4: version "1.3.8" @@ -7663,7 +7749,7 @@ is-color-stop@^1.0.0: rgb-regex "^1.0.1" rgba-regex "^1.0.0" -is-core-module@^2.0.0, is-core-module@^2.1.0: +is-core-module@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== @@ -7780,6 +7866,11 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -8446,10 +8537,10 @@ jest-watcher@^25.5.0: jest-util "^25.5.0" string-length "^3.1.0" -jest-worker@26.5.0: - version "26.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.5.0.tgz#87deee86dbbc5f98d9919e0dadf2c40e3152fa30" - integrity sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug== +jest-worker@26.6.2, jest-worker@^26.5.0: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: "@types/node" "*" merge-stream "^2.0.0" @@ -8463,15 +8554,6 @@ jest-worker@^25.5.0: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^26.5.0: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - jest@^25.0.0: version "25.5.4" resolved "https://registry.yarnpkg.com/jest/-/jest-25.5.4.tgz#f21107b6489cfe32b076ce2adcadee3587acb9db" @@ -8576,7 +8658,7 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json3@^3.3.2: +json3@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== @@ -8595,7 +8677,12 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -jsonc-parser@2.3.1, jsonc-parser@^2.3.0: +jsonc-parser@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== + +jsonc-parser@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== @@ -8616,7 +8703,7 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonparse@^1.2.0: +jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= @@ -8728,20 +8815,22 @@ lerna@^3.19.0: import-local "^2.0.0" npmlog "^4.1.2" -less-loader@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-7.0.2.tgz#0d73a49ec32a9d3ff12614598e6e2b47fb2a35c4" - integrity sha512-7MKlgjnkCf63E3Lv6w2FvAEgLMx3d/tNBExITcanAq7ys5U8VPWT3F6xcRjYmdNfkoQ9udoVFb1r2azSiTnD6w== +less-loader@7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-7.2.1.tgz#a923df8567256751b0ab4e0c3eecff10fd0a5876" + integrity sha512-4v83WZ7KGbluOWPgk3iNjreAaJDNStfmmdfJbQIib3Jlc8mejV3w6A9xU+EkaivjBVqwQEK0y8cFthyNeGnrTQ== dependencies: klona "^2.0.4" loader-utils "^2.0.0" schema-utils "^3.0.0" -less@3.12.2: - version "3.12.2" - resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" - integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== +less@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/less/-/less-4.1.0.tgz#a12708d1951239db1c9d7eaa405f1ebac9a75b8d" + integrity sha512-w1Ag/f34g7LwtQ/sMVSGWIyZx+gG9ZOAEtyxeX1fG75is6BMyC2lD5kG+1RueX7PkAvlQBm2Lf2aN2j0JbVr2A== dependencies: + copy-anything "^2.0.1" + parse-node-version "^1.0.1" tslib "^1.10.0" optionalDependencies: errno "^0.1.1" @@ -8749,7 +8838,7 @@ less@3.12.2: image-size "~0.5.0" make-dir "^2.1.0" mime "^1.4.1" - native-request "^1.0.5" + needle "^2.5.2" source-map "~0.6.0" less@^3.10.3: @@ -8781,10 +8870,10 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -license-webpack-plugin@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz#08eddb2f776c7c64c02f308a00e017d6e824d0b6" - integrity sha512-yhqTmlYIEpZWA122lf6E0G8+rkn0AzoQ1OpzUKKs/lXUqG1plmGnwmkuuPlfggzJR5y6DLOdot/Tv00CC51CeQ== +license-webpack-plugin@2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.11.tgz#0d93188a31fce350a44c86212badbaf33dcd29d8" + integrity sha512-0iVGoX5vx0WDy8dmwTTpOOMYiGqILyUbDeVMFH52AjgBlS58lHwOlFMSoqg5nY8Kxl6+FRKyUZY/UdlQaOyqDw== dependencies: "@types/webpack-sources" "^0.1.5" webpack-sources "^1.2.0" @@ -9052,7 +9141,7 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.0.2: +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -9081,6 +9170,27 @@ make-fetch-happen@^5.0.0: socks-proxy-agent "^4.0.0" ssri "^6.0.0" +make-fetch-happen@^8.0.9: + version "8.0.13" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-8.0.13.tgz#3692e1fdf027343c782e53bfe1f941fe85db9462" + integrity sha512-rQ5NijwwdU8tIaBrpTtSVrNCcAJfyDRcKBC76vOQlyJX588/88+TE+UpjWl4BgG7gCkp29wER7xcRqkeg+x64Q== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.0.5" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + promise-retry "^1.1.1" + socks-proxy-agent "^5.0.0" + ssri "^8.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -9317,10 +9427,10 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -mini-css-extract-plugin@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.2.1.tgz#30ea7dee632b3002b0c77aeed447790408cb247e" - integrity sha512-G3yw7/TQaPfkuiR73MDcyiqhyP8SnbmLhUbpC76H+wtQxA6wfKhMCQOCb6wnPK0dQbjORAeOILQqEesg4/wF7A== +mini-css-extract-plugin@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.3.tgz#7802e62b34199aa7d1a62e654395859a836486a0" + integrity sha512-7lvliDSMiuZc81kI+5/qxvn47SCM7BehXex3f2c6l/pR3Goj58IQxZh9nuPQ3AkGQgoETyXuIqLDaO5Oa0TyBw== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" @@ -9372,6 +9482,17 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" +minipass-fetch@^1.3.0, minipass-fetch@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.3.3.tgz#34c7cea038c817a8658461bf35174551dce17a0a" + integrity sha512-akCrLDWfbdAWkMLBxJEeWTdNsjML+dt5YgOI4gJ53vuO0vrmYQkUPxa6j6V65s9CcePIr2SSWqjT2EcrNseryQ== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" @@ -9379,13 +9500,28 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" -minipass-pipeline@^1.2.2: +minipass-json-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + dependencies: + jsonparse "^1.3.1" + minipass "^3.0.0" + +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -9394,7 +9530,7 @@ minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -minipass@^3.0.0, minipass@^3.1.1: +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== @@ -9408,7 +9544,7 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" -minizlib@^2.1.1: +minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -9555,6 +9691,11 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== +nanoid@^3.1.20: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -9582,6 +9723,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.6.0.tgz#24dbb55f2509e2324b4a99d61f413982013ccdbe" + integrity sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -9693,6 +9843,22 @@ node-gyp@^5.0.2: tar "^4.4.12" which "^1.3.1" +node-gyp@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.3" + nopt "^5.0.0" + npmlog "^4.1.2" + request "^2.88.2" + rimraf "^3.0.2" + semver "^7.3.2" + tar "^6.0.2" + which "^2.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -9763,6 +9929,13 @@ nopt@^4.0.1: abbrev "1" osenv "^0.1.4" +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5, normalize-package-data@^2.4.0, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -9810,7 +9983,7 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== -npm-bundled@^1.0.1: +npm-bundled@^1.0.1, npm-bundled@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== @@ -9843,7 +10016,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-package-arg@8.1.0, npm-package-arg@^8.0.0: +npm-package-arg@8.1.0, npm-package-arg@^8.0.0, npm-package-arg@^8.0.1: version "8.1.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.0.tgz#b5f6319418c3246a1c38e1a8fbaa06231bc5308f" integrity sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig== @@ -9862,7 +10035,7 @@ npm-package-arg@8.1.0, npm-package-arg@^8.0.0: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.12, npm-packlist@^1.4.4: +npm-packlist@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== @@ -9871,7 +10044,17 @@ npm-packlist@^1.1.12, npm-packlist@^1.4.4: npm-bundled "^1.0.1" npm-normalize-package-bin "^1.0.1" -npm-pick-manifest@6.1.0: +npm-packlist@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.4.tgz#40e96b2b43787d0546a574542d01e066640d09da" + integrity sha512-Qzg2pvXC9U4I4fLnUrBmcIT4x0woLtUgxUi9eC+Zrcv1Xx5eamytGAfbDWQ67j7xOcQ2VW1I3su9smVTIdu7Hw== + dependencies: + glob "^7.1.6" + ignore-walk "^3.0.3" + npm-bundled "^1.1.1" + npm-normalize-package-bin "^1.0.1" + +npm-pick-manifest@6.1.0, npm-pick-manifest@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== @@ -9889,18 +10072,19 @@ npm-pick-manifest@^3.0.0: npm-package-arg "^6.0.0" semver "^5.4.1" -npm-registry-fetch@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz#57951bf6541e0246b34c9f9a38ab73607c9449d7" - integrity sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ== +npm-registry-fetch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661" + integrity sha512-PuFYYtnQ8IyVl6ib9d3PepeehcUeHN9IO5N/iCRhyg9tStQcqGQBRVHmfmMWPDERU3KwZoHFvbJ4FPXPspvzbA== dependencies: - JSONStream "^1.3.4" - bluebird "^3.5.1" - figgy-pudding "^3.4.1" - lru-cache "^5.1.1" - make-fetch-happen "^5.0.0" - npm-package-arg "^6.1.0" - safe-buffer "^5.2.0" + "@npmcli/ci-detect" "^1.0.0" + lru-cache "^6.0.0" + make-fetch-happen "^8.0.9" + minipass "^3.1.3" + minipass-fetch "^1.3.0" + minipass-json-stream "^1.0.1" + minizlib "^2.0.0" + npm-package-arg "^8.0.0" npm-run-path@^2.0.0: version "2.0.2" @@ -10071,10 +10255,10 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -open@7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69" - integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw== +open@7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/open/-/open-7.3.1.tgz#111119cb919ca1acd988f49685c4fdd0f4755356" + integrity sha512-f2wt9DCBKKjlFbjzGb8MOAW8LH8F0mrs1zc7KTjAJ9PZNQbfenzWbNP1VZJvw6ICMG9r14Ah6yfwPn7T7i646A== dependencies: is-docker "^2.0.0" is-wsl "^2.1.1" @@ -10140,19 +10324,7 @@ ora@5.1.0: strip-ansi "^6.0.0" wcwidth "^1.0.1" -ora@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== - dependencies: - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-spinners "^2.0.0" - log-symbols "^2.2.0" - strip-ansi "^5.2.0" - wcwidth "^1.0.1" - -ora@^5.1.0: +ora@5.2.0, ora@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ora/-/ora-5.2.0.tgz#de10bfd2d15514384af45f3fa9d9b1aaf344fda1" integrity sha512-+wG2v8TUU8EgzPHun1k/n45pXquQ9fHnbXVetl9rRgO6kjZszGGbraF3XPTIdgeA+s1lbRjSEftAnyT0w8ZMvQ== @@ -10166,6 +10338,18 @@ ora@^5.1.0: strip-ansi "^6.0.0" wcwidth "^1.0.1" +ora@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" + strip-ansi "^5.2.0" + wcwidth "^1.0.1" + original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -10326,41 +10510,30 @@ p-waterfall@^1.0.0: dependencies: p-reduce "^1.0.0" -pacote@9.5.12: - version "9.5.12" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.5.12.tgz#1e11dd7a8d736bcc36b375a9804d41bb0377bf66" - integrity sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ== +pacote@11.1.14: + version "11.1.14" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.1.14.tgz#c60b9849ab05488d3f9ccd644c8a42543f2f36d6" + integrity sha512-6c5OhQelaJFDfiw/Zd8MfGCvvFHurSdeGzufZMPvRFImdbNOYFciOINf3DtUNUaU3h98eCb749UyHDsgvL19+A== dependencies: - bluebird "^3.5.3" - cacache "^12.0.2" - chownr "^1.1.2" - figgy-pudding "^3.5.1" - get-stream "^4.1.0" - glob "^7.1.3" + "@npmcli/git" "^2.0.1" + "@npmcli/installed-package-contents" "^1.0.5" + "@npmcli/promise-spawn" "^1.2.0" + "@npmcli/run-script" "^1.3.0" + cacache "^15.0.5" + chownr "^2.0.0" + fs-minipass "^2.1.0" infer-owner "^1.0.4" - lru-cache "^5.1.1" - make-fetch-happen "^5.0.0" - minimatch "^3.0.4" - minipass "^2.3.5" - mississippi "^3.0.0" - mkdirp "^0.5.1" - normalize-package-data "^2.4.0" - npm-normalize-package-bin "^1.0.0" - npm-package-arg "^6.1.0" - npm-packlist "^1.1.12" - npm-pick-manifest "^3.0.0" - npm-registry-fetch "^4.0.0" - osenv "^0.1.5" - promise-inflight "^1.0.1" + minipass "^3.1.3" + mkdirp "^1.0.3" + npm-package-arg "^8.0.1" + npm-packlist "^2.1.4" + npm-pick-manifest "^6.0.0" + npm-registry-fetch "^9.0.0" promise-retry "^1.1.1" - protoduck "^5.0.1" - rimraf "^2.6.2" - safe-buffer "^5.1.2" - semver "^5.6.0" - ssri "^6.0.1" - tar "^4.4.10" - unique-filename "^1.1.1" - which "^1.3.1" + read-package-json-fast "^1.1.3" + rimraf "^3.0.2" + ssri "^8.0.0" + tar "^6.1.0" pako@~1.0.2, pako@~1.0.5: version "1.0.11" @@ -10424,6 +10597,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -10457,6 +10635,13 @@ parse5-html-rewriting-stream@6.0.1: parse5 "^6.0.1" parse5-sax-parser "^6.0.1" +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + parse5-sax-parser@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz#98b4d366b5b266a7cd90b4b58906667af882daba" @@ -10718,20 +10903,19 @@ postcss-discard-overridden@^4.0.1: dependencies: postcss "^7.0.0" -postcss-import@12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-12.0.1.tgz#cf8c7ab0b5ccab5649024536e565f841928b7153" - integrity sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw== +postcss-import@14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.0.0.tgz#3ed1dadac5a16650bde3f4cdea6633b9c3c78296" + integrity sha512-gFDDzXhqr9ELmnLHgCC3TbGfA6Dm/YMb/UN8/f7Uuq4fL7VTk2vOIj6hwINEwbokEmp123bLD7a5m+E+KIetRg== dependencies: - postcss "^7.0.1" - postcss-value-parser "^3.2.3" + postcss-value-parser "^4.0.0" read-cache "^1.0.0" resolve "^1.1.7" -postcss-loader@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.0.4.tgz#b2d005b52e008a44991cf8123bee207e635eb53e" - integrity sha512-pntA9zIR14drQo84yGTjQJg1m7T0DkXR4vXYHBngiRZdJtEeCrojL6lOpqUanMzG375lIJbT4Yug85zC/AJWGw== +postcss-loader@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.1.0.tgz#4647a6c8dad3cb6b253fbfaa21d62201086f6e39" + integrity sha512-vbCkP70F3Q9PIk6d47aBwjqAMI4LfkXCoyxj+7NPNuVIwfTGdzv2KVQes59/RuxMniIgsYQCFSY42P3+ykJfaw== dependencies: cosmiconfig "^7.0.0" klona "^2.0.4" @@ -10801,38 +10985,33 @@ postcss-minify-selectors@^4.0.2: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" - integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== dependencies: - icss-utils "^4.1.1" - postcss "^7.0.32" + icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" + postcss-selector-parser "^6.0.4" -postcss-modules-values@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" + icss-utils "^5.0.0" postcss-normalize-charset@^4.0.1: version "4.0.1" @@ -10953,7 +11132,7 @@ postcss-selector-parser@^3.0.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== @@ -10993,12 +11172,12 @@ postcss-url@^8.0.0: postcss "^7.0.2" xxhashjs "^0.2.1" -postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3: +postcss-value-parser@^3.0.0: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== @@ -11012,16 +11191,16 @@ postcss@7.0.21: source-map "^0.6.1" supports-color "^6.1.0" -postcss@7.0.32: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== +postcss@8.2.4, postcss@^8.1.4: + version "8.2.4" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.4.tgz#20a98a39cf303d15129c2865a9ec37eda0031d04" + integrity sha512-kRFftRoExRVXZlwUuay9iC824qmXPcQQVzAjbCCgjpXnkdMCJYBu2gTwAaFBzv8ewND6O8xFb3aELmEkh9zTzg== dependencies: - chalk "^2.4.2" + colorette "^1.2.1" + nanoid "^3.1.20" source-map "^0.6.1" - supports-color "^6.1.0" -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.29, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.2, postcss@^7.0.27, postcss@^7.0.29, postcss@^7.0.32: version "7.0.35" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== @@ -11040,6 +11219,11 @@ prettier@^2.2.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +pretty-bytes@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.5.0.tgz#0cecda50a74a941589498011cf23275aa82b339e" + integrity sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA== + pretty-format@26.x, pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -11176,6 +11360,11 @@ public-encrypt@^4.0.0: randombytes "^2.0.1" safe-buffer "^5.1.2" +puka@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/puka/-/puka-1.0.1.tgz#a2df782b7eb4cf9564e4c93a5da422de0dfacc02" + integrity sha512-ssjRZxBd7BT3dte1RR3VoeT2cT/ODH8x+h0rUF1rMqB0srHYf48stSDWfiYakTp5UBZMxroZhB2+ExLDHm7W3g== + pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" @@ -11342,6 +11531,14 @@ read-cmd-shim@^1.0.1: dependencies: graceful-fs "^4.1.2" +read-package-json-fast@^1.1.1, read-package-json-fast@^1.1.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-1.2.1.tgz#e8518d6f37c99eb3afc26704c5cbb50d7ead82dd" + integrity sha512-OFbpwnHcv74Oa5YN5WvbOBfLw6yPmPcwvyJJw/tj9cWFBF7juQUDLDSZiOjEcgzfweWeeROOmbPpNN1qm4hcRg== + dependencies: + json-parse-even-better-errors "^2.3.0" + npm-normalize-package-bin "^1.0.1" + "read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@^2.0.13: version "2.1.2" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" @@ -11451,7 +11648,7 @@ read@1, read@~1.0.1: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== @@ -11755,15 +11952,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" - integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== - dependencies: - is-core-module "^2.0.0" - path-parse "^1.0.6" - -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.8.1: +resolve@1.19.0, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2: version "1.19.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== @@ -11872,14 +12061,7 @@ rollup-plugin-sourcemaps@^0.6.0: "@rollup/pluginutils" "^3.0.9" source-map-resolve "^0.6.0" -rollup@2.32.1: - version "2.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.32.1.tgz#625a92c54f5b4d28ada12d618641491d4dbb548c" - integrity sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw== - optionalDependencies: - fsevents "~2.1.2" - -rollup@^2.8.0: +rollup@2.36.1, rollup@^2.8.0: version "2.36.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.36.1.tgz#2174f0c25c7b400d57b05628d0e732c7ae8d2178" integrity sha512-eAfqho8dyzuVvrGqpR0ITgEdq0zG2QJeWYh+HeuTbpcaXk8vNFc48B7bJa1xYosTCKx0CuW+447oQOW8HgBIZQ== @@ -11973,10 +12155,10 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sass-loader@10.0.5: - version "10.0.5" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.5.tgz#f53505b5ddbedf43797470ceb34066ded82bb769" - integrity sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w== +sass-loader@10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" + integrity sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw== dependencies: klona "^2.0.4" loader-utils "^2.0.0" @@ -11984,10 +12166,10 @@ sass-loader@10.0.5: schema-utils "^3.0.0" semver "^7.3.2" -sass@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.27.0.tgz#0657ff674206b95ec20dc638a93e179c78f6ada2" - integrity sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig== +sass@1.32.4: + version "1.32.4" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.4.tgz#308bf29dd7f53d44ae4f06580e9a910ad9aa411e" + integrity sha512-N0BT0PI/t3+gD8jKa83zJJUb7ssfQnRRfqN+GIErokW6U4guBpfYl8qYB+OFLEho+QvnV5ZH1R9qhUC/Z2Ch9w== dependencies: chokidar ">=2.0.0 <4.0.0" @@ -12005,7 +12187,7 @@ saucelabs@^1.5.0: dependencies: https-proxy-agent "^2.2.1" -sax@>=0.6.0, sax@~1.2.4: +sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -12026,7 +12208,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.5, schema-utils@^2.7.0, schema-utils@^2.7.1: +schema-utils@^2.6.5, schema-utils@^2.7.0: version "2.7.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== @@ -12059,7 +12241,7 @@ selenium-webdriver@3.6.0, selenium-webdriver@^3.0.1: tmp "0.0.30" xml2js "^0.4.17" -selfsigned@^1.10.7: +selfsigned@^1.10.8: version "1.10.8" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== @@ -12090,12 +12272,7 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2: +semver@7.3.4, semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2: version "7.3.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== @@ -12323,26 +12500,26 @@ snq@^1.0.3: resolved "https://registry.yarnpkg.com/snq/-/snq-1.0.3.tgz#f9661d10eebb224c52fc3c50106445c268618168" integrity sha512-bXcxd1ppFnSNYKq84HyOYuYtbMHCFTZvuPSNCn/80yx9+DLkU/hLqjqCRKRHSDISrL1T/lWGXJyQxWS8TnutFA== -sockjs-client@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" - integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== +sockjs-client@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.0.tgz#2f8ff5d4b659e0d092f7aba0b7c386bd2aa20add" + integrity sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q== dependencies: - debug "^3.2.5" + debug "^3.2.6" eventsource "^1.0.7" - faye-websocket "~0.11.1" - inherits "^2.0.3" - json3 "^3.3.2" - url-parse "^1.4.3" + faye-websocket "^0.11.3" + inherits "^2.0.4" + json3 "^3.3.3" + url-parse "^1.4.7" -sockjs@0.3.20: - version "0.3.20" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" - integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== +sockjs@^0.3.21: + version "0.3.21" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417" + integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw== dependencies: - faye-websocket "^0.10.0" + faye-websocket "^0.11.3" uuid "^3.4.0" - websocket-driver "0.6.5" + websocket-driver "^0.7.4" socks-proxy-agent@^4.0.0: version "4.0.2" @@ -12352,6 +12529,23 @@ socks-proxy-agent@^4.0.0: agent-base "~4.2.1" socks "~2.3.2" +socks-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" + integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== + dependencies: + agent-base "6" + debug "4" + socks "^2.3.3" + +socks@^2.3.3: + version "2.5.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f" + integrity sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ== + dependencies: + ip "^1.1.5" + smart-buffer "^4.1.0" + socks@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" @@ -12372,10 +12566,10 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-loader@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.2.tgz#5b782bf08496d3a7f355e1780df0e25190a80991" - integrity sha512-bjf6eSENOYBX4JZDfl9vVLNsGAQ6Uz90fLmOazcmMcyDYOBFsGxPNn83jXezWLY9bJsVAo1ObztxPcV8HAbjVA== +source-map-loader@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.3.tgz#7dbc2fe7ea09d3e43c51fd9fc478b7f016c1f820" + integrity sha512-6YHeF+XzDOrT/ycFJNI53cgEsp/tHTMl37hi7uVyqFAlTXW109JazaQCkbc+jjoL2637qkH1amLi+JzrIpt5lA== dependencies: abab "^2.0.5" iconv-lite "^0.6.2" @@ -12816,10 +13010,10 @@ stylehacks@^4.0.0: postcss "^7.0.0" postcss-selector-parser "^3.0.0" -stylus-loader@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.3.1.tgz#8b4e749294d9fe0729c2e5e1f04cbf87e1c941aa" - integrity sha512-apDYJEM5ZpOAWbWInWcsbtI8gHNr/XYVcSY/tWqOUPt7M5tqhtwXVsAkgyiVjhuvw2Yrjq474a9H+g4d047Ebw== +stylus-loader@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.3.2.tgz#d3577e7f5ff65ea3f9516e1a0f1f16aea706d3f0" + integrity sha512-xXVKHY+J7GBlOmqjCL1VvQfc+pFkBdWGtcpJSvBGE49nWWHaukox7KCjRdLTEzjrmHODm4+rLpqkYWzfJteMXQ== dependencies: fast-glob "^3.2.4" klona "^2.0.4" @@ -12899,10 +13093,10 @@ symbol-observable@1.2.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -symbol-observable@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" - integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== +symbol-observable@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-3.0.0.tgz#eea8f6478c651018e059044268375c408c15c533" + integrity sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q== symbol-tree@^3.2.2: version "3.2.4" @@ -12933,7 +13127,7 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tapable@^2.0.0: +tapable@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== @@ -12951,7 +13145,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.2: +tar@^6.0.2, tar@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== @@ -13031,10 +13225,10 @@ terser-webpack-plugin@^1.4.3: webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@5.3.7: - version "5.3.7" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.7.tgz#798a4ae2e7ff67050c3e99fcc4e00725827d97e2" - integrity sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w== +terser@5.5.1, terser@^5.0.0, terser@^5.3.4: + version "5.5.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" + integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== dependencies: commander "^2.20.0" source-map "~0.7.2" @@ -13049,15 +13243,6 @@ terser@^4.1.2: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^5.0.0, terser@^5.3.4: - version "5.5.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" - integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -13307,21 +13492,16 @@ tsickle@^0.39.1: resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.39.1.tgz#7ccf672cde5b430f5dd0b281ee49e170ef390ff9" integrity sha512-CCc9cZhZbKoNizVM+K3Uqgit/go8GacjpqTv1cpwG/n2P0gB9GMoWZbxrUULDE9Wz26Lh86CGf6QyIPUVV1lnQ== -tslib@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== +tslib@2.1.0, tslib@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== tslib@^1.10.0, tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - tslint@~6.1.0: version "6.1.3" resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" @@ -13432,10 +13612,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@4.0.5, typescript@~4.0.3: - version "4.0.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" - integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== +typescript@4.1.3, typescript@~4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== typescript@^3.5.2, typescript@~3.9.2: version "3.9.7" @@ -13585,7 +13765,7 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-parse@^1.4.3: +url-parse@^1.4.3, url-parse@^1.4.7: version "1.4.7" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== @@ -13647,10 +13827,10 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@8.3.1: - version "8.3.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" - integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== +uuid@8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== uuid@^3.0.0, uuid@^3.0.1, uuid@^3.3.2, uuid@^3.4.0: version "3.4.0" @@ -13817,10 +13997,10 @@ webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" - integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== +webpack-dev-server@3.11.1: + version "3.11.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz#c74028bf5ba8885aaf230e48a20e8936ab8511f0" + integrity sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -13842,11 +14022,11 @@ webpack-dev-server@3.11.0: p-retry "^3.0.1" portfinder "^1.0.26" schema-utils "^1.0.0" - selfsigned "^1.10.7" + selfsigned "^1.10.8" semver "^6.3.0" serve-index "^1.9.1" - sockjs "0.3.20" - sockjs-client "1.4.0" + sockjs "^0.3.21" + sockjs-client "^1.5.0" spdy "^4.0.2" strip-ansi "^3.0.1" supports-color "^6.1.0" @@ -13864,18 +14044,18 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-merge@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.2.0.tgz#31cbcc954f8f89cd4b06ca8d97a38549f7f3f0c9" - integrity sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA== +webpack-merge@5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.7.3.tgz#2a0754e1877a25a8bbab3d2475ca70a052708213" + integrity sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA== dependencies: clone-deep "^4.0.1" wildcard "^2.0.0" -webpack-sources@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.0.1.tgz#1467f6e692ddce91e88b8044c44347b1087bbd4f" - integrity sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw== +webpack-sources@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" + integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== dependencies: source-list-map "^2.0.1" source-map "^0.6.1" @@ -13888,10 +14068,10 @@ webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack- source-list-map "^2.0.0" source-map "~0.6.1" -webpack-subresource-integrity@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.1.tgz#6f44ea99987266b70c4ec42ac51064d33e982277" - integrity sha512-uekbQ93PZ9e7BFB8Hl9cFIVYQyQqiXp2ExKk9Zv+qZfH/zHXHrCFAfw1VW0+NqWbTWrs/HnuDrto3+tiPXh//Q== +webpack-subresource-integrity@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.2.tgz#e40b6578d3072e2d24104975249c52c66e9a743e" + integrity sha512-GBWYBoyalbo5YClwWop9qe6Zclp8CIXYGIz12OPclJhIrSplDxs1Ls1JDMH8xBPPrg1T6ISaTW9Y6zOrwEiAzw== dependencies: webpack-sources "^1.3.0" @@ -13924,14 +14104,7 @@ webpack@4.44.2: watchpack "^1.7.4" webpack-sources "^1.4.1" -websocket-driver@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" - integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= - dependencies: - websocket-extensions ">=0.1.1" - -websocket-driver@>=0.5.1: +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index 45e8452cfe..0975963c21 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -19,31 +19,31 @@ "@abp/ng.tenant-management": "~4.2.0-rc.2", "@abp/ng.theme.basic": "~4.2.0-rc.2", "@abp/ng.theme.shared": "~4.2.0-rc.2", - "@angular/animations": "~11.0.0", - "@angular/common": "~11.0.0", - "@angular/compiler": "~11.0.0", - "@angular/core": "~11.0.0", - "@angular/forms": "~11.0.0", - "@angular/platform-browser": "~11.0.0", - "@angular/platform-browser-dynamic": "~11.0.0", - "@angular/router": "~11.0.0", + "@angular/animations": "~11.1.0", + "@angular/common": "~11.1.0", + "@angular/compiler": "~11.1.0", + "@angular/core": "~11.1.0", + "@angular/forms": "~11.1.0", + "@angular/platform-browser": "~11.1.0", + "@angular/platform-browser-dynamic": "~11.1.0", + "@angular/router": "~11.1.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", "zone.js": "~0.10.2" }, "devDependencies": { "@abp/ng.schematics": "~4.2.0-rc.2", - "@angular-devkit/build-angular": "~0.1100.0", - "@angular/cli": "~11.0.0", - "@angular/compiler-cli": "~11.0.0", - "@angular/language-service": "~11.0.0", + "@angular-devkit/build-angular": "~0.1101.0", + "@angular/cli": "~11.1.0", + "@angular/compiler-cli": "~11.1.0", + "@angular/language-service": "~11.1.0", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", "codelyzer": "^6.0.1", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", - "karma": "~5.1.1", + "karma": "~5.2.3", "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", @@ -52,6 +52,6 @@ "protractor": "~7.0.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~4.0.3" + "typescript": "~4.1.3" } -} +} \ No newline at end of file diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 62d6a7b7b8..4be629e899 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -22,31 +22,31 @@ "@abp/ng.tenant-management": "~4.2.0-rc.2", "@abp/ng.theme.basic": "~4.2.0-rc.2", "@abp/ng.theme.shared": "~4.2.0-rc.2", - "@angular/animations": "~11.0.0", - "@angular/common": "~11.0.0", - "@angular/compiler": "~11.0.0", - "@angular/core": "~11.0.0", - "@angular/forms": "~11.0.0", - "@angular/platform-browser": "~11.0.0", - "@angular/platform-browser-dynamic": "~11.0.0", - "@angular/router": "~11.0.0", + "@angular/animations": "~11.1.0", + "@angular/common": "~11.1.0", + "@angular/compiler": "~11.1.0", + "@angular/core": "~11.1.0", + "@angular/forms": "~11.1.0", + "@angular/platform-browser": "~11.1.0", + "@angular/platform-browser-dynamic": "~11.1.0", + "@angular/router": "~11.1.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", "zone.js": "~0.10.2" }, "devDependencies": { "@abp/ng.schematics": "~4.2.0-rc.2", - "@angular-devkit/build-angular": "~0.1100.0", - "@angular/cli": "~11.0.0", - "@angular/compiler-cli": "~11.0.0", - "@angular/language-service": "~11.0.0", + "@angular-devkit/build-angular": "~0.1101.1", + "@angular/cli": "~11.1.1", + "@angular/compiler-cli": "~11.1.0", + "@angular/language-service": "~11.1.0", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", "codelyzer": "^6.0.1", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", - "karma": "~5.1.1", + "karma": "~5.2.3", "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", @@ -56,6 +56,6 @@ "symlink-manager": "^1.5.0", "ts-node": "~8.3.0", "tslint": "~6.1.0", - "typescript": "~4.0.3" + "typescript": "~4.1.3" } }