diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 8ebcc32bfc..4ecb0c6543 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -56,10 +56,6 @@ jobs: with: dotnet-version: 9.0.100 - - name: chown - run: | - sudo chown -R $USER:$USER /home/runneradmin - - name: Build All run: ./build-all.ps1 working-directory: ./build diff --git a/docs/en/framework/architecture/multi-tenancy/index.md b/docs/en/framework/architecture/multi-tenancy/index.md index 0a035d110d..98a4c9afb5 100644 --- a/docs/en/framework/architecture/multi-tenancy/index.md +++ b/docs/en/framework/architecture/multi-tenancy/index.md @@ -380,6 +380,19 @@ namespace MultiTenancyDemo.Web * A tenant resolver should set `context.TenantIdOrName` if it can determine it. If not, just leave it as is to allow the next resolver to determine it. * `context.ServiceProvider` can be used if you need to additional services to resolve from the [dependency injection](../../fundamentals/dependency-injection.md) system. +##### The Fallback Tenant + +If you want to always fallback to a tenant (in case of no tenant was found by the tenant resolution logic), you can set the `AbpTenantResolveOptions.FallbackTenant` option: + +```csharp +Configure(options => +{ + options.FallbackTenant = "acme"; +}); +``` + +The `FallbackTenant` value can be a tenant name or tenant's Id. This option can be helpful on development time or some specific scenarios to set a constant tenant for the application. It is a simple and consistent way to ensure that a tenant context is always available when needed. However, when you do that, no way to switch to the host side. It is not something you will need it most of the time, but here if you need such a resolution logic. + #### Multi-Tenancy Middleware Multi-Tenancy middleware is an ASP.NET Core request pipeline [middleware](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware) that determines the current tenant from the HTTP request and sets the `ICurrentTenant` properties. diff --git a/docs/en/get-started/images/abp-studio-new-solution-dialog-modularity.png b/docs/en/get-started/images/abp-studio-new-solution-dialog-modularity.png new file mode 100644 index 0000000000..39bc5ace21 Binary files /dev/null and b/docs/en/get-started/images/abp-studio-new-solution-dialog-modularity.png differ diff --git a/docs/en/get-started/images/abp-studio-no-layers-new-solution-modularity.png b/docs/en/get-started/images/abp-studio-no-layers-new-solution-modularity.png new file mode 100644 index 0000000000..47ca772b8a Binary files /dev/null and b/docs/en/get-started/images/abp-studio-no-layers-new-solution-modularity.png differ diff --git a/docs/en/get-started/layered-web-application.md b/docs/en/get-started/layered-web-application.md index 9fed174f53..9e2a2ad6f2 100644 --- a/docs/en/get-started/layered-web-application.md +++ b/docs/en/get-started/layered-web-application.md @@ -103,6 +103,12 @@ Here, you can select the database management systems (DBMS){{ if DB == "EF" }} a If you uncheck the *Kubernetes Configuration* option, the solution will not include the Kubernetes configuration files, such as Helm charts and other Kubernetes-related files. You can also specify *Social Logins*; if you uncheck this option, the solution will not be configured for social login. Lastly, you can specify the *Include Tests* option to include or exclude the test projects from the solution. +On the next screen, you can configure the modularity options for your solution: + +> If you select the *Setup as a modular solution* option, the solution is created more ready for [modular monolith development](../tutorials/modular-crm/index.md) and allows you to add sub-modules during the solution creation phase. + +![abp-studio-new-solution-modularity](images/abp-studio-new-solution-dialog-modularity.png) + Now, we are ready to allow ABP Studio to create our solution. Just click the *Create* button and let the ABP Studio do the rest for you. After clicking the Create button, the dialog is closed and your solution is loaded into ABP Studio: ![abp-studio-created-new-solution](images/abp-studio-created-new-solution.png) diff --git a/docs/en/get-started/single-layer-web-application.md b/docs/en/get-started/single-layer-web-application.md index 4bd16da8b5..d75c3f2031 100644 --- a/docs/en/get-started/single-layer-web-application.md +++ b/docs/en/get-started/single-layer-web-application.md @@ -73,6 +73,12 @@ Here, you can select the database management systems (DBMS){{ if DB == "EF" }} a ![abp-studio-no-layers-new-solution-additional-options](images/abp-studio-no-layers-new-solution-additional-options-0.9.13.png) +Configure any additional options as needed and click the *Next* button to continue. On the next screen, you can configure the modularity options for your solution: + +> If you select the *Setup as a modular solution* option, the solution is created more ready for [modular monolith development](../tutorials/modular-crm/index.md) and allows you to add sub-modules during the solution creation phase. + +![abp-studio-no-layers-new-solution-modularity](images/abp-studio-no-layers-new-solution-modularity.png) + Now, we are ready to allow ABP Studio to create our solution. Just click the *Create* button and let the ABP Studio do the rest for you. After clicking the *Create* button, the dialog is closed and your solution is loaded into ABP Studio: diff --git a/docs/en/solution-templates/application-module/images/new-solution-v2.png b/docs/en/solution-templates/application-module/images/new-solution-v2.png index 28e7b3e687..ee87d719c8 100644 Binary files a/docs/en/solution-templates/application-module/images/new-solution-v2.png and b/docs/en/solution-templates/application-module/images/new-solution-v2.png differ diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md index cfb8e9fb61..d2db328fbf 100644 --- a/docs/en/studio/release-notes.md +++ b/docs/en/studio/release-notes.md @@ -2,6 +2,37 @@ This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version. +## 1.0.2 (2025-06-24) + +* Enhanced the ABP NuGet package installation experience. +* Upgraded template dependencies for ABP Framework and LeptonX. (targeting ABP `9.2.1`) +* Replaced the `System.Data` package with `Microsoft.Data`. +* Fixed a dynamic-env file path configuration issue in Angular templates. +* Disabled Pushed Authorization for MAUI applications. +* Improved the IDE experience by displaying the main project in a dedicated 'main' folder and hiding `.abppkg` files. + +## 1.0.1 (2025-06-13) + +* Fixed an issue with language selection during solution creation. +* Resolved a logo visibility problem in the Angular semi-dark theme. +* Added and corrected the handling of CEF (Chromium Embedded Framework) resources for the Windows version. + +## 1.0.0 (2025-06-11) + +* **Solution Runner with Health Checks:** ABP Studio's Solution Runner now provides visual health monitoring that makes tracking your applications' status easily. +* **Improved Multi-DbContext Migration Handling:** ABP Studio now prompts you to select the correct DbContext for migration operations when working with multiple DbContexts. +* **Theme Style Selection on Project Creation:** When creating a new solution, you can now choose your theme, theme style, and layout right from the project creation wizard instead of having to configure these settings later. +* **Solution & Module Creation:** Introduced major enhancements, including language selection, database provider choice for microservices, improved folder handling, theme visualization, and better module installation recommendations. +* **MAUI & Blazor:** Configured the new MAUI/Blazor bundling system, added dashboard pages to MAUI projects, and applied various fixes for themes and dependencies. +* **Solution Runner & Docker:** Added Docker container support to the solution runner, enabling users to add and manage containers within run profiles. +* **Authentication & Authorization:** Fixed Swagger authentication, and added dynamic claims support for microservices. +* **ABP Studio Login:** Improved login flows with selecting account and organization support. +* **Language Selection:** Added language selection during solution creation. You can now include only the languages you need in your project. +* **Performance:** Sped up the development cycle by skipping package restores during project runs when no dependencies have changed. +* **Dependency Updates:** Upgraded ABP Framework, LeptonX, and other Microsoft dependencies to the latest versions. (targeting ABP `9.2.0`) +* **User Experience:** Implemented several UI/UX improvements, such as remembering user choices in wizards and sorting items alphabetically. +* **Testing & Internals:** Switched to `MongoSandbox` for integration tests, improved local development against abp.io websites, and made various fixes to CI/CD workflows. + ## 0.9.26 (2025-04-30) * Fixed the issue where C# applications would not stop when requested. diff --git a/docs/en/studio/version-mapping.md b/docs/en/studio/version-mapping.md index 88d9be25fe..51e0de5bbd 100644 --- a/docs/en/studio/version-mapping.md +++ b/docs/en/studio/version-mapping.md @@ -4,6 +4,9 @@ This document provides a general overview of the relationship between various ve | **ABP Studio Version** | **ABP Version of Startup Template** | |------------------------|---------------------------| +| 1.0.2 | 9.2.1 | +| 1.0.1 | 9.2.0 | +| 1.0.0 | 9.2.0 | | 0.9.26 | 9.1.1 | | 0.9.24 - 0.9.25 | 9.1.0 | | 0.9.22 - 0.9.23 | 9.0.4 | diff --git a/docs/en/tutorials/book-store-with-abp-suite/part-01.md b/docs/en/tutorials/book-store-with-abp-suite/part-01.md index 637866e577..30cec4441a 100644 --- a/docs/en/tutorials/book-store-with-abp-suite/part-01.md +++ b/docs/en/tutorials/book-store-with-abp-suite/part-01.md @@ -34,11 +34,14 @@ You can use the following configurations: * **Database Provider:** {{DB_Value}} * **Public Website:** No * **Tiered:** No +* **Sample Crud Page:** No You can select the other options based on your preference. > **Please complete the [Get Started](../../get-started/layered-web-application.md) guide and run the web application before going further.** +> **Please do not check the `Sample Crud Page` option while creating the solution, since it conflicts with this tutorial.** + ## Summary We've created the initial layered monolith solution. In the next part, we will learn how to create entities, and generate CRUD pages based on the specified options (including tests, UI, customizable code support etc.) with [ABP Suite](../../suite/index.md). diff --git a/docs/en/tutorials/book-store-with-abp-suite/part-02.md b/docs/en/tutorials/book-store-with-abp-suite/part-02.md index fe19272b4e..0dd87aec07 100644 --- a/docs/en/tutorials/book-store-with-abp-suite/part-02.md +++ b/docs/en/tutorials/book-store-with-abp-suite/part-02.md @@ -128,7 +128,7 @@ It will take some time to complete the process. After the process is completed, {{ end }} -After the application is started, you can right-click and *Browse* on the application to open it in the ABP Studio's pre-integrated browser. You can see the Books page in the following figure with a single record: +After the application is started, you can right-click and *Browse* on the application to open it in the ABP Studio's pre-integrated browser. After logging in using the default credentials (`admin` / `1q2w3E*`), you can see the Books page in the following figure with a single record: ![](./images/suite-book-pages-browser.png) diff --git a/docs/en/tutorials/book-store-with-abp-suite/part-04.md b/docs/en/tutorials/book-store-with-abp-suite/part-04.md index 128e8c15c9..77b99ac4e4 100644 --- a/docs/en/tutorials/book-store-with-abp-suite/part-04.md +++ b/docs/en/tutorials/book-store-with-abp-suite/part-04.md @@ -51,7 +51,7 @@ Here is the details: * Selected the display property as *Name*, this will be used in the dropdown component to set an author with a book & also it will be shown in the datatable of the *Books* page. * Also, made the relation **required** and set it **filterable** so books can be filterable by authors. -> **Note**: You should delete all existing books in the database (if any), before the code generation. Because, a new foreign-key will be added to the _books_ table and if there is any record in the table, then a new migration can't apply to the database and you may need to update the database manually. +> **Note**: You should delete all existing books in the database (if any), before the code generation. Because, a new foreign-key will be added to the _books_ table and if there is any record in the table, then a new migration can't apply to the database and you may need to update the database manually. Deleting existing records from UI will not be enough because of soft-delete, therefore you will have to delete the records manually. After, specifying the metadata, you can click the *Ok* button to close the modal. Then, click the **Save and generate** button to start code generation process. ABP Suite will establish one-to-many relationship between the entities, and will generate all necessary code automatically: diff --git a/docs/en/tutorials/book-store/part-01.md b/docs/en/tutorials/book-store/part-01.md index bf878b4214..6b856d9687 100644 --- a/docs/en/tutorials/book-store/part-01.md +++ b/docs/en/tutorials/book-store/part-01.md @@ -101,6 +101,8 @@ The final folder/file structure should be as shown below: EF Core requires that you relate the entities with your `DbContext`. The easiest way to do so is adding a `DbSet` property to the `BookStoreDbContext` class in the `Acme.BookStore.EntityFrameworkCore` project, as shown below: ````csharp +using Acme.BookStore.Books; + public class BookStoreDbContext : AbpDbContext { public DbSet Books { get; set; } diff --git a/docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside-dark.png b/docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside-dark.png new file mode 100644 index 0000000000..bf5ff14ac2 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-abp-suite-inside-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration-dark.png new file mode 100644 index 0000000000..1d83340f2f Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-entity-framework-core-migration-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2-dark.png new file mode 100644 index 0000000000..9c09d40ca4 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-2-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-dark.png new file mode 100644 index 0000000000..30c175c8eb Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-command-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2-dark.png new file mode 100644 index 0000000000..21d95d5415 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-2-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step-dark.png new file mode 100644 index 0000000000..2414542705 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-additional-options-step-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-dark.png new file mode 100644 index 0000000000..c2f5bc39e8 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-management-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-management-dark.png new file mode 100644 index 0000000000..d77d0d734d Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-management-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step-dark.png new file mode 100644 index 0000000000..8fbd941e46 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step-mongo-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step-mongo-dark.png new file mode 100644 index 0000000000..a2ca48d879 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-database-step-mongo-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step-dark.png new file mode 100644 index 0000000000..9e62649864 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step-ng-dark.png b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step-ng-dark.png new file mode 100644 index 0000000000..201a6efeb2 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-add-new-microservice-dialog-integration-step-ng-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-dark.png b/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-dark.png new file mode 100644 index 0000000000..dd7cea1ae0 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browse-catalog-service-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service-dark.png b/docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service-dark.png new file mode 100644 index 0000000000..17ae3566f8 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browse-ordering-service-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui-dark.png b/docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui-dark.png new file mode 100644 index 0000000000..7b7d248754 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-browser-catalog-service-swagger-ui-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-catalog-service-start-dark.png b/docs/en/tutorials/microservice/images/abp-studio-catalog-service-start-dark.png new file mode 100644 index 0000000000..703f6a8f1a Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-catalog-service-start-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order-dark.png b/docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order-dark.png new file mode 100644 index 0000000000..1553e22d93 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-entity-framework-core-add-migration-order-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2-blazor-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2-blazor-dark.png new file mode 100644 index 0000000000..48602964d5 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2-blazor-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2-dark.png new file mode 100644 index 0000000000..c7738f787f Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-2-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-dark.png new file mode 100644 index 0000000000..c93a7f8a45 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-server-2-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-server-2-dark.png new file mode 100644 index 0000000000..c377b1f7c8 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-server-2-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-server-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-server-dark.png new file mode 100644 index 0000000000..994bd444c9 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-server-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-webapp-server-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-webapp-server-dark.png new file mode 100644 index 0000000000..4302a04124 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-blazor-webapp-server-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-dark.png new file mode 100644 index 0000000000..162ba387d3 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-dark.png new file mode 100644 index 0000000000..e40602993d Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module-dark.png b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module-dark.png new file mode 100644 index 0000000000..d0d60d840f Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-generate-proxy-window-ordering-module-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer-dark.png b/docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer-dark.png new file mode 100644 index 0000000000..c4715f8f58 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-new-catalog-service-in-solution-explorer-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-dark.png b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-dark.png new file mode 100644 index 0000000000..f060ae2911 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module-dark.png b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module-dark.png new file mode 100644 index 0000000000..5a8a29418a Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-open-abp-suite-select-module-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio-dark.png b/docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio-dark.png new file mode 100644 index 0000000000..7e5126c022 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-open-with-visual-studio-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-run-solution-dark.png b/docs/en/tutorials/microservice/images/abp-studio-run-solution-dark.png new file mode 100644 index 0000000000..c7947a5041 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-run-solution-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-run-start-all-dark.png b/docs/en/tutorials/microservice/images/abp-studio-run-start-all-dark.png new file mode 100644 index 0000000000..545d39f211 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-run-start-all-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-blazor-dark.png b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-blazor-dark.png new file mode 100644 index 0000000000..34dfcbdd85 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-blazor-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-dark.png b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-dark.png new file mode 100644 index 0000000000..03da5e50d4 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-ng-dark.png b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-ng-dark.png new file mode 100644 index 0000000000..00f3e8440e Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-ng-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice-dark.png b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice-dark.png new file mode 100644 index 0000000000..c1b53e1309 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-explorer-ordering-microservice-dark.png differ diff --git a/docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all-dark.png b/docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all-dark.png new file mode 100644 index 0000000000..4d60bfbc08 Binary files /dev/null and b/docs/en/tutorials/microservice/images/abp-studio-solution-runner-play-all-dark.png differ diff --git a/docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference-dark.png b/docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference-dark.png new file mode 100644 index 0000000000..c16a4a46db Binary files /dev/null and b/docs/en/tutorials/microservice/images/add-catalog-service-contracts-reference-dark.png differ diff --git a/docs/en/tutorials/microservice/images/add-package-reference-ordering-service-dark.png b/docs/en/tutorials/microservice/images/add-package-reference-ordering-service-dark.png new file mode 100644 index 0000000000..d40f82f551 Binary files /dev/null and b/docs/en/tutorials/microservice/images/add-package-reference-ordering-service-dark.png differ diff --git a/docs/en/tutorials/microservice/images/catalog-service-dependency-dark.png b/docs/en/tutorials/microservice/images/catalog-service-dependency-dark.png new file mode 100644 index 0000000000..0dab0fc0ba Binary files /dev/null and b/docs/en/tutorials/microservice/images/catalog-service-dependency-dark.png differ diff --git a/docs/en/tutorials/microservice/images/create-order-dark.png b/docs/en/tutorials/microservice/images/create-order-dark.png new file mode 100644 index 0000000000..640b98c266 Binary files /dev/null and b/docs/en/tutorials/microservice/images/create-order-dark.png differ diff --git a/docs/en/tutorials/microservice/images/generate-catalog-service-proxy-dark.png b/docs/en/tutorials/microservice/images/generate-catalog-service-proxy-dark.png new file mode 100644 index 0000000000..7b6fa124fe Binary files /dev/null and b/docs/en/tutorials/microservice/images/generate-catalog-service-proxy-dark.png differ diff --git a/docs/en/tutorials/microservice/images/generate-proxy-catalog-service-dark.png b/docs/en/tutorials/microservice/images/generate-proxy-catalog-service-dark.png new file mode 100644 index 0000000000..90ce27d314 Binary files /dev/null and b/docs/en/tutorials/microservice/images/generate-proxy-catalog-service-dark.png differ diff --git a/docs/en/tutorials/microservice/images/import-module-dark.png b/docs/en/tutorials/microservice/images/import-module-dark.png new file mode 100644 index 0000000000..6fa0ed4e4c Binary files /dev/null and b/docs/en/tutorials/microservice/images/import-module-dark.png differ diff --git a/docs/en/tutorials/microservice/images/import-module-dialog-dark.png b/docs/en/tutorials/microservice/images/import-module-dialog-dark.png new file mode 100644 index 0000000000..eea84ff56e Binary files /dev/null and b/docs/en/tutorials/microservice/images/import-module-dialog-dark.png differ diff --git a/docs/en/tutorials/microservice/images/install-module-dialog-dark.png b/docs/en/tutorials/microservice/images/install-module-dialog-dark.png new file mode 100644 index 0000000000..a92309634f Binary files /dev/null and b/docs/en/tutorials/microservice/images/install-module-dialog-dark.png differ diff --git a/docs/en/tutorials/microservice/images/ordering-service-swagger-ui-dark.png b/docs/en/tutorials/microservice/images/ordering-service-swagger-ui-dark.png new file mode 100644 index 0000000000..d5e0241ab3 Binary files /dev/null and b/docs/en/tutorials/microservice/images/ordering-service-swagger-ui-dark.png differ diff --git a/docs/en/tutorials/microservice/images/visual-studio-new-migration-class-dark.png b/docs/en/tutorials/microservice/images/visual-studio-new-migration-class-dark.png new file mode 100644 index 0000000000..48f3dd264c Binary files /dev/null and b/docs/en/tutorials/microservice/images/visual-studio-new-migration-class-dark.png differ diff --git a/docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service-dark.png b/docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service-dark.png new file mode 100644 index 0000000000..551e58c878 Binary files /dev/null and b/docs/en/tutorials/microservice/images/visual-studio-solution-explorer-catalog-service-dark.png differ diff --git a/docs/en/tutorials/microservice/images/vs-ordering-contracts-dark.png b/docs/en/tutorials/microservice/images/vs-ordering-contracts-dark.png new file mode 100644 index 0000000000..ad94403292 Binary files /dev/null and b/docs/en/tutorials/microservice/images/vs-ordering-contracts-dark.png differ diff --git a/docs/en/tutorials/microservice/images/vs-ordering-entity-dark.png b/docs/en/tutorials/microservice/images/vs-ordering-entity-dark.png new file mode 100644 index 0000000000..5ebcc69a81 Binary files /dev/null and b/docs/en/tutorials/microservice/images/vs-ordering-entity-dark.png differ diff --git a/docs/en/tutorials/microservice/images/web-orders-page-dark.png b/docs/en/tutorials/microservice/images/web-orders-page-dark.png new file mode 100644 index 0000000000..04c01a6fbf Binary files /dev/null and b/docs/en/tutorials/microservice/images/web-orders-page-dark.png differ diff --git a/docs/en/tutorials/microservice/images/web-orders-page-with-product-name-dark.png b/docs/en/tutorials/microservice/images/web-orders-page-with-product-name-dark.png new file mode 100644 index 0000000000..ec889a803f Binary files /dev/null and b/docs/en/tutorials/microservice/images/web-orders-page-with-product-name-dark.png differ diff --git a/docs/en/tutorials/microservice/index.md b/docs/en/tutorials/microservice/index.md index a4605f7508..76715d9b5e 100644 --- a/docs/en/tutorials/microservice/index.md +++ b/docs/en/tutorials/microservice/index.md @@ -1,5 +1,13 @@ # Microservice Development Tutorial +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -30,7 +38,7 @@ This tutorial is organized as the following parts: ## Download the Source Code -After logging in to the ABP website, you can download the source code from [here](https://abp.io/api/download/samples/cloud-crm-mvc-ef). +After logging in to the ABP website, you can download the source code from {{if UI == "MVC"}} [here](https://abp.io/api/download/samples/cloud-crm-mvc-ef) {{else if UI == "NG"}} [here](https://abp.io/api/download/samples/cloud-crm-ng-ef) {{else if UI == "Blazor"}} [here](https://abp.io/api/download/samples/cloud-crm-blazor-wasm-ef) {{else if UI == "BlazorServer"}} [here](https://abp.io/api/download/samples/cloud-crm-blazor-server-ef) {{else if UI == "BlazorWebApp"}} [here](https://abp.io/api/download/samples/cloud-crm-blazor-webapp-ef) {{end}}. ## See Also diff --git a/docs/en/tutorials/microservice/part-01.md b/docs/en/tutorials/microservice/part-01.md index b2d0dade7f..8e741b9a47 100644 --- a/docs/en/tutorials/microservice/part-01.md +++ b/docs/en/tutorials/microservice/part-01.md @@ -1,5 +1,13 @@ # Microservice Tutorial Part 01: Creating the Initial Solution +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -13,19 +21,49 @@ Follow the *[Get Started](../../get-started/microservice.md)* guide to create a new layered web application with the following configurations: * **Solution name**: `CloudCrm` +{{if DB == "EF"}} * **Database Provider**: Entity Framework Core -* **Database Management System**: SQL Server +{{end}} +{{if DB == "Mongo"}} +* **Database Provider**: MongoDB +{{end}} +{{if UI == "MVC"}} * **UI Framework**: MVC / Razor Pages +{{end}} +{{if UI == "Blazor"}} +* **UI Framework**: Blazor WebAssembly +{{end}} +{{if UI == "BlazorServer"}} +* **UI Framework**: Blazor Server +{{end}} +{{if UI == "BlazorWebApp"}} +* **UI Framework**: Blazor Web App +{{end}} +{{if UI == "NG"}} +* **UI Framework**: Angular +{{end}} * **Mobile framework**: None * **Public website**: Selected You can select the other options based on your preference. -> **Please complete the *[Get Started](../../get-started/microservice.md)* guide and run the applications before going further.** You can skip the sections after the *Running the Solution* section, if you don't prefer to complete all. +> **Please complete the *[Get Started](../../get-started/layered-web-application.md)* guide and run the web application before going further.** You can skip the sections after the *Running the Solution* section, if you don't prefer to complete all. The initial solution structure should be like the following in ABP Studio's *[Solution Explorer](../../studio/solution-explorer.md)*: -![abp-studio-solution-explorer-initial-cloud-crm-microservice-solution](images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution.png) +{{if UI == "MVC"}} + +![abp-studio-solution-explorer-initial-cloud-crm-microservice-solution](images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-dark.png) + +{{else if UI == "NG"}} + +![abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-ng](images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-ng-dark.png) + +{{else}} + +![abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-blazor](images/abp-studio-solution-explorer-initial-cloud-crm-microservice-solution-blazor-dark.png) + +{{end}} > ABP Studio will perform a few additional steps after creating your solution. **Please wait until all the background tasks are completed** before going further. @@ -37,4 +75,4 @@ You can see the *[Microservice Solution Template](../../solution-templates/micro ## Summary -In this part, you've created the initial microservice solution, which already contains a few infrastructure services. We will create our first business service in the [next part](part-02.md). +In this part, you've created the initial microservice solution, which already contains a few infrastructure services. We will create our first business service in the [next part](part-02.md). \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-02.md b/docs/en/tutorials/microservice/part-02.md index 014daf9b11..f54ee4fe56 100644 --- a/docs/en/tutorials/microservice/part-02.md +++ b/docs/en/tutorials/microservice/part-02.md @@ -1,5 +1,13 @@ # Microservice Tutorial Part 02: Creating the initial Catalog service +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -20,11 +28,11 @@ In this tutorial, you will create a new Catalog service and integrate it to the Right-click the `services` folder in the *Solution Explorer* panel, select the *Add* -> *New Module* -> *Microservice* command: -![abp-studio-add-new-microservice-command](images/abp-studio-add-new-microservice-command.png) +![abp-studio-add-new-microservice-command](images/abp-studio-add-new-microservice-command-dark.png) This command opens a new dialog to define the properties of the new microservice. You can use the following values to create a new microservice named `CatalogService`: -![abp-studio-add-new-microservice-dialog](images/abp-studio-add-new-microservice-dialog.png) +![abp-studio-add-new-microservice-dialog](images/abp-studio-add-new-microservice-dialog-dark.png) When you click the *Next* button, you are redirected to the database provider selection step. @@ -32,28 +40,49 @@ When you click the *Next* button, you are redirected to the database provider se Here, you can select the database provider to be used by the new microservice: -![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-step.png) +{{if DB == "Mongo"}} -Select *Entity Framework Core* option and proceed the *Next* step. +![abp-studio-add-new-microservice-dialog-database-step-mongo](images/abp-studio-add-new-microservice-dialog-database-step-mongo-dark.png) -### Integrating to the Solution +Select *MongoDB* option and proceed the *Next* step. +{{end}} -In this step, we can select the options for integrating the new microservice to the rest of the solution components: +{{if DB == "EF"}} -![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step.png) +![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-step-dark.png) -ABP Studio intelligently selects the right values for you, but you should still check them carefully since they directly affect what we will do in the next parts of this tutorial. +Select *Entity Framework Core* option and proceed the *Next* step. -**Ensure the options are configured the same as in the preceding figure**, and click the *Next* button. +### Selecting Database Management System + +![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-management-dark.png) + +Select the desired option and proceed to the next step. + +{{end}} ### Additional Options -![abp-studio-add-new-microservice-dialog-additional-options-step](images/abp-studio-add-new-microservice-dialog-additional-options-step.png) +![abp-studio-add-new-microservice-dialog-additional-options-step](images/abp-studio-add-new-microservice-dialog-additional-options-step-dark.png) In this step, you can select additional options for the new microservice. You can leave them as default and click the *Create* button. That's all, ABP Studio creates the new microservice and arranges all the integration and configuration for you. +### Integrating to the Solution + +In this step, we can select the options for integrating the new microservice to the rest of the solution components: + +{{if UI == "NG"}} +![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step-ng-dark.png) +{{else}} +![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step-dark.png) +{{end}} + +ABP Studio intelligently selects the right values for you, but you should still check them carefully since they directly affect what we will do in the next parts of this tutorial. + +**Ensure the options are configured the same as in the preceding figure**, and click the *Next* button. + ## Exploring the New Catalog Microservice In this section, we will investigate the new microservice in overall. @@ -62,7 +91,7 @@ In this section, we will investigate the new microservice in overall. The new microservice is added under the `services` folder in the `CloudCrm` ABP Studio solution: -![abp-studio-new-catalog-service-in-solution-explorer](images/abp-studio-new-catalog-service-in-solution-explorer.png) +![abp-studio-new-catalog-service-in-solution-explorer](images/abp-studio-new-catalog-service-in-solution-explorer-dark.png) The new microservice has its own separate .NET solution that includes three packages (.NET projects): @@ -74,11 +103,11 @@ The new microservice has its own separate .NET solution that includes three pack You can open the new microservice in your favorite IDE for development. As a shortcut, you can right-click it in ABP Studio, select the *Open with* -> *Visual Studio* command for example: -![abp-studio-open-with-visual-studio](images/abp-studio-open-with-visual-studio.png) +![abp-studio-open-with-visual-studio](images/abp-studio-open-with-visual-studio-dark.png) Here is the `CloudCrm.CatalogService` .NET solution in Visual Studio: -![visual-studio-solution-explorer-catalog-service](images/visual-studio-solution-explorer-catalog-service.png) +![visual-studio-solution-explorer-catalog-service](images/visual-studio-solution-explorer-catalog-service-dark.png) ### Running the New Service @@ -88,17 +117,17 @@ You can run the solution using ABP Studio's *Solution Runner*. It will also run Click the *Play* button near to the solution root: -![abp-studio-solution-runner-play-all](images/abp-studio-solution-runner-play-all.png) +![abp-studio-solution-runner-play-all](images/abp-studio-solution-runner-play-all-dark.png) ### Browsing the Catalog Service Once all of the applications have started, right-click the Catalog service and select the *Browse* command: -![abp-studio-browse-catalog-service](images/abp-studio-browse-catalog-service.png) +![abp-studio-browse-catalog-service](images/abp-studio-browse-catalog-service-dark.png) It will open the built-in browser and you will see the Swagger UI for the Catalog service: -![abp-studio-browser-catalog-service-swagger-ui](images/abp-studio-browser-catalog-service-swagger-ui.png) +![abp-studio-browser-catalog-service-swagger-ui](images/abp-studio-browser-catalog-service-swagger-ui-dark.png) You can test the APIs on the Swagger UI to see if the new microservice is properly working. diff --git a/docs/en/tutorials/microservice/part-03.md b/docs/en/tutorials/microservice/part-03.md index adae9877dd..f75b3bd23f 100644 --- a/docs/en/tutorials/microservice/part-03.md +++ b/docs/en/tutorials/microservice/part-03.md @@ -1,5 +1,13 @@ # Microservice Tutorial Part 03: Building the Catalog service +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -24,21 +32,21 @@ First of all, **stop all the applications** in ABP Studio's *Solution Runner* pa Now, select the *ABP Suite* -> *Open* command on the main menu to open ABP Suite: -![abp-studio-open-abp-suite](images/abp-studio-open-abp-suite.png) +![abp-studio-open-abp-suite](images/abp-studio-open-abp-suite-dark.png) It will ask to you which module you want to use: -![abp-studio-open-abp-suite-select-module](images/abp-studio-open-abp-suite-select-module.png) +![abp-studio-open-abp-suite-select-module](images/abp-studio-open-abp-suite-select-module-dark.png) The `CloudCrm` microservice solution contains more than one .NET solution. Typically, each ABP Studio module represents a separate .NET solution (see the [concepts](../../studio/concepts.md) document). ABP Suite works on a single .NET solution to generate code, so we should select a module here. Select the `CloudCrm.CatalogService` module and click the *OK* button. It will open ABP Suite as shown below: -![abp-studio-abp-suite-inside](images/abp-studio-abp-suite-inside.png) +![abp-studio-abp-suite-inside](images/abp-studio-abp-suite-inside-dark.png) ## Generating a Products Page -In the next section, we will use ABP Suite to create a fully functional CRUD page with ABP Suite. The UI part will be in the main web application (`CloudCrm.Web`) and the application service and other parts will be generated in the Catalog microservice. +In the next section, we will use ABP Suite to create a fully functional CRUD page with ABP Suite. The UI part will be in the main web application (`{{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "BlazorServer" || UI == "Blazor" || UI == "BlazorWebApp"}} `CloudCrm.Blazor` {{else}} `Angular` {{end}}`) and the application service and other parts will be generated in the Catalog microservice. ### Configuring the Product Entity Information @@ -71,7 +79,7 @@ That's all. You can click the *Save and generate* button to start the code gener ABP Suite will generate the necessary code for you. It will take some time to complete the process. After the process is completed, you will see a success message, click the *OK* button. -![abp-studio-catalog-service-build-and-start](images/abp-studio-catalog-service-start.png) +![abp-studio-catalog-service-build-and-start](images/abp-studio-catalog-service-start-dark.png) We can now start the `CloudCrm.CatalogService` application by clicking the *Start* button (or alternatively, directly clicking the *run* icon) in the *Solution Runner* panel. @@ -79,39 +87,65 @@ We can now start the `CloudCrm.CatalogService` application by clicking the *Star After the application is started, you can right-click and [Browse](../../studio/running-applications.md#monitoring) on the `CloudCrm.CatalogService` application to open it in the ABP Studio's pre-integrated browser. You can see the *Products* controller in the Swagger UI. +{{if UI == "NG"}} ### Generating the UI Proxy -Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the *Web* project. Right-click the *CloudCrm.Web* [package](../../studio/concepts.md#package) and select the *ABP CLI* -> *Generate Proxy* -> *C#* command: +ABP Suite automatically generates the UI proxy for the `Angular` project. If you want to create manually, run this command under the `Angular` project folder: + +```bash +abp generate-proxy -t ng -m catalog -u http://localhost:44384 --target catalog-service +``` -![abp-studio-generate-proxy](images/abp-studio-generate-proxy.png) +For more information, please refer to the [Service Proxies](https://abp.io/docs/latest/framework/ui/angular/service-proxies) documentation. +{{end}} -It will open the *Generate C# Proxies* window. Select the `CloudCrm.CatalogService` application, and it will automatically populate the *URL* field. Select the *catalog* module, set the service type to *application*, and check the *Without contracts* checkbox, as the `CloudCrm.Web` project already depends on the `CloudCrm.CatalogService.Contracts` package: +{{if UI == "MVC" || UI == "Blazor" || UI == "BlazorServer" || UI == "BlazorWebApp"}} +### Generating the UI Proxy -![abp-studio-generate-proxy-window](images/abp-studio-generate-proxy-window.png) +{{if UI == "BlazorWebApp"}} -> To be able to select the *Application*, you must *Start* the related application beforehand. You can start the application using [Solution Runner](../../studio/running-applications.md) as explained in the previous parts. +Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the UI project. Since we are using `BlazorWebApp`, we will generate client proxies in both the `CloudCrm.Blazor` and `CloudCrm.Blazor.Client` projects, as pages are sometimes rendered on the server side and sometimes on the client side. -Lastly, we need to configure the use of a static HTTP client for the `CatalogService` in the `CloudCrm.Web` project. Open the `CloudCrmWebModule.cs` file in the `Web` project and add the following line to the `ConfigureServices` method: +#### For the Server Project +![abp-studio-generate-proxy-blazor-server](images/abp-studio-generate-proxy-blazor-webapp-server-dark.png) -```csharp -//... -using CloudCrm.CatalogService; +This will open the Generate C# Proxies window. Select the `CloudCrm.CatalogService` application this will automatically fill in the *URL* field. Next, choose the catalog module, set the service type to *Application*, and check the *Without contracts* option, since the `CloudCrm.Blazor` project already references the `CloudCrm.CatalogService.Contracts` package. + +#### For client project +![abp-studio-generate-proxy-blazor](images/abp-studio-generate-proxy-blazor-dark.png) + +This will open the Generate C# Proxies window. Select the `CloudCrm.CatalogService` application this will automatically fill in the *URL* field. Next, choose the catalog module, set the service type to *Application*, and check the *Without contracts* option, since the `CloudCrm.Blazor.Client` project already references the `CloudCrm.CatalogService.Contracts` package. + +{{else}} + +Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the UI project. Right-click the {{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "BlazorServer"}} `CloudCrm.Blazor` {{else if UI == "Blazor"}} `CloudCrm.Blazor.Client` {{end}} [package](../../studio/concepts.md#package) and select the *ABP CLI* -> *Generate Proxy* -> *C#* command: + +{{if UI == "MVC"}} +![abp-studio-generate-proxy](images/abp-studio-generate-proxy-dark.png) +{{else if UI == "BlazorServer"}} +![abp-studio-generate-proxy-blazor-server](images/abp-studio-generate-proxy-blazor-server-dark.png) +{{else if UI == "Blazor"}} +![abp-studio-generate-proxy-blazor](images/abp-studio-generate-proxy-blazor-dark.png) +{{end}} + +{{end}} + +It will open the *Generate C# Proxies* window. Select the `CloudCrm.CatalogService` application, and it will automatically populate the *URL* field. Select the *catalog* module, set the service type to *application*, and check the *Without contracts* checkbox, as the {{if UI == "MVC"}} `CloudCrm.Web` {{else}} `CloudCrm.Blazor` {{end}} project already depends on the `CloudCrm.CatalogService.Contracts` package: + +![abp-studio-generate-proxy-window](images/abp-studio-generate-proxy-window-dark.png) + +> To be able to select the *Application*, you must *Start* the related application beforehand. You can start the application using [Solution Runner](../../studio/running-applications.md) as explained in the previous parts. + +{{end}} -public override void ConfigureServices(ServiceConfigurationContext context) -{ - // Code omitted for brevity - context.Services.AddStaticHttpClientProxies( - typeof(CloudCrmCatalogServiceContractsModule).Assembly); -} -``` ### Running the Application Now, stop any application running in the *Solution Runner* panel, and then run the applications by clicking the *Start All* button on the root item in the *Solution Runner* panel: -![abp-studio-run-build-and-start-all](images/abp-studio-run-start-all.png) +![abp-studio-run-build-and-start-all](images/abp-studio-run-start-all-dark.png) -After the application is started, you can right-click and [Browse](../../studio/running-applications.md#monitoring) on the `CloudCrm.Web` application to open it in the ABP Studio's pre-integrated browser: +After the application is started, you can right-click and [Browse](../../studio/running-applications.md#monitoring) on the {{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "NG"}} `CloudCrm.Angular` {{else}} `CloudCrm.Blazor` {{end}} application to open it in the ABP Studio's pre-integrated browser: ![abp-studio-browse-cloud-crm-products](images/abp-studio-browse-cloud-crm-products.png) @@ -125,4 +159,4 @@ You can open the Sql Server Management Studio to see the created tables and data ## Summary -In this part, we've created a new entity named *Product* and generated the necessary code for it. We've also generated the UI proxy for the `CatalogService` application and configured the static HTTP client for it in the `Web` project. We've run the application and tested the *Products* page. \ No newline at end of file +In this part, we've created a new entity named *Product* and generated the necessary code for it. We've also generated the UI proxy for the `CatalogService` application and configured the static HTTP client for it in the {{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "BlazorServer"}} `CloudCrm.Blazor` {{else}} `CloudCrm.Blazor.Client` {{end}} project. We've run the application and tested the *Products* page. \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-04.md b/docs/en/tutorials/microservice/part-04.md index 50386ab8e1..0cae944d07 100644 --- a/docs/en/tutorials/microservice/part-04.md +++ b/docs/en/tutorials/microservice/part-04.md @@ -1,5 +1,13 @@ # Microservice Tutorial Part 04: Creating the initial Ordering service +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -20,11 +28,11 @@ In the previous part, we implemented the Catalog microservice functionality usin Right-click the `services` folder in the *Solution Explorer* panel, select the *Add* -> *New Module* -> *Microservice* command: -![abp-studio-add-new-microservice-command](images/abp-studio-add-new-microservice-command-2.png) +![abp-studio-add-new-microservice-command](images/abp-studio-add-new-microservice-command-2-dark.png) This command opens a new dialog to define the properties of the new microservice. You can use the following values to create a new microservice named `OrderingService`: -![abp-studio-add-new-microservice-dialog](images/abp-studio-add-new-microservice-dialog-2.png) +![abp-studio-add-new-microservice-dialog](images/abp-studio-add-new-microservice-dialog-2-dark.png) When you click the *Next* button, you are redirected to the database provider selection step. @@ -32,28 +40,49 @@ When you click the *Next* button, you are redirected to the database provider se Here, you can select the database provider to be used by the new microservice: -![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-step.png) +{{if DB == "Mongo"}} -Select *Entity Framework Core* option and proceed the *Next* step. +![abp-studio-add-new-microservice-dialog-database-step-mongo](images/abp-studio-add-new-microservice-dialog-database-step-mongo-dark.png) -### Integrating to the Solution +Select *MongoDB* option and proceed the *Next* step. +{{end}} -In this step, we can select the options for integrating the new microservice to the rest of the solution components: +{{if DB == "EF"}} -![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step.png) +![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-step-dark.png) -ABP Studio intelligently selects the right values for you, but you should still check them carefully since they directly affect what we will do in the next parts of this tutorial. +Select *Entity Framework Core* option and proceed the *Next* step. -**Ensure the options are configured the same as in the preceding figure**, and click the *Next* button. +### Selecting Database Management System + +![abp-studio-add-new-microservice-dialog-database-step](images/abp-studio-add-new-microservice-dialog-database-management-dark.png) + +Select the desired option and proceed to the next step. + +{{end}} ### Additional Options -![abp-studio-add-new-microservice-dialog-additional-options-step](images/abp-studio-add-new-microservice-dialog-additional-options-step.png) +![abp-studio-add-new-microservice-dialog-additional-options-step](images/abp-studio-add-new-microservice-dialog-additional-options-step-dark.png) In this step, you can select additional options for the new microservice. You can leave them as default and click the *Create* button. That's all, ABP Studio creates the new microservice and arranges all the integration and configuration for you. +### Integrating to the Solution + +In this step, we can select the options for integrating the new microservice to the rest of the solution components: + +{{if UI == "NG"}} +![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step-ng-dark.png) +{{else}} +![abp-studio-add-new-microservice-dialog-integration-step](images/abp-studio-add-new-microservice-dialog-integration-step-dark.png) +{{end}} + +ABP Studio intelligently selects the right values for you, but you should still check them carefully since they directly affect what we will do in the next parts of this tutorial. + +**Ensure the options are configured the same as in the preceding figure**, and click the *Next* button. + ## Exploring the New Ordering Microservice In this section, we will investigate the new microservice in overall. @@ -62,7 +91,7 @@ In this section, we will investigate the new microservice in overall. Just like the Catalog microservice, the Ordering microservice is a .NET solution that contains multiple projects. You can see the solution structure in the *Solution Explorer* panel: -![abp-studio-solution-explorer-ordering-microservice](images/abp-studio-solution-explorer-ordering-microservice.png) +![abp-studio-solution-explorer-ordering-microservice](images/abp-studio-solution-explorer-ordering-microservice-dark.png) * `CloudCrm.OrderingService` is the main project that you will implement your service. It typically contains your [entities](../../framework/architecture/domain-driven-design/entities.md), [repositories](../../framework/architecture/domain-driven-design/repositories.md), [application services](../../framework/architecture/domain-driven-design/application-services.md), API controllers, etc. * `CloudCrm.OrderingService.Contracts` project can be shared with the other services and applications. It typically contains interfaces of your [application services](../../framework/architecture/domain-driven-design/application-services.md), [data transfer objects](../../framework/architecture/domain-driven-design/data-transfer-objects.md), and some other types you may want to share with the clients of this microservice. @@ -76,13 +105,13 @@ You can run the solution using ABP Studio's *Solution Runner*. It will also run Click the *Play* button near to the solution root: -![abp-studio-run-solution](images/abp-studio-run-solution.png) +![abp-studio-run-solution](images/abp-studio-run-solution-dark.png) ### Browsing the Ordering Service After the application is started, you can right-click and [Browse](../../studio/running-applications.md#monitoring) on the `CloudCrm.OrderingService` application to open it in the ABP Studio's pre-integrated browser. You can see the *Orders* controller in the Swagger UI: -![abp-studio-browse-ordering-service](images/abp-studio-browse-ordering-service.png) +![abp-studio-browse-ordering-service](images/abp-studio-browse-ordering-service-dark.png) ### Opening the Ordering Database diff --git a/docs/en/tutorials/microservice/part-05.md b/docs/en/tutorials/microservice/part-05.md index f2cefce256..3fcfc9cac8 100644 --- a/docs/en/tutorials/microservice/part-05.md +++ b/docs/en/tutorials/microservice/part-05.md @@ -1,5 +1,13 @@ # Microservice Tutorial Part 05: Building the Ordering service +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -18,7 +26,7 @@ In the previous part, we created the Ordering microservice. In this part, we wil ## Creating the Order Entity -We will start by creating the `Order` entity, which will represent an order in our system. We'll add this entity to the `CloudCrm.OrderingService` project. Create a new folder named `Entities` and add a class named `Order` inside it: +We will start by creating the `Order` entity, which will represent an order in our system. We'll add this entity to the `CloudCrm.OrderingService` project. Create a new folder named `Entities` and create a file `Order.cs` inside it: ```csharp using CloudCrm.OrderingService.Enums; @@ -38,7 +46,7 @@ To keep this example simple, we allow users to include only a single product wit ### Adding the OrderState Enum -We also need to define the `OrderState` enum. In the `CloudCrm.OrderingService.Contracts` project, create a folder named `Enums` and add an `OrderState` enum inside it: +We also need to define the `OrderState` enum. In the `CloudCrm.OrderingService.Contracts` project, create a folder named `Enums` and create a file `OrderState.cs` inside it: ```csharp namespace CloudCrm.OrderingService.Enums; @@ -53,7 +61,7 @@ public enum OrderState : byte The final solution structure should look like this: -![vs-ordering-entity](images/vs-ordering-entity.png) +![vs-ordering-entity](images/vs-ordering-entity-dark.png) ## Configuring the Database Mapping @@ -122,15 +130,15 @@ Please stop the applications if they are running and ensure that the solution ha Right-click the `CloudCrm.OrderingService` package and select the *EF Core CLI* -> *Add Migration* command: -![abp-studio-add-entity-framework-core-migration](images/abp-studio-add-entity-framework-core-migration.png) +![abp-studio-add-entity-framework-core-migration](images/abp-studio-add-entity-framework-core-migration-dark.png) -The *Add Migration* command opens a new dialog to get a migration name: +The *Add Migration* command opens a new dialog to get a migration name `Added_Order_Entity`: -![abp-studio-entity-framework-core-add-migration-order](images/abp-studio-entity-framework-core-add-migration-order.png) +![abp-studio-entity-framework-core-add-migration-order](images/abp-studio-entity-framework-core-add-migration-order-dark.png) Once you click the *OK* button, a new database migration class is added to the `Migrations` folder of the `CloudCrm.OrderingService` project: -![visual-studio-new-migration-class](images/visual-studio-new-migration-class.png) +![visual-studio-new-migration-class](images/visual-studio-new-migration-class-dark.png) The changes will be applied to the database during the next application startup. For more details, refer to the [database migrations on service startup](../../solution-templates/microservice/database-configurations.md#database-migrations-on-service-startup) section. @@ -198,7 +206,7 @@ public class OrderDto The final solution structure should look like this: -![vs-ordering-contracts](images/vs-ordering-contracts.png) +![vs-ordering-contracts](images/vs-ordering-contracts-dark.png) ## Implementing the Application Service @@ -269,7 +277,7 @@ public class OrderingServiceApplicationAutoMapperProfile : Profile Now, we can test the `OrderAppService` class using the Swagger UI. Open the Solution Runner and right-click to `CloudCrm.OrderingService` project and select the *Start* command. After the application starts, you can open the Swagger UI by clicking to the [Browse](../../studio/running-applications.md#monitoring) command: -![ordering-service-swagger-ui](images/ordering-service-swagger-ui.png) +![ordering-service-swagger-ui](images/ordering-service-swagger-ui-dark.png) Expand the `api/ordering/order` API and click the *Try it out* button. Then, create a few orders by filling in the request body and clicking the *Execute* button: @@ -283,6 +291,7 @@ If you check the database, you should see the entities created in the `Orders` t ## Creating the User Interface +{{if UI == "MVC"}} Now, we will create the user interface for the Ordering module. We will use the `CloudCrm.Web` project to create the user interface. Open the `CloudCrm.Web` .NET solution in your favorite IDE. ### Creating the Orders Page @@ -339,38 +348,120 @@ Here, we inject the `IOrderAppService` into the `Index` Razor Page. We use this ``` This page shows a list of orders on the UI. We haven't created a UI to create new orders, and we will not do it to keep this tutorial simple. If you want to learn how to create advanced UIs with ABP, please follow the [Book Store tutorial](../../tutorials/book-store/index.md). +{{end}} -### Generating the UI Proxy +{{if UI == "Blazor" || UI == "BlazorServer" || UI == "BlazorWebApp"}} -To select the *Application* during proxy generation, ensure that the `CloudCrm.OrderingService` is *Started* beforehand. You can start the application using [Solution Runner](../../studio/running-applications.md). +Now, we will create the user interface for the Ordering module. We will use the {{if UI == "BlazorServer"}} `CloudCrm.Blazor.Server` {{else}} `CloudCrm.Blazor.Client` {{end}} project to create the user interface. Open the {{if UI == "BlazorServer"}} `CloudCrm.Blazor.Server` {{else}} `CloudCrm.Blazor.Client` {{end}} .NET solution in your favorite IDE. -Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the *Web* project. Right-click the *CloudCrm.Web* [package](../../studio/concepts.md#package) and select the *ABP CLI* -> *Generate Proxy* -> *C#* command: +### Creating the Orders Page -![abp-studio-generate-proxy-2](images/abp-studio-generate-proxy-2.png) +{{if UI == "BlazorServer"}} -It will open the *Generate C# Proxies* window. Select the `CloudCrm.OrderingService` application, and it will automatically populate the *URL* field. Choose the *ordering* module and service type is *application* lastly check the *Without contracts* checkbox, since we already have a dependency on the `CloudCrm.OrderingService.Contracts` package in the `CloudCrm.Web` project: +Create `Orders.razor` file under the `Components/Pages` folder in the `CloudCrm.Blazor.Server` project. -![abp-studio-generate-proxy-window-ordering-module](images/abp-studio-generate-proxy-window-ordering-module.png) +{{else}} -Lastly, we need to configure the use of a static HTTP client for the `OrderingService` in the `CloudCrm.Web` project. Open the `CloudCrmWebModule.cs` file in the `Web` project and add the following line to the `ConfigureServices` method: +Create `Orders.razor` file under the `Pages` folder in the `CloudCrm.Blazor.Client` project. -```csharp -//... -using CloudCrm.OrderingService; +{{end}} -public override void ConfigureServices(ServiceConfigurationContext context) -{ - // Code omitted for brevity - context.Services.AddStaticHttpClientProxies( - typeof(CloudCrmOrderingServiceContractsModule).Assembly); +```html +@page "/orders" +@using CloudCrm.OrderingService.Services +@inject IOrderAppService OrderAppService + +

Orders

+ +
+
+ @if (OrderList == null) + { +

Loading orders...

+ } + else if (!OrderList.Any()) + { +

No orders found.

+ } + else + { +
    + @foreach (var order in OrderList) + { +
  • + Customer: @order.CustomerName
    + Product: @order.ProductId
    + State: @order.State +
  • + } +
+ } +
+
+ +@code { + private List OrderList { get; set; } + + protected override async Task OnInitializedAsync() + { + try + { + var result = await OrderAppService.GetListAsync(); + OrderList = result; + } + catch (Exception ex) + { + Console.WriteLine($"Error loading orders: {ex.Message}"); + OrderList = new List(); + } + } } ``` +{{end}} + +{{if UI == "MVC" || UI == "Blazor" || UI == "BlazorServer" || UI == "BlazorWebApp"}} +### Generating the UI Proxy + +To select the *Application* during proxy generation, ensure that the `CloudCrm.OrderingService` is *Started* beforehand. You can start the application using [Solution Runner](../../studio/running-applications.md). + +{{if UI == "BlazorWebApp"}} + +Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the UI project. Since we are using `BlazorWebApp`, we will generate client proxies in both the `CloudCrm.Blazor` and `CloudCrm.Blazor.Client` projects, as pages are sometimes rendered on the server side and sometimes on the client side. + +#### For the Server Project +![abp-studio-generate-proxy-blazor-server](images/abp-studio-generate-proxy-blazor-webapp-server-dark.png) + +This will open the Generate C# Proxies window. Select the `CloudCrm.CatalogService` application this will automatically fill in the *URL* field. Next, choose the catalog module, set the service type to *Application*, and check the *Without contracts* option, since the `CloudCrm.Blazor` project already references the `CloudCrm.CatalogService.Contracts` package. + +#### For client project +![abp-studio-generate-proxy-blazor](images/abp-studio-generate-proxy-blazor-dark.png) + +This will open the Generate C# Proxies window. Select the `CloudCrm.CatalogService` application this will automatically fill in the *URL* field. Next, choose the catalog module, set the service type to *Application*, and check the *Without contracts* option, since the `CloudCrm.Blazor.Client` project already references the `CloudCrm.CatalogService.Contracts` package. + +{{else}} + +Now, we need to generate the [Static API Proxy](../../framework/api-development/static-csharp-clients.md) for the UI project. Right-click the {{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "BlazorServer"}} `CloudCrm.Blazor.Server` {{else if UI == "Blazor"}} `CloudCrm.Blazor.Client` {{end}} [package](../../studio/concepts.md#package) and select the *ABP CLI* -> *Generate Proxy* -> *C#* command: + +{{if UI == "MVC"}} +![abp-studio-generate-proxy-2](images/abp-studio-generate-proxy-2-dark.png) +{{else if UI == "BlazorServer"}} +![abp-studio-generate-proxy-blazor-server-2](images/abp-studio-generate-proxy-blazor-server-2-dark.png) +{{else if UI == "Blazor"}} +![abp-studio-generate-proxy-blazor-2](images/abp-studio-generate-proxy-2-blazor-dark.png) +{{end}} + +{{end}} + +It will open the *Generate C# Proxies* window. Select the `CloudCrm.OrderingService` application, and it will automatically populate the *URL* field. Choose the *ordering* module and service type is *application* lastly check the *Without contracts* checkbox, since we already have a dependency on the `CloudCrm.OrderingService.Contracts` package in the {{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "BlazorServer"}} `CloudCrm.Blazor.Server` {{else if UI == "Blazor"}} `CloudCrm.Blazor.Client` {{end}} project: + +![abp-studio-generate-proxy-window-ordering-module](images/abp-studio-generate-proxy-window-ordering-module-dark.png) + ### Adding the Menu Item > ABP provides a modular navigation [menu system](../../framework/ui/mvc-razor-pages/navigation-menu.md) that allows you to define the menu items in a modular way. -Finally, we need to add a menu item to the sidebar to navigate to the `Orders` page. Open the `CloudCrmMenus` file in the `Navigation` folder of the `CloudCrm.Web` project and edit with the following code: +Finally, we need to add a menu item to the sidebar to navigate to the `Orders` page. Open the `CloudCrmMenus` file in the `Navigation` folder of the {{if UI == "MVC"}} `CloudCrm.Web` {{else}} `CloudCrm.Blazor.Client` {{end}} project and edit with the following code: ```csharp namespace CloudCrm.Web.Navigation; @@ -391,7 +482,7 @@ public class CloudCrmMenus } ``` -Then, open the `CloudCrmMenuContributor` class in the `CloudCrm.Web` project, located in the `Navigation` folder, and add the following code to `ConfigureMainMenuAsync` method: +Then, open the `CloudCrmMenuContributor` class in the {{if UI == "MVC"}} `CloudCrm.Web` {{else}} `CloudCrm.Blazor` {{end}} project, located in the `Navigation` folder, and add the following code to `ConfigureMainMenuAsync` method: ```csharp private static async Task ConfigureMainMenuAsync(MenuConfigurationContext context) @@ -409,15 +500,152 @@ private static async Task ConfigureMainMenuAsync(MenuConfigurationContext contex } ``` +{{end}} + +{{if UI == "NG"}} + +### Generating the Proxies + +Run the following command line under the `Angular` project folder to generate the UI proxy for the `OrderingService`: + +```bash +abp generate-proxy -t ng -m ordering -u http://localhost:44311 --target ordering-service +``` + +> Make sure the url port (example 44311) must be same with your OrderService's port. + +For more information, please refer to the [Service Proxies](https://abp.io/docs/latest/framework/ui/angular/service-proxies) documentation. + +### Create Order Module + +Run the following command line to create a new module, named `OrderModule` in the root folder of the angular application: + +```bash +yarn ng generate module order --module ordering-service --project ordering-service --routing --route orders +``` + +### Add Order Route + +* Create `order-base.routes.ts` file under the `projects/ordering-service/config/src/providers` folder and add the following code: + +*order-base.routes.ts* +```typescript +import { ABP, eLayoutType } from '@abp/ng.core'; + +import { eOrderingServiceRouteNames } from '../enums/route-names'; + +export const ORDER_BASE_ROUTES: ABP.Route[] = [ + { + path: '/ordering-service/orders', + parentName: eOrderingServiceRouteNames.OrderingService, + name: 'OrderingService::Menu:Orders', + layout: eLayoutType.application, + breadcrumbText: 'OrderingService::Orders', + }, +]; +``` + +* Create `order-route.provider.ts` file under the `projects/ordering-service/config/src/providers` folder and add the following code: + +*order-route.provider.ts* +```typescript +import { inject, provideAppInitializer } from '@angular/core'; +import { ABP, RoutesService } from '@abp/ng.core'; +import { ORDER_BASE_ROUTES } from './order-base.routes'; + +export const ORDERS_ORDER_ROUTE_PROVIDER = [ + provideAppInitializer(() => { + configureRoutes(); + }), +]; + +function configureRoutes() { + const routesService = inject(RoutesService); + const routes: ABP.Route[] = [...ORDER_BASE_ROUTES]; + routesService.add(routes); +} +``` + +* Open the `projects/ordering-service/config/src/ordering-service-config.module.ts` file and add `ORDERS_ORDER_ROUTE_PROVIDER` to the `providers` array as following code: + +*ordering-service-config.module.ts* +```typescript +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { ORDERING_SERVICE_ROUTE_PROVIDERS } from './providers/route.provider'; +import { ORDERS_ORDER_ROUTE_PROVIDER } from './providers/order-route.provider'; + +@NgModule() +export class OrderingServiceConfigModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: OrderingServiceConfigModule, + providers: [ORDERING_SERVICE_ROUTE_PROVIDERS, ORDERS_ORDER_ROUTE_PROVIDER], + }; + } +} +``` + +### Create Order Page + +* Create `order.component.ts` file under the `projects/ordering-service/src/lib/order` folder as following code: + +```typescript +import { Component } from '@angular/core'; +import { OrderDto, OrderService } from './proxy/ordering-service/services'; + +@Component({ + selector: 'lib-order', + standalone: false, + templateUrl: './order.component.html', + styleUrl: './order.component.css' +}) +export class OrderComponent { + + items: OrderDto[] = []; + + constructor(private readonly proxy: OrderService) { + this.proxy.getList().subscribe((res) => { + this.items = res; + }); + } + +} +``` + +* Create `order.component.html` file under the `projects/ordering-service/src/lib/order` folder as following code: + +```html +
+
+ + + + + + + + + + + + + +
Order IDProduct IdCustomer Name
{%{{{item.id}}}%}{%{{{item.productId}}}%}{%{{{item.customerName}}}%}
+
+
+``` + +{{end}} + ## Building and Running the Application Now, we can run the application to see the changes. Please stop the applications if they are running. Then open the *Solution Runner* panel, right-click the `CloudCrm` root item, and select the *Start* command: -![abp-studio-run-build-start](images/abp-studio-run-start-all.png) +![abp-studio-run-build-start](images/abp-studio-run-start-all-dark.png) After the applications are started, you can *Browse* and navigate to the `Orders` page to see the list of orders: -![web-orders-page](images/web-orders-page.png) +![web-orders-page](images/web-orders-page-dark.png) Great! We have successfully implemented the Ordering module. However, there is a problem: diff --git a/docs/en/tutorials/microservice/part-06.md b/docs/en/tutorials/microservice/part-06.md index 1cc19bf6d8..e5a8f53fe5 100644 --- a/docs/en/tutorials/microservice/part-06.md +++ b/docs/en/tutorials/microservice/part-06.md @@ -1,5 +1,13 @@ # Microservice Tutorial Part 06: Integrating the services: HTTP API Calls +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -22,7 +30,7 @@ In this section, we will integrate the Ordering service with the Catalog service In a microservices architecture, each service is responsible for its own data and business logic. However, services often need to communicate with each other to fulfill their responsibilities. This communication can be synchronous or asynchronous, depending on the requirements. -![web-orders-page](images/web-orders-page.png) +![web-orders-page](images/web-orders-page-dark.png) In our case, the Ordering service needs to display the product name instead of the product ID. To achieve this, we need to call the Catalog service to retrieve the product details based on the product ID. This is a typical example of a synchronous communication pattern between microservices. As a solution to that problem, we will use an [integration service](../../framework/api-development/integration-services.md) that will handle the communication with the Catalog service. Integration service concept in ABP is designed for request/response style inter-module (in modular applications) and inter-microservice (in distributed systems) communication. @@ -111,11 +119,11 @@ Now that we have created the `IProductIntegrationService` interface and the `Pro First, we need to add a reference to the `CloudCrm.OrderingService` package in the Ordering service. Open the ABP Studio, and stop the application(s) if it is running. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select *Add* -> *Package Reference* command: -![add-package-reference-ordering-service](images/add-package-reference-ordering-service.png) +![add-package-reference-ordering-service](images/add-package-reference-ordering-service-dark.png) In the *Add Package Reference* window, select the `CloudCrm.CatalogService.Contracts` package from the *This solution* tab. Click the *OK* button to add the reference: -![add-catalog-service-contracts-reference](images/add-catalog-service-contracts-reference.png) +![add-catalog-service-contracts-reference](images/add-catalog-service-contracts-reference-dark.png) ABP Studio adds the package reference and arranges the [module](../../framework/architecture/modularity/basics.md) dependency. @@ -236,25 +244,13 @@ Let's explain the changes we made: We have created the `IProductIntegrationService` interface and the `ProductIntegrationService` class in the `CloudCrm.CatalogService` solution. Now, we need to generate the proxy classes for the integration service in the `CloudCrm.OrderingService` package. First, *Start* the `CloudCrm.CatalogService` application in ABP Studio *Solution Runner*. Then, open the *Solution Explorer* and right-click on the `CloudCrm.OrderingService` package. Select the *ABP CLI* -> *Generate Proxy* -> *C#* command: -![generate-proxy-catalog-service](images/generate-proxy-catalog-service.png) +![generate-proxy-catalog-service](images/generate-proxy-catalog-service-dark.png) It opens the *Generate C# proxies* window. Select the `CloudCrm.CatalogService` application from the *Application* dropdown list. Then, choose the *catalog* module from the *Module* dropdown list and choose the *integration* service from the *Service type* dropdown list. Check the *Without contracts* checkbox and click the *Generate* button: -![generate-catalog-service-proxy](images/generate-catalog-service-proxy.png) +![generate-catalog-service-proxy](images/generate-catalog-service-proxy-dark.png) -We have generated the proxy classes for the `IProductIntegrationService` interface. Now, we must add the *Remote Service* url to the `appsettings.json` file of the `CloudCrm.OrderingService` project. Open the `appsettings.json` file (the `appsettings.json` file of the `CloudCrm.OrderingService` project of the `CloudCrm.OrderingService` .NET solution) and add the *CatalogService* section following configuration: - -```json -{ - "RemoteServices": { - "CatalogService": { - "BaseUrl": "http://localhost:44334" - } - } -} -``` - -> **BaseUrl** refers to the base URL of the Catalog service. You can use the *Copy Url* option from the Catalog service's context menu in the ABP Studio **Solution Runner** to paste it here. +Proxy classes for the `IProductIntegrationService` interface have been generated. Lastly, open the `CloudCrmOrderingServiceModule` class (the `CloudCrmOrderingServiceModule.cs` file under the `CloudCrm.OrderingService` project of the `CloudCrm.OrderingService` .NET solution) and add the following code to the `ConfigureServices` method: @@ -271,6 +267,8 @@ public override void ConfigureServices(ServiceConfigurationContext context) ### Updating the UI to Display the Product Name +{{if UI == "MVC"}} + Open the `Index.cshtml` file (the `Index.cshtml` file under the `Pages/Orders` folder of the `CloudCrm.Web` project of the `CloudCrm.Web` .NET solution) and update the table content to display the product name instead of the product ID: ```html @@ -295,9 +293,53 @@ Open the `Index.cshtml` file (the `Index.cshtml` file under the `Pages/Orders` f ``` +{{else if UI == "NG"}} + +Open the `order.component.html` file (the `order.component.html` file under the `angular\projects\ordering-service\src\lib\order`) and update the table content to display the product name instead of the product ID: + +```html +
+
+ + + + + + + + + + + + + +
Order IDProduct NameCustomer Name
{%{{{item.id}}}%}{%{{{item.productName}}}%}{%{{{item.customerName}}}%}
+
+
+``` + +{{else if UI == "Blazor" || UI == "BlazorServer" || UI == "BlazorWebApp"}} + +Open the `Orders.razor` file (the `Orders.razor` file under the {{if UI == "BlazorServer"}} `CloudCrm.Blazor\Pages` {{else}} `CloudCrm.Blazor.Client\Pages` {{end}} and update the table content to display the product name instead of the product ID: + +```html +
    + @foreach (var order in OrderList) + { +
  • + Customer: @order.CustomerName
    + Product Name: @order.ProductName
    + State: @order.State +
  • + } +
+``` + +{{end}} + That's it! Now, you can *Start* the all applications and browse it in ABP Studio to see the result: -![web-orders-page-with-product-name](images/web-orders-page-with-product-name.png) +![web-orders-page-with-product-name](images/web-orders-page-with-product-name-dark.png) Now, the Ordering service displays the product name instead of the product ID. We have successfully integrated the Ordering service with the Catalog service using HTTP API calls. @@ -318,4 +360,4 @@ Open the `etc\helm\cloudcrm\charts\ordering\templates\ordering.yaml` file in a t value: "http://{%{{{ .Release.Name }}}%}-catalog" ```` -With this simple configuration, now the Ordering module can discover the catalog microservice's URL inside your Kubernetes cluster. +With this simple configuration, now the Ordering module can discover the catalog microservice's URL inside your Kubernetes cluster. \ No newline at end of file diff --git a/docs/en/tutorials/microservice/part-07.md b/docs/en/tutorials/microservice/part-07.md index 633789fc6c..620d49f6c5 100644 --- a/docs/en/tutorials/microservice/part-07.md +++ b/docs/en/tutorials/microservice/part-07.md @@ -1,5 +1,13 @@ # Microservice Tutorial Part 07: Integrating the services: Using Distributed Events +````json +//[doc-params] +{ + "UI": ["MVC","Blazor","BlazorServer", "BlazorWebApp", "NG"], + "DB": ["EF","Mongo"] +} +```` + ````json //[doc-nav] { @@ -135,21 +143,21 @@ Since the `OrderPlacedEto` class is in the `CloudCrm.OrderingService.Contracts` Open the ABP Studio UI and stop the applications if they are running. Then, open the *Solution Explorer* panel and right-click on the `CloudCrm.CatalogService`. Select *Import Module* from the context menu: -![Import Module](images/import-module.png) +![Import Module](images/import-module-dark.png) In the opening dialog, find and select the `CloudCrm.OrderingService` module, check the *Install this module* option, click the *OK* button: -![Import Module Dialog](images/import-module-dialog.png) +![Import Module Dialog](images/import-module-dialog-dark.png) Once you click the OK button, the Ordering service is imported to the Catalog service. It opens the *Install Module* dialog: -![Install Module Dialog](images/install-module-dialog.png) +![Install Module Dialog](images/install-module-dialog-dark.png) Here, select the `CloudCrm.OrderingService.Contracts` package on the left side (because we want to add that package reference) and `CloudCrm.CatalogService` package on the middle area (because we want to add the package reference to that project). You can check the ABP Studio's *Solution Explorer* panel to see the module and the project reference (dependency): -![catalog-service-dependency](images/catalog-service-dependency.png) +![catalog-service-dependency](images/catalog-service-dependency-dark.png) ### Handling the `OrderPlacedEto` Event @@ -205,7 +213,7 @@ To keep this tutorial simple, we will not implement a user interface for creatin Once the application is running and ready, [Browse](../../studio/running-applications.md#c-application) the `CloudCrm.OrderingService` application. Use the `POST /api/ordering/order` endpoint to create a new order: -![Create Order](images/create-order.png) +![Create Order](images/create-order-dark.png) Find the *Order* API, click the *Try it out* button, enter a sample value the *Request body* section, and click the *Execute* button: @@ -218,9 +226,9 @@ Find the *Order* API, click the *Try it out* button, enter a sample value the *R > **IMPORTANT:** Here, you should type a valid Product Id from the Products table of your database! -Once you press the *Execute* button, a new order is created. At that point, you can check the `/Orders` page to see if the new order is listed. You can also check the `/Products` page to see if the stock count of the related product is decreased by one in the `CloudCrm.Web` application. +Once you press the *Execute* button, a new order is created. At that point, you can check the `/Orders` page to see if the new order is listed. You can also check the `/Products` page to see if the stock count of the related product is decreased by one in the {{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "NG"}} `Angular` {{else}} `CloudCrm.Blazor` {{end}} application. -Here are sample screenshots from the Orders and Products pages of the `CloudCrm.Web` application: +Here are sample screenshots from the Orders and Products pages of the {{if UI == "MVC"}} `CloudCrm.Web` {{else if UI == "NG"}} `Angular` {{else}} `CloudCrm.Blazor` {{end}} application: ![Orders](images/orders.png) diff --git a/docs/en/tutorials/modular-crm/images/abp-studio-catalog-module-expanded-in-solution-explorer.png b/docs/en/tutorials/modular-crm/images/abp-studio-catalog-module-expanded-in-solution-explorer.png new file mode 100644 index 0000000000..b3720bb3fb Binary files /dev/null and b/docs/en/tutorials/modular-crm/images/abp-studio-catalog-module-expanded-in-solution-explorer.png differ diff --git a/docs/en/tutorials/modular-crm/images/solution-explorer-modular-crm-initial-with-modules-v2.png b/docs/en/tutorials/modular-crm/images/solution-explorer-modular-crm-initial-with-modules-v2.png new file mode 100644 index 0000000000..ea29349339 Binary files /dev/null and b/docs/en/tutorials/modular-crm/images/solution-explorer-modular-crm-initial-with-modules-v2.png differ diff --git a/docs/en/tutorials/modular-crm/images/solution-explorer-modular-crm-initial-with-modules.png b/docs/en/tutorials/modular-crm/images/solution-explorer-modular-crm-initial-with-modules.png deleted file mode 100644 index f664910c58..0000000000 Binary files a/docs/en/tutorials/modular-crm/images/solution-explorer-modular-crm-initial-with-modules.png and /dev/null differ diff --git a/docs/en/tutorials/modular-crm/part-01.md b/docs/en/tutorials/modular-crm/part-01.md index 7b2a650f58..c3dfb50f22 100644 --- a/docs/en/tutorials/modular-crm/part-01.md +++ b/docs/en/tutorials/modular-crm/part-01.md @@ -42,23 +42,26 @@ The *Standard Module* template is suggested for building modular monolith applic The initial solution structure should be like the following in ABP Studio's *[Solution Explorer](../../studio/solution-explorer.md)*: -![solution-explorer-modular-crm-initial-with-modules](images/solution-explorer-modular-crm-initial-with-modules.png) +![solution-explorer-modular-crm-initial-with-modules](images/solution-explorer-modular-crm-initial-with-modules-v2.png) -Initially, you see a `ModularCrm` solution, a `ModularCrm` module under that solution (our main single layer application), and a `modules` folder that contains the `ModularCrm.Catalog` module and its sub .NET projects. +Initially, you see a `ModularCrm` solution with two solution folders: -> An ABP Studio module is typically a .NET solution and an ABP Studio solution is an umbrella concept for multiple .NET Solutions (see the [concepts](../../studio/concepts.md) document for more). +* `main`: That folder contains the `ModularCrm` ABP Studio Module, which is the main executable web application of the solution. +* `modules`: A folder to place your modules. It contains the `ModularCrm.Catalog` module initially. -The `ModularCrm` module is the core of your application, built as a single-layer ASP.NET Core Web application. On the other hand, the `ModularCrm.Catalog` module consist of four packages (.NET projects) and used to implement the catalog module's functionality. +> An **ABP Studio Module** is typically a separate .NET solution. On the other hand, an **ABP Studio Solution** is an umbrella concept for multiple .NET Solutions (see the [concepts](../../studio/concepts.md) document for details). ## Catalog Module's Packages -Here are the .NET projects (ABP Studio packages) of the Catalog module: +If you expand it, you can see the .NET projects (ABP Studio Packages) of the `ModularCrm.Catalog` module: + +![abp-studio-catalog-module-expanded-in-solution-explorer](images/abp-studio-catalog-module-expanded-in-solution-explorer.png) - `ModularCrm.Catalog`: The main module project that contains your [entities](../../framework/architecture/domain-driven-design/entities.md), [application service](../../framework/architecture/domain-driven-design/application-services.md) implementations and other business objects -- `ModularCrm.Catalog.Contracts`: Basically contains [application service](../../framework/architecture/domain-driven-design/application-services.md) interfaces and [DTOs](../../framework/architecture/domain-driven-design/data-transfer-objects.md) -- `ModularCrm.Catalog.Tests`: Unit and integration tests (if you selected the _Include Tests_ option) -- `ModularCrm.Catalog.UI`: Contains user interface pages components for the module +- `ModularCrm.Catalog.Contracts`: Basically contains [application service](../../framework/architecture/domain-driven-design/application-services.md) interfaces and [DTOs](../../framework/architecture/domain-driven-design/data-transfer-objects.md). These interfaces then can be used by client modules for integration purposes or by the user interface to perform use cases related to that module +- `ModularCrm.Catalog.Tests`: Unit and integration tests (if you selected the _Include Tests_ option) for that module +- `ModularCrm.Catalog.UI`: Contains user interface pages and components for the module ## Summary -You've created the initial single layer monolith modular solution with a Catalog module included. In the next part, you will learn how install the Catalog module to the main application. +You've created the initial single layer monolith modular solution with a Catalog module included. In the [next part](part-02.md), you will learn how install the Catalog module to the main application. diff --git a/docs/en/tutorials/modular-crm/part-02.md b/docs/en/tutorials/modular-crm/part-02.md index e175823350..383f5bd2a9 100644 --- a/docs/en/tutorials/modular-crm/part-02.md +++ b/docs/en/tutorials/modular-crm/part-02.md @@ -16,20 +16,6 @@ In this part, you will install the `ModularCrm.Catalog` module to the main application, which was created in the [previous part](part-01.md). -## Creating an ABP Studio Solution Folder - -Before installing the `ModularCrm.Catalog` module to the main application, let's create a "main" folder and move the `ModularCrm` module (the main application) to it to tidy up the solution structure. Right-click to the solution root on the *Solution Explorer* panel, and select *Add* -> *New Folder* command: - -![abp-studio-add-new-folder-command-2](images/abp-studio-add-new-folder-command-2.png) - -That command opens a dialog where you can set the *Folder name*: - -![abp-studio-new-folder-dialog](images/abp-studio-new-folder-dialog.png) - -After the folder is created, now you can move the `ModularCrm` module to the `main` folder (simply by drag & drop). The _Solution Explorer_ panel should look like the following figure now: - -![abp-studio-solution-explorer-with-folders](images/abp-studio-solution-explorer-with-folders-v2.png) - ## Installing the Catalog Module to the Main Application A module does not contain an executable application inside. The `ModularCrm.Catalog.UI` project is just a class library project, not an executable web application. A module should be installed in an executable application to run it. @@ -62,7 +48,7 @@ Graph Build is a dotnet CLI command that recursively builds all the referenced d > While developing multi-module solutions with ABP Studio, you may need to perform *Graph Build* on the root/main module if you change the depending modules. -### Run the Main Application +## Running the Main Application Open the *Solution Runner* panel, click the *Play* button (near to the solution root), right-click the `ModularCrm` application and select the *Browse* command. It will open the web application in the built-in browser. Then you can navigate to the *Catalog* page on the main menu of the application to see the Catalog page that is coming from the `ModularCrm.Catalog` module: diff --git a/docs/en/tutorials/modular-crm/part-03.md b/docs/en/tutorials/modular-crm/part-03.md index b9862afd4b..fb19277895 100644 --- a/docs/en/tutorials/modular-crm/part-03.md +++ b/docs/en/tutorials/modular-crm/part-03.md @@ -353,9 +353,9 @@ We've added the `CreateMap();` line to define the mapping. To create HTTP API endpoints for the catalog module, you have two options: * You can create a regular ASP.NET Core Controller class in the `ModularCrm.Catalog` project, inject `IProductAppService` and create wrapper methods for each public method of the product application service. You will do this later while you create the Ordering module. (Also, you can check the `SampleController` class under the **Samples** folder in the `ModularCrm.Catalog` project for an example) -* Alternatively, you can use the ABP's [Auto API Controllers](../../framework/api-development/auto-controllers.md) feature to expose your application services as API controllers by conventions. We will do it here. +* Alternatively, you can use the ABP's [Auto API Controllers](../../framework/api-development/auto-controllers.md) feature to expose your application services as API controllers by conventions. -Open the `CatalogModule` class in the Catalog module's .NET solution (the `ModularCrm.Catalog` .NET solution, the `ModularCrm.Catalog` .NET project) in your IDE, find the `ConfigureServices` method and add the following code block into that method: +We will use the second approach here. Open the `CatalogModule` class in the Catalog module's .NET solution (the `ModularCrm.Catalog` .NET solution, the `ModularCrm.Catalog` .NET project) in your IDE, find the `ConfigureServices` method and add the following code block into that method: ````csharp Configure(options => @@ -375,15 +375,13 @@ Now, ABP will automatically expose the application services defined in the `Modu This section will create a few example products using the [Swagger UI](../../framework/api-development/swagger.md). Thus, you will have some sample products to show on the UI. -Now, right-click the `ModularCrm` under the `main` folder in the Solution Explorer panel and select the *Dotnet CLI* -> *Graph Build* command. This will ensure that the catalog module and the main application are built and ready to run. - -After the build process completes, open the Solution Runner panel and click the *Play* button near the solution root. Once the `ModularCrm` application runs, you can right-click it and select the *Browse* command to open the user interface. +Open the Solution Runner panel and click the *Play* button near the solution root. Once the `ModularCrm` application runs, you can right-click it and select the *Browse* command to open the user interface. Once you see the user interface of the web application, type `/swagger` at the end of the URL to open the Swagger UI. If you scroll down, you should see the `Catalog` API: ![abp-studio-swagger-ui-in-browser](images/abp-studio-swagger-ui-in-browser.png) -> **Note:** If you have a swagger error on the UI, then you can open the `SampleAppService` class in the `ModularCrm.Catalog` project and add `[RemoteService(false)]` attribute to the `SampleAppService` class. With this attribute, the `SampleAppService` class will not be exposed as a remote service automatically but since there is a `SampleController` class in the `ModularCrm.Catalog` project, the `Catalog` API will be exposed as a remote service. +> If you don't see the Product API, you may need to re-build the entire solution. Right-click the `ModularCrm` under the `main` folder in the ABP Studio Solution Explorer panel and select the *Dotnet CLI* -> *Graph Build* command. This will ensure that the catalog module and the main application are completely built. Expand the `POST /api/catalog/product` API and click the *Try it out* button as shown in the following figure: @@ -443,7 +441,6 @@ Here, you simply use the `IProductAppService` to get a list of all products and @page @using Microsoft.Extensions.Localization @using ModularCrm.Catalog.Localization -@using ModularCrm.Catalog.UI.Pages.Catalog @model ModularCrm.Catalog.UI.Pages.Catalog.IndexModel @inject IStringLocalizer L diff --git a/docs/en/tutorials/modular-crm/part-05.md b/docs/en/tutorials/modular-crm/part-05.md index af47fadf29..139c4a9a8b 100644 --- a/docs/en/tutorials/modular-crm/part-05.md +++ b/docs/en/tutorials/modular-crm/part-05.md @@ -75,7 +75,6 @@ Open your IDE, in `Data` folder under the `ModularCrm.Ordering` project, and edi ````csharp using Microsoft.EntityFrameworkCore; -using ModularCrm.Ordering.Entities; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -92,7 +91,6 @@ Afterwards, create *Orders* `DbSet` for the `OrderingDbContext` class in the `Da ````csharp using Microsoft.EntityFrameworkCore; -using ModularCrm.Ordering.Entities; using Volo.Abp.Data; using Volo.Abp.EntityFrameworkCore; @@ -103,9 +101,10 @@ public class OrderingDbContext : AbpDbContext, IOrderingDbCon { public DbSet Orders { get; set; } - public OrderingDbContext(DbContextOptions options) : base(options) + public OrderingDbContext(DbContextOptions options) + : base(options) { - + } protected override void OnModelCreating(ModelBuilder builder) @@ -123,7 +122,6 @@ It is best to configure the database table mapping for the `Order` entity in the ````csharp using Microsoft.EntityFrameworkCore; -using ModularCrm.Ordering.Entities; using Volo.Abp; using Volo.Abp.EntityFrameworkCore.Modeling; @@ -184,7 +182,7 @@ public class ModularCrmDbContext : protected override void OnModelCreating(ModelBuilder builder) { ... - builder.ConfigureOrdering(); //NEW: CALL THE EXTENSION METHOD + builder.ConfigureOrdering(); } ```` @@ -309,7 +307,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; -namespace ModularCrm.Ordering.Services; +namespace ModularCrm.Ordering; public class OrderAppService : OrderingAppService, IOrderAppService { @@ -360,15 +358,13 @@ This will tell the ABP framework to create API controllers for the application s This section will create a few example orders using the [Swagger UI](../../framework/api-development/swagger.md). Thus, you will have some sample orders to show on the UI. -Now, right-click the `ModularCrm` under the `main` folder in the Solution Explorer panel and select the *Dotnet CLI* -> *Graph Build* command. This will ensure that the order module and the main application are built and ready to run. - -After the build process completes, open the Solution Runner panel and click the *Play* button near the solution root. Once the `ModularCrm` application runs, you can right-click it and select the *Browse* command to open the user interface. +Open the Solution Runner panel and click the *Play* button near the solution root. Once the `ModularCrm` application runs, you can right-click it and select the *Browse* command to open the user interface. Once you see the user interface of the web application, type `/swagger` at the end of the URL to open the Swagger UI. If you scroll down, you should see the `Order` API: ![abp-studio-ordering-swagger-ui-in-browser](images/abp-studio-ordering-swagger-ui-in-browser-v2.png) -> **Note:** If you have a swagger error on the UI, then you can open the `SampleAppService` class in the `ModularCrm.Ordering` project and add `[RemoteService(false)]` attribute to the `SampleAppService` class. With this attribute, the `SampleAppService` class will not be exposed as a remote service automatically but since there is a `SampleController` class in the `ModularCrm.Catalog` project, the `Catalog` API will be exposed as a remote service. +> If you don't see the Order API, you may need to re-build the entire solution. Right-click the `ModularCrm` under the `main` folder in the ABP Studio Solution Explorer panel and select the *Dotnet CLI* -> *Graph Build* command. This will ensure that all the modules and the main application are completely built. Expand the `POST /api/ordering/order` API and click the *Try it out* button. Then, create a few orders by filling in the request body and clicking the *Execute* button: diff --git a/docs/en/tutorials/modular-crm/part-06.md b/docs/en/tutorials/modular-crm/part-06.md index 568fa4d800..75af841312 100644 --- a/docs/en/tutorials/modular-crm/part-06.md +++ b/docs/en/tutorials/modular-crm/part-06.md @@ -56,11 +56,10 @@ Open the `IProductIntegrationService.cs` file and replace it's content with the using System; using System.Collections.Generic; using System.Threading.Tasks; -using ModularCrm.Catalog; using Volo.Abp; using Volo.Abp.Application.Services; -namespace ModularCrm.Products.Integration; +namespace ModularCrm.Catalog.Integration; [IntegrationService] public interface IProductIntegrationService : IApplicationService @@ -91,13 +90,10 @@ Open the `ProductIntegrationService.cs` file and replace its content with the fo using System; using System.Collections.Generic; using System.Threading.Tasks; -using ModularCrm.Catalog; -using Volo.Abp; using Volo.Abp.Domain.Repositories; -namespace ModularCrm.Products.Integration; +namespace ModularCrm.Catalog.Integration; -[IntegrationService] public class ProductIntegrationService : CatalogAppService, IProductIntegrationService { @@ -152,13 +148,12 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using ModularCrm.Products.Integration; -using Volo.Abp.Application.Services; +using ModularCrm.Catalog.Integration; using Volo.Abp.Domain.Repositories; namespace ModularCrm.Ordering; -public class OrderAppService : ApplicationService, IOrderAppService +public class OrderAppService : OrderingAppService, IOrderAppService { private readonly IRepository _orderRepository; private readonly IProductIntegrationService _productIntegrationService; diff --git a/docs/en/tutorials/modular-crm/part-07.md b/docs/en/tutorials/modular-crm/part-07.md index 8006bcae54..49eb24b71f 100644 --- a/docs/en/tutorials/modular-crm/part-07.md +++ b/docs/en/tutorials/modular-crm/part-07.md @@ -64,12 +64,12 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using ModularCrm.Catalog.Integration; using ModularCrm.Ordering.Events; -using ModularCrm.Products.Integration; using Volo.Abp.Domain.Repositories; using Volo.Abp.EventBus.Distributed; -namespace ModularCrm.Ordering.Services; +namespace ModularCrm.Ordering; public class OrderAppService : OrderingAppService, IOrderAppService { @@ -163,22 +163,21 @@ You can check the ABP Studio's *Solution Explorer* panel to see the module impor Now, it is possible to use the `OrderPlacedEto` class inside the Catalog module since it has the `ModularCrm.Ordering.Contracts` package reference. -Open the Catalog module's .NET solution in your IDE, locate the `ModularCrm.Catalog` project, and create a new `Orders` folder and an `OrderEventHandler` class inside that folder. The final folder structure should be like this: +Open the Catalog module's .NET solution in your IDE, locate the `ModularCrm.Catalog` project, and create a new `EventHandlers` folder and an `OrderEventHandler` class inside that folder. The final folder structure should be like this: ![visual-studio-order-event-handler](images/visual-studio-order-event-handler-v2.png) Replace the `OrderEventHandler.cs` file's content with the following code block: ````csharp -using ModularCrm.Catalog; -using ModularCrm.Ordering.Events; using System; using System.Threading.Tasks; +using ModularCrm.Ordering.Events; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; using Volo.Abp.EventBus.Distributed; -namespace ModularCrm.Products.Orders; +namespace ModularCrm.Catalog.EventHandlers; public class OrderEventHandler : IDistributedEventHandler, diff --git a/docs/en/tutorials/modular-crm/part-08.md b/docs/en/tutorials/modular-crm/part-08.md index 9e00bb1a9e..0046f3d82d 100644 --- a/docs/en/tutorials/modular-crm/part-08.md +++ b/docs/en/tutorials/modular-crm/part-08.md @@ -40,7 +40,7 @@ In this section, we will create an application service in the main application's Open the main `ModularCrm` .NET solution in your IDE, create an `Orders` folder under the `Services` folder and add an `IOrderReportingAppService` interface. Here is the definition of that interface: ````csharp -using ModularCrm.Orders; +using ModularCrm.Services.Dtos.Orders; using Volo.Abp.Application.Services; namespace ModularCrm.Services.Orders; @@ -51,13 +51,12 @@ public interface IOrderReportingAppService : IApplicationService } ```` -We have a single method, `GetLatestOrders`, that will return a list of the latest orders. We should also define the `OrderReportDto` class that that method returns. Create the `Orders` folder under the `Services/Dtos` folder and create a class named `OrderReportDto`. +We have a single method, `GetLatestOrders`, that will return a list of the latest orders. We should also define the `OrderReportDto` class that method returns. Create the `Orders` folder under the `Services/Dtos` folder and create a class named `OrderReportDto`. ````csharp -using System; using ModularCrm.Ordering; -namespace ModularCrm.Orders; +namespace ModularCrm.Services.Dtos.Orders; public class OrderReportDto { @@ -87,11 +86,10 @@ Open the `OrderReportingAppService.cs` file and change its content by the follow ````csharp using ModularCrm.Catalog; using ModularCrm.Ordering; -using ModularCrm.Services; -using ModularCrm.Services.Orders; +using ModularCrm.Services.Dtos.Orders; using Volo.Abp.Domain.Repositories; -namespace ModularCrm.Orders; +namespace ModularCrm.Services.Orders; public class OrderReportingAppService : ModularCrmAppService, @@ -114,16 +112,16 @@ public class OrderReportingAppService : var products = await _productRepository.GetQueryableAsync(); var latestOrders = (from order in orders - join product in products on order.ProductId equals product.Id - orderby order.CreationTime descending - select new OrderReportDto - { - OrderId = order.Id, - CustomerName = order.CustomerName, - State = order.State, - ProductId = product.Id, - ProductName = product.Name - }) + join product in products on order.ProductId equals product.Id + orderby order.CreationTime descending + select new OrderReportDto + { + OrderId = order.Id, + CustomerName = order.CustomerName, + State = order.State, + ProductId = product.Id, + ProductName = product.Name + }) .Take(10) .ToList(); @@ -150,7 +148,7 @@ Open the ABP Studio UI, stop the application if it is running, build and run it You should get the order objects with product names. -Alternatively, you can visit the `/api/app/order-reporting/latest-orders` URL to directly execute the HTTP API on the browser (you should write the full URL, like `https://localhost:44303/api/app/order-reporting/latest-orders` - port can be different for your case) +Alternatively, you can visit the `/api/app/order-reporting/latest-orders` URL to directly execute the HTTP API on the browser (you should write the full URL, like `https://localhost:44303/api/app/order-reporting/latest-orders` - the **port number** can be different for your case) ## Summary diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs index ba21ef11fd..4fc7c4ca75 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Volo/Abp/AspNetCore/Authentication/OpenIdConnect/AbpAspNetCoreAuthenticationOpenIdConnectModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Authentication.OAuth; +using Volo.Abp.AspNetCore.Security; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.RemoteServices; @@ -16,5 +17,10 @@ public class AbpAspNetCoreAuthenticationOpenIdConnectModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddHttpClient(); + + Configure(options => + { + options.IgnoredScriptNoncePaths.Add("/signout-oidc"); + }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.Bundling/BlazorWebAssemblyScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.Bundling/BlazorWebAssemblyScriptContributor.cs index d24e5d6976..870d4a6e5f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.Bundling/BlazorWebAssemblyScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.Theming.Bundling/BlazorWebAssemblyScriptContributor.cs @@ -10,7 +10,6 @@ public class BlazorWebAssemblyScriptContributor : BundleContributor context.Files.AddIfNotContains("_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"); context.Files.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"); context.Files.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js"); - context.Files.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js"); context.Files.AddIfNotContains("_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/authentication-state-listener.js"); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs index 8b46f6e78c..3365ff6a85 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Modal/AbpModalFooterTagHelperService.cs @@ -96,7 +96,7 @@ public class AbpModalFooterTagHelperService : AbpTagHelperService predicate) + { + bundleConfiguration.Contributors.RemoveBundleFile(predicate); + return bundleConfiguration; + } + public static BundleConfiguration AddContributors(this BundleConfiguration bundleConfiguration, params IBundleContributor[] contributors) { Check.NotNull(contributors, nameof(contributors)); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs index 5f245c1827..e92538a23f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollection.cs @@ -76,6 +76,36 @@ public class BundleContributorCollection } } + public void RemoveBundleFile(string fileName) + { + RemoveBundleFile([fileName]); + } + + public void RemoveBundleFile(string[] fileNames) + { + var contributors = _contributors + .Where(x => x is BundleFileContributor bundleContributor && + bundleContributor.Files.Any(f => fileNames.Any(name => name.Equals(f.FileName, StringComparison.OrdinalIgnoreCase)))) + .Cast(); + foreach (var contributor in contributors) + { + contributor.Files.RemoveAll(x => fileNames.Any(name => name.Equals(x.FileName, StringComparison.OrdinalIgnoreCase))); + } + } + + public void RemoveBundleFile(Func predicate) + { + var contributors = _contributors + .Where(x => x is BundleFileContributor bundleContributor && + bundleContributor.Files.Any(f => predicate(f.FileName))) + .Cast(); + + foreach (var contributor in contributors) + { + contributor.Files.RemoveAll(x => predicate(x.FileName)); + } + } + public IReadOnlyList GetAll() { return _contributors.ToImmutableList(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs index addbe7e2cd..74f73fa1f3 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs @@ -1,4 +1,7 @@ -namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using System; +using System.Linq; + +namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; public static class BundleContributorCollectionExtensions { @@ -16,4 +19,14 @@ public static class BundleContributorCollectionExtensions { contributors.Add(new BundleFileContributor(files)); } + + public static void RemoveFile(this BundleContributorCollection contributors, params string[] files) + { + contributors.RemoveBundleFile(files.ToArray()); + } + + public static void RemoveFile(this BundleContributorCollection contributors, Func predicate) + { + contributors.RemoveBundleFile(predicate); + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs index 9bc15ae617..d0ba8b98c0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationPartSorter.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Volo.Abp.Modularity; @@ -15,152 +13,36 @@ public static class ApplicationPartSorter { public static void Sort(ApplicationPartManager partManager, IModuleContainer moduleContainer) { - /* Performing a double Reverse() to preserve the original order for non-sorted parts - */ + var orderedModuleAssemblies = moduleContainer.Modules + .Select((moduleDescriptor, index) => new { moduleDescriptor.Assembly, index }) + .ToDictionary(x => x.Assembly, x => x.index); - var dependencyDictionary = CreateDependencyDictionary(partManager, moduleContainer); + var modulesAssemblies = moduleContainer.Modules.Select(x => x.Assembly).ToList(); + var sortedTypes = partManager.ApplicationParts + .Where(x => modulesAssemblies.Contains(GetApplicationPartAssembly(x))) + .OrderBy(x => orderedModuleAssemblies[GetApplicationPartAssembly(x)]) + .ToList(); - var sortedParts = partManager - .ApplicationParts - .Reverse() //First Revers - .SortByDependencies(p => dependencyDictionary[p]); + var sortIndex = 0; + var sortedParts = partManager.ApplicationParts + .Select(x => modulesAssemblies.Contains(GetApplicationPartAssembly(x)) ? sortedTypes[sortIndex++] : x) + .ToList(); - sortedParts.Reverse(); //Reverse again - - //Replace the original parts with the sorted parts partManager.ApplicationParts.Clear(); + sortedParts.Reverse(); foreach (var applicationPart in sortedParts) { partManager.ApplicationParts.Add(applicationPart); } } - private static Dictionary> CreateDependencyDictionary( - ApplicationPartManager partManager, IModuleContainer moduleContainer) - { - var dependencyDictionary = new Dictionary>(); - - foreach (var applicationPart in partManager.ApplicationParts) - { - dependencyDictionary[applicationPart] = - CreateDependencyList(applicationPart, partManager, moduleContainer); - } - - return dependencyDictionary; - } - - private static List CreateDependencyList( - ApplicationPart applicationPart, - ApplicationPartManager partManager, - IModuleContainer moduleContainer) + private static Assembly GetApplicationPartAssembly(ApplicationPart part) { - var list = new List(); - - if (applicationPart is AssemblyPart assemblyPart) + return part switch { - AddDependencies(list, assemblyPart, partManager, moduleContainer); - } - else if (applicationPart is CompiledRazorAssemblyPart compiledRazorAssemblyPart) - { - AddDependencies(list, compiledRazorAssemblyPart, partManager, moduleContainer); - } - - return list; - } - - private static void AddDependencies( - List list, - AssemblyPart assemblyPart, - ApplicationPartManager partManager, - IModuleContainer moduleContainer) - { - var dependedAssemblyParts = GetDependedAssemblyParts( - partManager, - moduleContainer, - assemblyPart - ); - - list.AddRange(dependedAssemblyParts); - - foreach (var dependedAssemblyPart in dependedAssemblyParts) - { - var viewsPart = GetViewsPartOrNull(partManager, dependedAssemblyPart); - if (viewsPart != null) - { - list.Add(viewsPart); - } - } - } - - private static void AddDependencies( - List list, - CompiledRazorAssemblyPart compiledRazorAssemblyPart, - ApplicationPartManager partManager, - IModuleContainer moduleContainer) - { - if (!compiledRazorAssemblyPart.Name.EndsWith(".Views")) - { - return; - } - - var originalAssemblyPart = GetOriginalAssemblyPartOrNull(compiledRazorAssemblyPart, partManager); - if (originalAssemblyPart == null) - { - return; - } - - list.Add(originalAssemblyPart); - } - - private static AssemblyPart[] GetDependedAssemblyParts( - ApplicationPartManager partManager, - IModuleContainer moduleContainer, - AssemblyPart assemblyPart) - { - var moduleDescriptor = GetModuleDescriptorForAssemblyOrNull(moduleContainer, assemblyPart.Assembly); - if (moduleDescriptor == null) - { - return Array.Empty(); - } - - var moduleDependedAssemblies = moduleDescriptor - .Dependencies - .SelectMany(d => d.AllAssemblies) - .ToArray(); - - return partManager.ApplicationParts - .OfType() - .Where(a => a.Assembly.IsIn(moduleDependedAssemblies)) - .Distinct() - .ToArray(); - } - - private static CompiledRazorAssemblyPart? GetViewsPartOrNull(ApplicationPartManager partManager, - ApplicationPart assemblyPart) - { - var viewsAssemblyName = assemblyPart.Name + ".Views"; - return partManager - .ApplicationParts - .OfType() - .FirstOrDefault(p => p.Name == viewsAssemblyName); - } - - private static AssemblyPart? GetOriginalAssemblyPartOrNull( - CompiledRazorAssemblyPart compiledRazorAssemblyPart, - ApplicationPartManager partManager) - { - var originalAssemblyName = compiledRazorAssemblyPart.Name.RemovePostFix(".Views"); - return partManager.ApplicationParts - .OfType() - .FirstOrDefault(p => p.Name == originalAssemblyName); - } - - private static IAbpModuleDescriptor? GetModuleDescriptorForAssemblyOrNull( - IModuleContainer moduleContainer, - Assembly assembly) - { - return moduleContainer - .Modules - .FirstOrDefault(m => m.AllAssemblies.Contains(assembly)); + AssemblyPart assemblyPart => assemblyPart.Assembly, + CompiledRazorAssemblyPart compiledRazorAssemblyPart => compiledRazorAssemblyPart.Assembly, + _ => throw new AbpException("Unknown application part type") + }; } } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs index 2d231c46ee..f1f2c9016e 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/AbpSecurityHeadersMiddleware.cs @@ -25,7 +25,9 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende { var endpoint = context.GetEndpoint(); - if (endpoint?.Metadata.GetMetadata() != null) + if (endpoint?.Metadata.GetMetadata() != null || + await AlwaysIgnoreContentTypes(context) || + Options.Value.IgnoredScriptNoncePaths.Any(x => context.Request.Path.StartsWithSegments(x.EnsureStartsWith('/'), StringComparison.OrdinalIgnoreCase))) { await next.Invoke(context); return; @@ -41,13 +43,13 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende AddHeader(context, "X-Frame-Options", "SAMEORIGIN"); var requestAcceptTypeHtml = context.Request.Headers["Accept"].Any(x => - x!.Contains("text/html") || x.Contains("*/*") || x.Contains("application/xhtml+xml")); + x!.Contains("text/html", StringComparison.OrdinalIgnoreCase) || + x.Contains("*/*", StringComparison.OrdinalIgnoreCase) || + x.Contains("application/xhtml+xml", StringComparison.OrdinalIgnoreCase)); if (!requestAcceptTypeHtml || !Options.Value.UseContentSecurityPolicyHeader - || await AlwaysIgnoreContentTypes(context) - || endpoint == null - || Options.Value.IgnoredScriptNoncePaths.Any(x => context.Request.Path.StartsWithSegments(x.EnsureStartsWith('/'), StringComparison.OrdinalIgnoreCase))) + || endpoint == null) { AddOtherHeaders(context); await next.Invoke(context); @@ -60,7 +62,6 @@ public class AbpSecurityHeadersMiddleware : AbpMiddlewareBase, ITransientDepende context.Items.Add(AbpAspNetCoreConsts.ScriptNonceKey, randomValue); } - context.Response.OnStarting(() => { if (context.Response.Headers.ContainsKey("Content-Security-Policy")) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs index 71964653a7..092e996095 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/CleanCommand.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Cli.Args; +using Volo.Abp.Cli.Utils; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Cli.Commands; @@ -13,11 +14,14 @@ namespace Volo.Abp.Cli.Commands; public class CleanCommand : IConsoleCommand, ITransientDependency { public const string Name = "clean"; - + public ILogger Logger { get; set; } - public CleanCommand() + protected ICmdHelper CmdHelper { get; } + + public CleanCommand(ICmdHelper cmdHelper) { + CmdHelper = cmdHelper; Logger = NullLogger.Instance; } @@ -26,6 +30,10 @@ public class CleanCommand : IConsoleCommand, ITransientDependency var binEntries = Directory.EnumerateDirectories(Directory.GetCurrentDirectory(), "bin", SearchOption.AllDirectories); var objEntries = Directory.EnumerateDirectories(Directory.GetCurrentDirectory(), "obj", SearchOption.AllDirectories); + Logger.LogInformation("Cleaning the solution with 'dotnet clean' command..."); + CmdHelper.RunCmd($"dotnet clean", workingDirectory: Directory.GetCurrentDirectory()); + + Logger.LogInformation($"Removing 'bin' and 'obj' folders..."); foreach (var path in binEntries.Concat(objEntries)) { if (path.IndexOf("node_modules", StringComparison.OrdinalIgnoreCase) > 0) @@ -38,9 +46,9 @@ public class CleanCommand : IConsoleCommand, ITransientDependency Directory.Delete(path, true); } } + Logger.LogInformation($"'bin' and 'obj' folders removed successfully!"); - Logger.LogInformation($"BIN and OBJ folders have been successfully deleted!"); - + Logger.LogInformation("Solution cleaned successfully!"); return Task.CompletedTask; } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs index 2b7252e298..eab54dd453 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSettingProvider.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.Emailing; /// Defines settings to send emails. /// for all available configurations. /// -internal class EmailSettingProvider : SettingDefinitionProvider +public class EmailSettingProvider : SettingDefinitionProvider { public override void Define(ISettingDefinitionContext context) { diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs index 63ddc222bd..3d7b12f86e 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ChangeTrackers/AbpEfCoreNavigationHelper.cs @@ -31,14 +31,6 @@ public class AbpEfCoreNavigationHelper : ITransientDependency protected virtual void EntityEntryTrackedOrStateChanged(EntityEntry entityEntry) { - if (entityEntry.State is EntityState.Unchanged or EntityState.Modified) - { - foreach (var entry in EntityEntries.Values.Where(x => x.NavigationEntries.Any())) - { - entry.UpdateNavigationEntries(); - } - } - if (entityEntry.State != EntityState.Unchanged) { return; @@ -58,23 +50,11 @@ public class AbpEfCoreNavigationHelper : ITransientDependency EntityEntries.Add(entryId, new AbpEntityEntry(entryId, entityEntry)); } - protected virtual void DetectChanges(EntityEntry entityEntry) - { - if (entityEntry.State != EntityState.Added && - entityEntry.State != EntityState.Deleted && - entityEntry.State != EntityState.Modified) - { - return; - } - - RecursiveDetectChanges(entityEntry); - } - - protected virtual void RecursiveDetectChanges(EntityEntry entityEntry) + protected virtual void DetectChanges(EntityEntry entityEntry, bool checkEntityEntryState = true) { #pragma warning disable EF1001 var stateManager = entityEntry.Context.GetDependencies().StateManager; - var internalEntityEntityEntry = stateManager.Entries.FirstOrDefault(x => x.Entity == entityEntry.Entity); + var internalEntityEntityEntry = stateManager.TryGetEntry(entityEntry.Entity, throwOnNonUniqueness: false); if (internalEntityEntityEntry == null) { return; @@ -95,15 +75,16 @@ public class AbpEfCoreNavigationHelper : ITransientDependency continue; } - if (!abpEntityEntry.IsModified) + abpEntityEntry.UpdateNavigationEntries(); + if (!abpEntityEntry.IsModified && (!checkEntityEntryState || IsEntityEntryChanged(entityEntry))) { abpEntityEntry.IsModified = true; - RecursiveDetectChanges(abpEntityEntry.EntityEntry); + DetectChanges(abpEntityEntry.EntityEntry, false); } var navigationEntry = abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is INavigation navigationMetadata && navigationMetadata.ForeignKey == foreignKey) ?? abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is ISkipNavigation skipNavigationMetadata && skipNavigationMetadata.ForeignKey == foreignKey); - if (navigationEntry != null) + if (navigationEntry != null && IsEntityEntryChanged(entityEntry)) { navigationEntry.IsModified = true; } @@ -134,15 +115,16 @@ public class AbpEfCoreNavigationHelper : ITransientDependency continue; } - if (!abpEntityEntry.IsModified) + abpEntityEntry.UpdateNavigationEntries(); + if (!abpEntityEntry.IsModified && (!checkEntityEntryState || IsEntityEntryChanged(entityEntry))) { abpEntityEntry.IsModified = true; - RecursiveDetectChanges(abpEntityEntry.EntityEntry); + DetectChanges(abpEntityEntry.EntityEntry, false); } var navigationEntry = abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is INavigation navigationMetadata && navigationMetadata.ForeignKey == inverseForeignKey) ?? abpEntityEntry.NavigationEntries.FirstOrDefault(x => x.NavigationEntry.Metadata is ISkipNavigation skipNavigationMetadata && skipNavigationMetadata.ForeignKey == inverseForeignKey); - if (navigationEntry != null) + if (navigationEntry != null && (!checkEntityEntryState || IsEntityEntryChanged(entityEntry))) { navigationEntry.IsModified = true; } @@ -151,6 +133,13 @@ public class AbpEfCoreNavigationHelper : ITransientDependency #pragma warning restore EF1001 } + protected virtual bool IsEntityEntryChanged(EntityEntry entityEntry) + { + return entityEntry.State == EntityState.Added || + entityEntry.State == EntityState.Deleted || + entityEntry.State == EntityState.Modified; + } + public virtual List GetChangedEntityEntries() { return EntityEntries diff --git a/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpTenantResolveOptions.cs b/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpTenantResolveOptions.cs index 414983c87a..26640c3dfd 100644 --- a/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpTenantResolveOptions.cs +++ b/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpTenantResolveOptions.cs @@ -8,6 +8,11 @@ public class AbpTenantResolveOptions [NotNull] public List TenantResolvers { get; } + /// + /// Fallback tenant to use when no other resolver resolves a tenant. + /// + public string? FallbackTenant { get; set; } + public AbpTenantResolveOptions() { TenantResolvers = new List(); diff --git a/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantResolverNames.cs b/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantResolverNames.cs new file mode 100644 index 0000000000..3542d3a948 --- /dev/null +++ b/framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/TenantResolverNames.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Volo.Abp.MultiTenancy; + +public static class TenantResolverNames +{ + public const string FallbackTenant = "FallbackTenant"; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs index 3ae9ae67b1..4351ef62bb 100644 --- a/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs +++ b/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/TenantResolver.cs @@ -39,6 +39,12 @@ public class TenantResolver : ITenantResolver, ITransientDependency } } + if (result.TenantIdOrName.IsNullOrEmpty() && !string.IsNullOrWhiteSpace(_options.FallbackTenant)) + { + result.TenantIdOrName = _options.FallbackTenant; + result.AppliedResolvers.Add(TenantResolverNames.FallbackTenant); + } + return result; } } diff --git a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs index 7776b34b13..1f53e36260 100644 --- a/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Without_DomainResolver_Tests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Net; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationPart/ApplicationPartSorter_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationPart/ApplicationPartSorter_Tests.cs new file mode 100644 index 0000000000..829aa23527 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ApplicationPart/ApplicationPartSorter_Tests.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using Microsoft.AspNetCore.Mvc.ApplicationParts; +using NSubstitute; +using Shouldly; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationPart; + +public class ApplicationPartSorter_Tests +{ + [Fact] + public void Should_Sort_ApplicationParts_By_Module_Dependencies() + { + var moduleDescriptors = new List(); + var partManager = new ApplicationPartManager(); + + for (var i = 0; i < 10; i++) + { + var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName($"ModuleA{i}.dll"), AssemblyBuilderAccess.Run); + partManager.ApplicationParts.Add(new AssemblyPart(assembly)); + moduleDescriptors.Add(CreateModuleDescriptor(assembly)); + } + var randomApplicationParts = partManager.ApplicationParts.OrderBy(x => Guid.NewGuid()).ToList(); // Shuffle the parts + + // Additional part + randomApplicationParts.AddFirst(new CompiledRazorAssemblyPart(typeof(AbpAspNetCoreModule).Assembly)); + randomApplicationParts.Insert(5, new CompiledRazorAssemblyPart(typeof(AbpAspNetCoreMvcModule).Assembly)); + randomApplicationParts.AddLast(new CompiledRazorAssemblyPart(typeof(AbpVirtualFileSystemModule).Assembly)); + + partManager.ApplicationParts.Clear(); + foreach (var part in randomApplicationParts) + { + partManager.ApplicationParts.Add(part); + } + + var moduleContainer = CreateFakeModuleContainer(moduleDescriptors); + + ApplicationPartSorter.Sort(partManager, moduleContainer); + + // Act + partManager.ApplicationParts.Count.ShouldBe(13); // 10 modules + 3 additional parts + + var applicationParts = partManager.ApplicationParts.Reverse().ToList(); // Reverse the order to match the expected output + + applicationParts[0].ShouldBeOfType().Assembly.ShouldBe(typeof(AbpAspNetCoreModule).Assembly); + applicationParts[1].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA0"); + applicationParts[2].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA1"); + applicationParts[3].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA2"); + applicationParts[4].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA3"); + applicationParts[5].ShouldBeOfType().Assembly.ShouldBe(typeof(AbpAspNetCoreMvcModule).Assembly); + applicationParts[6].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA4"); + applicationParts[7].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA5"); + applicationParts[8].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA6"); + applicationParts[9].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA7"); + applicationParts[10].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA8"); + applicationParts[11].ShouldBeOfType().Assembly.GetName().Name.ShouldStartWith("ModuleA9"); + applicationParts[12].ShouldBeOfType().Assembly.ShouldBe(typeof(AbpVirtualFileSystemModule).Assembly); + } + + private static IModuleContainer CreateFakeModuleContainer(List moduleDescriptors) + { + var fakeModuleContainer = Substitute.For(); + fakeModuleContainer.Modules.Returns(moduleDescriptors); + return fakeModuleContainer; + } + + private static IAbpModuleDescriptor CreateModuleDescriptor(Assembly assembly) + { + var moduleDescriptor = Substitute.For(); + moduleDescriptor.Assembly.Returns(assembly); + return moduleDescriptor; + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs index 8e92558cb8..aa6462996a 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController.cs @@ -2,10 +2,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Security.Headers; +[Route("SecurityHeadersTest")] public class SecurityHeadersTestController : AbpController { + [HttpGet("Get")] public ActionResult Get() { return Content("OK"); } + + [HttpGet("ignored")] + public ActionResult Get_Ignored() + { + return Content("OK"); + } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs index 336c7fe8db..b71103620b 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Headers/SecurityHeadersTestController_Tests.cs @@ -15,6 +15,8 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase { options.UseContentSecurityPolicyHeader = true; options.Headers["Referrer-Policy"] = "no-referrer"; + + options.IgnoredScriptNoncePaths.Add("/SecurityHeadersTest/ignored"); }); base.ConfigureServices(services); @@ -27,7 +29,6 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase responseMessage.Headers.ShouldContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); responseMessage.Headers.ShouldContain(x => x.Key == "X-XSS-Protection" & x.Value.First().ToString() == "1; mode=block"); responseMessage.Headers.ShouldContain(x => x.Key == "X-Frame-Options" & x.Value.First().ToString() == "SAMEORIGIN"); - responseMessage.Headers.ShouldContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); } [Fact] @@ -37,4 +38,15 @@ public class SecurityHeadersTestController_Tests : AspNetCoreMvcTestBase responseMessage.Headers.ShouldNotBeEmpty(); responseMessage.Headers.ShouldContain(x => x.Key == "Referrer-Policy" && x.Value.First().ToString() == "no-referrer"); } + + [Fact] + public async Task SecurityHeaders_Should_Be_Ignored() + { + var responseMessage = await GetResponseAsync("/SecurityHeadersTest/ignored"); + responseMessage.Headers.ShouldNotBeEmpty(); + responseMessage.Headers.ShouldNotContain(x => x.Key == "X-Content-Type-Options" & x.Value.First().ToString() == "nosniff"); + responseMessage.Headers.ShouldNotContain(x => x.Key == "X-XSS-Protection" & x.Value.First().ToString() == "1; mode=block"); + responseMessage.Headers.ShouldNotContain(x => x.Key == "X-Frame-Options" & x.Value.First().ToString() == "SAMEORIGIN"); + responseMessage.Headers.ShouldNotContain(x => x.Key == "Referrer-Policy" && x.Value.First().ToString() == "no-referrer"); + } } diff --git a/framework/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/FallbackTenantResolveContributor_Tests.cs b/framework/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/FallbackTenantResolveContributor_Tests.cs new file mode 100644 index 0000000000..00bf669aaf --- /dev/null +++ b/framework/test/Volo.Abp.MultiTenancy.Tests/Volo/Abp/MultiTenancy/FallbackTenantResolveContributor_Tests.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Shouldly; +using Volo.Abp.MultiTenancy.ConfigurationStore; +using Xunit; + +namespace Volo.Abp.MultiTenancy; + +public class FallbackTenantResolveContributor_Tests : MultiTenancyTestBase +{ + private readonly Guid _testTenantId = Guid.NewGuid(); + private readonly string _testTenantName = "acme"; + private readonly string _testTenantNormalizedName = "ACME"; + + private readonly AbpTenantResolveOptions _options; + private readonly ITenantResolver _tenantResolver; + + public FallbackTenantResolveContributor_Tests() + { + _options = ServiceProvider.GetRequiredService>().Value; + _tenantResolver = ServiceProvider.GetRequiredService(); + } + + protected override void BeforeAddApplication(IServiceCollection services) + { + services.Configure(options => + { + options.Tenants = new[] + { + new TenantConfiguration(_testTenantId, _testTenantName, _testTenantNormalizedName) + }; + }); + + services.Configure(options => + { + options.FallbackTenant = _testTenantName; + }); + } + + [Fact] + public async Task Should_Resolve_To_Fallback_Tenant_If_No_Other_Contributor_Succeeds() + { + var result = await _tenantResolver.ResolveTenantIdOrNameAsync(); + + result.TenantIdOrName.ShouldBe(_testTenantName); + result.AppliedResolvers.ShouldContain(TenantResolverNames.FallbackTenant); + } + + [Fact] + public async Task Should_Not_Override_Resolved_Tenant() + { + // Arrange + var customTenantName = "resolved-tenant"; + _options.TenantResolvers.Insert(0, new TestTenantResolveContributor(customTenantName)); + + // Act + var result = await _tenantResolver.ResolveTenantIdOrNameAsync(); + + // Assert + result.TenantIdOrName.ShouldBe(customTenantName); + result.AppliedResolvers.First().ShouldBe("Test"); + result.AppliedResolvers.ShouldNotContain(TenantResolverNames.FallbackTenant); + } + + public class TestTenantResolveContributor : TenantResolveContributorBase + { + private readonly string _tenant; + + public TestTenantResolveContributor(string tenant) + { + _tenant = tenant; + } + + public override string Name => "Test"; + + public override Task ResolveAsync(ITenantResolveContext context) + { + context.TenantIdOrName = _tenant; + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/latest-versions.json b/latest-versions.json index 7e756d726b..21fbe41bf4 100644 --- a/latest-versions.json +++ b/latest-versions.json @@ -1,4 +1,13 @@ [ + { + "version": "9.2.1", + "releaseDate": "", + "type": "stable", + "message": "", + "leptonx": { + "version": "4.2.1" + } + }, { "version": "9.2.0", "releaseDate": "", diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.css b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.css index 36ca24e100..71840b79d2 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.css +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.css @@ -10,7 +10,7 @@ body a { text-decoration: none; } body .btn-primary { - background-color: #b84297 !important; + background-color: #E83090 !important; font-size: 12px; } body .for-mobile { @@ -298,7 +298,7 @@ body.scrolledMore .alert-criteria p.alert-p { } .docs-page .docs-sidebar .docs-tree-list ul li.selected-tree > span .fa { transform: rotate(90deg); - color: #b84297; + color: #E83090; } .docs-page .docs-sidebar .docs-tree-list ul li.selected-tree.last-link > span .fa { transform: rotate(0deg); @@ -482,7 +482,7 @@ body.scrolledMore .alert-criteria p.alert-p { margin-bottom: 1rem; margin-left: 0; padding: 1em 1.5em; - background-color: #e3edf2; + background-color: rgb(227, 237, 242); font-size: 1em; border-radius: 12px; color: #385766; @@ -549,7 +549,7 @@ body.scrolledMore .alert-criteria p.alert-p { background-color: #f4f6fa; border-color: #f4f6fa; border-radius: 12px; - background: rgba(190, 223, 238, 0.82); + background: hsla(199, 59%, 84%, 0.82); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); z-index: 3; @@ -600,16 +600,16 @@ body.scrolledMore .alert-criteria p.alert-p { font-weight: normal; } .docs-page .docs-page-index .docs-inner-anchors .navbar .nav-pills .nav-link.active { - border-left: 1px solid #b84297; + border-left: 1px solid #E83090; background: none; - color: #b84297; + color: #E83090; font-weight: normal; } .docs-page .docs-page-index .docs-inner-anchors .navbar .nav-pills .nav-pills .nav-link.active { - color: #b84297; + color: #E83090; } .docs-page .docs-page-index .docs-inner-anchors .navbar .nav-pills .nav-pills .nav-pills .nav-link.active { - color: #b84297; + color: #E83090; } .docs-page .docs-page-index .docs-inner-anchors .index-scroll { margin-left: -30px; @@ -667,7 +667,7 @@ body.scrolledMore .alert-criteria p.alert-p { display: none; } body .close-mmenu, -body .close-dmenu { + body .close-dmenu { position: absolute; top: -78px; left: 25px; @@ -720,7 +720,7 @@ body .close-dmenu { display: none; } .docs-page .docs-sidebar .docs-top .navbar.navbar-logo .navbar-collapse { - background: #38003d; + background: rgb(56, 0, 61); position: fixed; top: 86px; left: 0; diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.scss b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.scss index deba3d39c3..9d5de59393 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.scss +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/Styles/vs.scss @@ -16,7 +16,7 @@ body { } .btn-primary { - background-color: #b84297 !important; + background-color: #E83090 !important; font-size: 12px; } @@ -368,7 +368,7 @@ body { > span { .fa { transform: rotate(90deg); - color: #b84297; + color: #E83090; } } @@ -734,9 +734,9 @@ body { font-weight: normal; &.active { - border-left: 1px solid #b84297; + border-left: 1px solid #E83090; background: none; - color: #b84297; + color: #E83090; font-weight: normal; } } @@ -744,14 +744,14 @@ body { .nav-pills { .nav-link { &.active { - color: #b84297; + color: #E83090; } } .nav-pills { .nav-link { &.active { - color: #b84297; + color: #E83090; } } } diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs index 4423036b51..af0bce93db 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs @@ -97,9 +97,48 @@ public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppSer { await CheckProviderPolicy(providerName, providerKey); + var inputFeatureNames = input.Features.Select(f => f.Name).ToHashSet(); + var featureMap = input.Features.ToDictionary(f => f.Name); + var features = new Dictionary>(); + var processed = new HashSet(); + foreach (var feature in input.Features) { - await FeatureManager.SetAsync(feature.Name, feature.Value, providerName, providerKey); + if (!processed.Add(feature.Name)) + { + continue; + } + + var featureDefinition = await FeatureDefinitionManager.GetAsync(feature.Name); + var validChildren = new List(); + + foreach (var childFeature in featureDefinition.Children) + { + if (inputFeatureNames.Contains(childFeature.Name) && + featureMap.TryGetValue(childFeature.Name, out var childDto) && + processed.Add(childFeature.Name)) + { + validChildren.Add(childDto); + } + } + + features[feature] = validChildren; + } + + foreach (var feature in features) + { + var forceToSet = false; + foreach (var childFeature in feature.Value) + { + await FeatureManager.SetAsync(childFeature.Name, childFeature.Value, providerName, providerKey); + var value = await FeatureManager.GetOrNullWithProviderAsync(childFeature.Name, providerName, providerKey); + if (value.Provider.Name == providerName && value.Provider.Key == providerKey) + { + forceToSet = true; + } + } + + await FeatureManager.SetAsync(feature.Key.Name, feature.Key.Value, providerName, providerKey, forceToSet: forceToSet); } } diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs index 0bea533bc7..f3475d6ded 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs @@ -65,7 +65,42 @@ public class FeatureAppService_Tests : FeatureManagementApplicationTestBase x.Name == TestFeatureDefinitionProvider.SocialLogins && x.Value == false.ToString().ToLowerInvariant()) .ShouldBeTrue(); + } + + [Fact] + public async Task Select_Child_Feature_Should_Also_Update_Parent_Feature() + { + Login(_testData.User1Id); + + await _featureAppService.UpdateAsync(EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString(), new UpdateFeaturesDto() + { + Features = new List() + { + new UpdateFeatureDto() + { + Name = TestFeatureDefinitionProvider.EmailSupport, + Value = true.ToString().ToLowerInvariant() + }, + new UpdateFeatureDto() + { + Name = TestFeatureDefinitionProvider.EmailSupportMaxNumber, + Value = true.ToString().ToLowerInvariant() + } + } + }); + + (await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString())).Groups.SelectMany(g => g.Features).Any(x => + x.Name == TestFeatureDefinitionProvider.EmailSupportMaxNumber && + x.Value == true.ToString().ToLowerInvariant()) + .ShouldBeTrue(); + (await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName, + TestEditionIds.Regular.ToString())).Groups.SelectMany(g => g.Features).Any(x => + x.Name == TestFeatureDefinitionProvider.EmailSupport && + x.Value == true.ToString().ToLowerInvariant()) + .ShouldBeTrue(); } [Fact] diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs index 7590d8f3b9..0a19e56956 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs @@ -7,6 +7,7 @@ public class TestFeatureDefinitionProvider : FeatureDefinitionProvider { public const string SocialLogins = "SocialLogins"; public const string EmailSupport = "EmailSupport"; + public const string EmailSupportMaxNumber = "EmailSupportMaxNumber"; public const string DailyAnalysis = "DailyAnalysis"; public const string UserCount = "UserCount"; public const string ProjectCount = "ProjectCount"; @@ -21,11 +22,17 @@ public class TestFeatureDefinitionProvider : FeatureDefinitionProvider valueType: new ToggleStringValueType() ); - group.AddFeature( + var emailSupport = group.AddFeature( EmailSupport, + "true", valueType: new ToggleStringValueType() ); + emailSupport.CreateChild( + EmailSupportMaxNumber, + "false", + valueType: new ToggleStringValueType()); + group.AddFeature( DailyAnalysis, defaultValue: false.ToString().ToLowerInvariant(), //Optional, it is already false by default diff --git a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml index dc32cca59c..ea4a9f3347 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml +++ b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml @@ -15,17 +15,38 @@ @if (HttpContext.User.Identity != null && HttpContext.User.Identity.IsAuthenticated) {
    +

    Current User

    @foreach (var claim in HttpContext.User.Claims) {
  • @claim.Type : @claim.Value
  • }
+
    +

    oidc

    + @{ + var oidc = await HttpContext.AuthenticateAsync("oidc"); + if (oidc.Principal != null) + { + foreach (var claim in oidc.Principal.Claims) + { +
  • @claim.Type : @claim.Value
  • + } + } + } +
+ +

HttpContext.GetTokenAsync("access_token")
@await HttpContext.GetTokenAsync("access_token")

+

HttpContext.GetTokenAsync("id_token") +
+ @await HttpContext.GetTokenAsync("id_token") +

+ var client = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44303/api/claims"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await HttpContext.GetTokenAsync("access_token")); diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.Designer.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.Designer.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs index fec1a9f143..5c4b03ed99 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.Designer.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace OpenIddict.Demo.Server.Migrations { [DbContext(typeof(ServerDbContext))] - [Migration("20250215074649_Initial")] + [Migration("20250630055813_Initial")] partial class Initial { /// @@ -22,7 +22,7 @@ namespace OpenIddict.Demo.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("ProductVersion", "9.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -938,6 +938,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("FrontChannelLogoutUri") + .HasColumnType("nvarchar(max)"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs similarity index 99% rename from modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.cs rename to modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs index fdf8dac987..77775a3324 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250215074649_Initial.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/20250630055813_Initial.cs @@ -378,6 +378,7 @@ namespace OpenIddict.Demo.Server.Migrations RedirectUris = table.Column(type: "nvarchar(max)", nullable: true), Requirements = table.Column(type: "nvarchar(max)", nullable: true), Settings = table.Column(type: "nvarchar(max)", nullable: true), + FrontChannelLogoutUri = table.Column(type: "nvarchar(max)", nullable: true), ClientUri = table.Column(type: "nvarchar(max)", nullable: true), LogoUri = table.Column(type: "nvarchar(max)", nullable: true), ExtraProperties = table.Column(type: "nvarchar(max)", nullable: false), diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs index 4099fccc43..ff0eaf970c 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Migrations/ServerDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace OpenIddict.Demo.Server.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.SqlServer) - .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("ProductVersion", "9.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -935,6 +935,9 @@ namespace OpenIddict.Demo.Server.Migrations .HasColumnType("nvarchar(max)") .HasColumnName("ExtraProperties"); + b.Property("FrontChannelLogoutUri") + .HasColumnType("nvarchar(max)"); + b.Property("IsDeleted") .ValueGeneratedOnAdd() .HasColumnType("bit") diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml b/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml index fd28464cc7..daa3ee9b95 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Pages/Index.cshtml @@ -4,7 +4,13 @@ ViewData["Title"] = "Home page"; } -
-

Welcome

-

Learn about building Web apps with ASP.NET Core.

-
\ No newline at end of file +@if (HttpContext.User.Identity != null && HttpContext.User.Identity.IsAuthenticated) +{ +
    +

    Current User

    + @foreach (var claim in HttpContext.User.Claims) + { +
  • @claim.Type : @claim.Value
  • + } +
+} diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs index 5c2f6ef996..a8350e22e5 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/Controllers/AuthorizeController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; @@ -148,6 +149,13 @@ public class AuthorizeController : AbpOpenIdDictControllerBase case OpenIddictConstants.ConsentTypes.Explicit when authorizations.Any() && !request.HasPromptValue(OpenIddictConstants.PromptValues.Consent): var principal = await SignInManager.CreateUserPrincipalAsync(user); + var sid = dynamicPrincipal.FindFirst(JwtRegisteredClaimNames.Sid); + if (sid != null) + { + principal.RemoveClaims(JwtRegisteredClaimNames.Sid); + principal.AddClaim(JwtRegisteredClaimNames.Sid, sid.Value); + } + if (result.Properties != null && result.Properties.IsPersistent) { var claim = new Claim(AbpClaimTypes.RememberMe, true.ToString()).SetDestinations(OpenIddictConstants.Destinations.AccessToken); @@ -247,6 +255,13 @@ public class AuthorizeController : AbpOpenIdDictControllerBase var principal = await SignInManager.CreateUserPrincipalAsync(user); + var sid = User.FindFirst(JwtRegisteredClaimNames.Sid); + if (sid != null) + { + principal.RemoveClaims(JwtRegisteredClaimNames.Sid); + principal.AddClaim(JwtRegisteredClaimNames.Sid, sid.Value); + } + var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme); if (result.Succeeded && result.Properties != null && result.Properties.IsPersistent) { diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs index 3b5a4fc247..5fb2de1205 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationDescriptor.cs @@ -1,16 +1,22 @@ -using OpenIddict.Abstractions; +using System; +using OpenIddict.Abstractions; namespace Volo.Abp.OpenIddict.Applications; public class AbpApplicationDescriptor : OpenIddictApplicationDescriptor { + /// + /// Gets or sets the front-channel logout URI associated with the application. + /// + public virtual Uri FrontChannelLogoutUri { get; set; } + /// /// URI to further information about client. /// - public string ClientUri { get; set; } + public virtual string ClientUri { get; set; } /// /// URI to client logo. /// - public string LogoUri { get; set; } + public virtual string LogoUri { get; set; } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs index e02d671652..99b44e4293 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpApplicationManager.cs @@ -44,6 +44,17 @@ public class AbpApplicationManager : OpenIddictApplicationManager GetFrontChannelLogoutUriAsync(object application, CancellationToken cancellationToken = default) + { + Check.NotNull(application, nameof(application)); + Check.AssignableTo(application.GetType(), nameof(application)); + + return await Store.As().GetFrontChannelLogoutUriAsync(application.As(), cancellationToken); + } + public virtual async ValueTask GetClientUriAsync(object application, CancellationToken cancellationToken = default) { Check.NotNull(application, nameof(application)); - Check.AssignableTo(application.GetType(), nameof(application)); + Check.AssignableTo(application.GetType(), nameof(application)); return await Store.As().GetClientUriAsync(application.As(), cancellationToken); } @@ -71,8 +91,15 @@ public class AbpApplicationManager : OpenIddictApplicationManager GetLogoUriAsync(object application, CancellationToken cancellationToken = default) { Check.NotNull(application, nameof(application)); - Check.AssignableTo(application.GetType(), nameof(application)); + Check.AssignableTo(application.GetType(), nameof(application)); return await Store.As().GetLogoUriAsync(application.As(), cancellationToken); } + + protected virtual bool IsImplicitFileUri(Uri uri) + { + Check.NotNull(uri, nameof(uri)); + + return uri.IsAbsoluteUri && uri.IsFile && !uri.OriginalString.StartsWith(uri.Scheme, StringComparison.OrdinalIgnoreCase); + } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs index 416d90b3b7..f604a0960c 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/AbpOpenIddictApplicationStore.cs @@ -635,6 +635,13 @@ public class AbpOpenIddictApplicationStore : AbpOpenIddictStoreBase GetFrontChannelLogoutUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default) + { + Check.NotNull(application, nameof(application)); + + return await new ValueTask(application.FrontChannelLogoutUri); + } + public virtual ValueTask GetClientUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default) { Check.NotNull(application, nameof(application)); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs index 1f12a9d088..b0dd375908 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpApplicationManager.cs @@ -6,6 +6,8 @@ namespace Volo.Abp.OpenIddict.Applications; public interface IAbpApplicationManager : IOpenIddictApplicationManager { + ValueTask GetFrontChannelLogoutUriAsync(object application, CancellationToken cancellationToken = default); + ValueTask GetClientUriAsync(object application, CancellationToken cancellationToken = default); ValueTask GetLogoUriAsync(object application, CancellationToken cancellationToken = default); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs index ca2cd50102..9dd0b70515 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/IAbpOpenIdApplicationStore.cs @@ -6,6 +6,8 @@ namespace Volo.Abp.OpenIddict.Applications; public interface IAbpOpenIdApplicationStore : IOpenIddictApplicationStore { + ValueTask GetFrontChannelLogoutUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default); + ValueTask GetClientUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default); ValueTask GetLogoUriAsync(OpenIddictApplicationModel application, CancellationToken cancellationToken = default); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs index e88370e874..d2f7b5f752 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplication.cs @@ -94,13 +94,18 @@ public class OpenIddictApplication : FullAuditedAggregateRoot /// public virtual string Settings { get; set; } + /// + /// Gets or sets the front-channel logout URI associated with the application. + /// + public virtual string FrontChannelLogoutUri { get; set; } + /// /// URI to further information about client. /// - public string ClientUri { get; set; } + public virtual string ClientUri { get; set; } /// /// URI to client logo. /// - public string LogoUri { get; set; } + public virtual string LogoUri { get; set; } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs index 791136316a..818a40f973 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationExtensions.cs @@ -27,6 +27,7 @@ public static class OpenIddictApplicationExtensions RedirectUris = model.RedirectUris, Requirements = model.Requirements, Settings = model.Settings, + FrontChannelLogoutUri = model.FrontChannelLogoutUri, ClientUri = model.ClientUri, LogoUri = model.LogoUri }; @@ -59,6 +60,7 @@ public static class OpenIddictApplicationExtensions entity.RedirectUris = model.RedirectUris; entity.Requirements = model.Requirements; entity.Settings = model.Settings; + entity.FrontChannelLogoutUri = model.FrontChannelLogoutUri; entity.ClientUri = model.ClientUri; entity.LogoUri = model.LogoUri; @@ -100,6 +102,7 @@ public static class OpenIddictApplicationExtensions RedirectUris = entity.RedirectUris, Requirements = entity.Requirements, Settings = entity.Settings, + FrontChannelLogoutUri = entity.FrontChannelLogoutUri, ClientUri = entity.ClientUri, LogoUri = entity.LogoUri }; diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs index 48a376769f..6841b10a04 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Applications/OpenIddictApplicationModel.cs @@ -89,13 +89,18 @@ public class OpenIddictApplicationModel : ExtensibleObject /// public virtual string Settings { get; set; } + /// + /// Gets or sets the front-channel logout URI associated with the application. + /// + public virtual string FrontChannelLogoutUri { get; set; } + /// /// URI to further information about client. /// - public string ClientUri { get; set; } + public virtual string ClientUri { get; set; } /// /// URI to client logo. /// - public string LogoUri { get; set; } + public virtual string LogoUri { get; set; } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ar.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ar.json index 8146974400..bc1e004895 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ar.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ar.json @@ -16,8 +16,8 @@ "Permission:Edit": "تحرير", "Permission:Delete": "حذف", "Permission:ManageConnectionStrings": "Manage connection strings", - "Permission:ManageFeatures": "إدارة الميزات", + "Permission:ManageFeatures": "الميزات", "DisplayName:AdminEmailAddress": "عنوان البريد الإلكتروني للمسؤول", "DisplayName:AdminPassword": "كلمة مرور المسؤول" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json index 3902ad703b..a563181b61 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/cs.json @@ -16,7 +16,7 @@ "Permission:Edit": "Upravit", "Permission:Delete": "Smazat", "Permission:ManageConnectionStrings": "Spravovat connection stringy", - "Permission:ManageFeatures": "Spravovat funkce", + "Permission:ManageFeatures": "Funkce", "DisplayName:AdminEmailAddress": "Email adresa správce", "DisplayName:AdminPassword": "Heslo správce" } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/de.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/de.json index 0a62c49a4d..59fd237333 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/de.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/de.json @@ -16,8 +16,8 @@ "Permission:Edit": "Bearbeiten", "Permission:Delete": "Löschen", "Permission:ManageConnectionStrings": "Connection Strings verwalten", - "Permission:ManageFeatures": "Features verwalten", + "Permission:ManageFeatures": "Funktionen", "DisplayName:AdminEmailAddress": "Admin-E-Mail-Adresse", "DisplayName:AdminPassword": "Admin-Passwort" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/el.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/el.json index bc75c34e03..21fbed24bb 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/el.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/el.json @@ -15,7 +15,7 @@ "Permission:Edit": "Επεξεργασία", "Permission:Delete": "Διαγραφή", "Permission:ManageConnectionStrings": "Διαχείριση συμβολοσειρών σύνδεσης", - "Permission:ManageFeatures": "Διαχείριση λειτουργιών", + "Permission:ManageFeatures": "Λειτουργίες", "DisplayName:AdminEmailAddress": "Διεύθυνση email διαχειριστή", "DisplayName:AdminPassword": "Κωδικός διαχειριστή" } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en-GB.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en-GB.json index 8b4e4c185b..c53f2be460 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en-GB.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en-GB.json @@ -15,7 +15,7 @@ "Permission:Edit": "Edit", "Permission:Delete": "Delete", "Permission:ManageConnectionStrings": "Manage connection strings", - "Permission:ManageFeatures": "Manage features", + "Permission:ManageFeatures": "Features", "DisplayName:AdminEmailAddress": "Admin Email Address", "DisplayName:AdminPassword": "Admin Password" } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json index 0ba2b5c42f..1fc5b1985c 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/en.json @@ -16,8 +16,9 @@ "Permission:Edit": "Edit", "Permission:Delete": "Delete", "Permission:ManageConnectionStrings": "Manage connection strings", - "Permission:ManageFeatures": "Manage features", + "Permission:ManageFeatures": "Features", "DisplayName:AdminEmailAddress": "Admin Email Address", "DisplayName:AdminPassword": "Admin Password" } } + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/es.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/es.json index bcbfc98852..12ef0a4d31 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/es.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/es.json @@ -16,8 +16,8 @@ "Permission:Edit": "Editar", "Permission:Delete": "Borrar", "Permission:ManageConnectionStrings": "Gestión de cadenas de conexión", - "Permission:ManageFeatures": "Gestión de características", + "Permission:ManageFeatures": "Características", "DisplayName:AdminEmailAddress": "Dirección e-mail de administrador", "DisplayName:AdminPassword": "Contraseña de administrador" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fa.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fa.json index c67a9c070b..7fb481af5e 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fa.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fa.json @@ -15,7 +15,7 @@ "Permission:Edit": "ویرایش", "Permission:Delete": "حذف", "Permission:ManageConnectionStrings": "مدیریت کانکشن استرینگها", - "Permission:ManageFeatures": "مدیریت ویژگی ها", + "Permission:ManageFeatures": "ویژگی‌ها", "DisplayName:AdminEmailAddress": "آدرس ایمیل مدیر", "DisplayName:AdminPassword": "گذرواژه مدیریت" } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fi.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fi.json index 7721629201..4acd99fd0d 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fi.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fi.json @@ -16,8 +16,8 @@ "Permission:Edit": "Muokkaus", "Permission:Delete": "Poisto", "Permission:ManageConnectionStrings": "Hallitse tietokantayhteyksiä", - "Permission:ManageFeatures": "Hallitse ominaisuuksia", + "Permission:ManageFeatures": "Ominaisuudet", "DisplayName:AdminEmailAddress": "Järjestelmänvalvojan sähköpostiosoite", "DisplayName:AdminPassword": "Järjestelmänvalvojan salasana" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fr.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fr.json index 3537ca8ca6..705669c78d 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fr.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/fr.json @@ -16,8 +16,8 @@ "Permission:Edit": "Modifier", "Permission:Delete": "Supprimer", "Permission:ManageConnectionStrings": "Gérer les chaînes de connexion", - "Permission:ManageFeatures": "Gérer les fonctionnalités", + "Permission:ManageFeatures": "Fonctionnalités", "DisplayName:AdminEmailAddress": "Adresse de messagerie d’administrateur", "DisplayName:AdminPassword": "Mot de passe d’administrateur" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hi.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hi.json index 70367305a9..8072bdf6f9 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hi.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hi.json @@ -16,8 +16,8 @@ "Permission:Edit": "संपादित करें", "Permission:Delete": "हटाएं", "Permission:ManageConnectionStrings": "कनेक्शन स्ट्रिंग्स प्रबंधित करें", - "Permission:ManageFeatures": "सुविधाओं को प्रबंधित करें", + "Permission:ManageFeatures": "सुविधाएं", "DisplayName:AdminEmailAddress": "ईमेल पता", "DisplayName:AdminPassword": "व्यवस्थापक का पारण शब्द" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hr.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hr.json index 7ca876277f..c646a9a6c1 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hr.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hr.json @@ -16,8 +16,9 @@ "Permission:Edit": "Uredi", "Permission:Delete": "Izbrisati", "Permission:ManageConnectionStrings": "Upravljanje vezom na bazu podataka", - "Permission:ManageFeatures": "Upravljanje značajkama", + "Permission:ManageFeatures": "Značajke", "DisplayName:AdminEmailAddress": "Adresa e-pošte administratora", "DisplayName:AdminPassword": "Administratorska lozinka" } } + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hu.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hu.json index 26787bca90..19085d7a6b 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hu.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/hu.json @@ -16,8 +16,8 @@ "Permission:Edit": "Szerkesztés", "Permission:Delete": "Törlés", "Permission:ManageConnectionStrings": "Kapcsolati beállítások kezelése", - "Permission:ManageFeatures": "Funkciók kezelése", + "Permission:ManageFeatures": "Funkciók", "DisplayName:AdminEmailAddress": "Admin email cím", "DisplayName:AdminPassword": "Admin jelszó" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/is.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/is.json index 5c170f9a27..902383427c 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/is.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/is.json @@ -16,8 +16,8 @@ "Permission:Edit": "Breyta", "Permission:Delete": "Eyða", "Permission:ManageConnectionStrings": "Umsjá tengistrengja", - "Permission:ManageFeatures": "Umsjá eiginleika", + "Permission:ManageFeatures": "Eiginleikar", "DisplayName:AdminEmailAddress": "Netfang stjórnanda (admin)", "DisplayName:AdminPassword": "Lykilorð stjórnanda (admin)" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/it.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/it.json index 5d95e2039c..e8c0b866ae 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/it.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/it.json @@ -16,8 +16,8 @@ "Permission:Edit": "Modifica", "Permission:Delete": "Elimina", "Permission:ManageConnectionStrings": "Gestisci le stringhe di connessione", - "Permission:ManageFeatures": "Gestisci le funzionalità", + "Permission:ManageFeatures": "Funzionalità", "DisplayName:AdminEmailAddress": "Indirizzo e-mail dell'amministratore", "DisplayName:AdminPassword": "Password dell'amministratore" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/nl.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/nl.json index f739a3eb27..f72e56afe9 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/nl.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/nl.json @@ -16,8 +16,8 @@ "Permission:Edit": "Bewerk", "Permission:Delete": "Verwijder", "Permission:ManageConnectionStrings": "Beheer connection strings", - "Permission:ManageFeatures": "Beheer functies", + "Permission:ManageFeatures": "Functies", "DisplayName:AdminEmailAddress": "Admin e-mail adres", "DisplayName:AdminPassword": "Admin wachtwoord" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl-PL.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl-PL.json index 95a7b39e0e..46f1ca47bf 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl-PL.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pl-PL.json @@ -16,8 +16,8 @@ "Permission:Edit": "Edytuj", "Permission:Delete": "Usuń", "Permission:ManageConnectionStrings": "Zarządzaj connection string'ami", - "Permission:ManageFeatures": "Zarządzaj fukcjami", + "Permission:ManageFeatures": "Funkcje", "DisplayName:AdminEmailAddress": "Adres e-mail administratora", "DisplayName:AdminPassword": "Hasło administratora" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pt-BR.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pt-BR.json index d9f6b14a69..b621cc2069 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pt-BR.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/pt-BR.json @@ -16,8 +16,8 @@ "Permission:Edit": "Editar", "Permission:Delete": "Excluir", "Permission:ManageConnectionStrings": "Gerenciar conexões (connection strings)", - "Permission:ManageFeatures": "Gerenciar Funcionalidades", + "Permission:ManageFeatures": "Recursos", "DisplayName:AdminEmailAddress": "Endereço de e-mail do administrador", "DisplayName:AdminPassword": "Senha do administrador" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ro-RO.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ro-RO.json index cb14140ddb..8c7915614d 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ro-RO.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ro-RO.json @@ -16,8 +16,8 @@ "Permission:Edit": "Editează", "Permission:Delete": "Şterge", "Permission:ManageConnectionStrings": "Administrează stringurile de conexiune", - "Permission:ManageFeatures": "Administrarea caracteristicilor", + "Permission:ManageFeatures": "Caracteristici", "DisplayName:AdminEmailAddress": "Adresa de email admin", "DisplayName:AdminPassword": "Parola admin" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ru.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ru.json index 2204cf35cf..f89a39074b 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ru.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/ru.json @@ -16,8 +16,8 @@ "Permission:Edit": "Редактировать", "Permission:Delete": "Удалить", "Permission:ManageConnectionStrings": "Управление строками подключения", - "Permission:ManageFeatures": "Управление функциями", + "Permission:ManageFeatures": "Возможности", "DisplayName:AdminEmailAddress": "Адрес электронной почты администратора", "DisplayName:AdminPassword": "Пароль администратора" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sk.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sk.json index 1aff65e71c..a1479764ba 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sk.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sk.json @@ -16,8 +16,8 @@ "Permission:Edit": "Upraviť", "Permission:Delete": "Zmazať", "Permission:ManageConnectionStrings": "Správa connection stringov", - "Permission:ManageFeatures": "Správa funkcií", + "Permission:ManageFeatures": "Funkcie", "DisplayName:AdminEmailAddress": "Emailová adresa administrátora", "DisplayName:AdminPassword": "Heslo administrátora" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sl.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sl.json index a21cfa702a..b559583420 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sl.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sl.json @@ -16,8 +16,8 @@ "Permission:Edit": "Urejanje", "Permission:Delete": "Brisanje", "Permission:ManageConnectionStrings": "Upravljanje connection string-ov", - "Permission:ManageFeatures": "Upravljanje funkcionalnosti", + "Permission:ManageFeatures": "Lastnosti", "DisplayName:AdminEmailAddress": "Admin e-poštni naslov", "DisplayName:AdminPassword": "Skrbniško geslo" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sv.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sv.json index f112b05001..9cb8c11c89 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sv.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/sv.json @@ -16,8 +16,8 @@ "Permission:Edit": "Redigera", "Permission:Delete": "Radera", "Permission:ManageConnectionStrings": "Hantera anslutningssträngar", - "Permission:ManageFeatures": "Hantera funktioner", + "Permission:ManageFeatures": "Funktioner", "DisplayName:AdminEmailAddress": "Admin E-postadress", "DisplayName:AdminPassword": "Lösenord för administratör" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json index 667a65fb69..24c2292e74 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/tr.json @@ -16,8 +16,9 @@ "Permission:Edit": "Düzenleme", "Permission:Delete": "Silme", "Permission:ManageConnectionStrings": "Bağlantı cümlelerini yönet", - "Permission:ManageFeatures": "Özellikleri yönet", + "Permission:ManageFeatures": "Özellikler", "DisplayName:AdminEmailAddress": "Admin Eposta Adresi", "DisplayName:AdminPassword": "Admin Şifresi" } } + diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/vi.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/vi.json index 13f5afb095..2ca10f4f05 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/vi.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/vi.json @@ -16,8 +16,8 @@ "Permission:Edit": "Sửa", "Permission:Delete": "Xóa", "Permission:ManageConnectionStrings": "Quản lý chuỗi kết nối", - "Permission:ManageFeatures": "Quản lý các tính năng", + "Permission:ManageFeatures": "Tính năng", "DisplayName:AdminEmailAddress": "Địa chỉ Email Quản trị viên", "DisplayName:AdminPassword": "Mật khẩu quản trị" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hans.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hans.json index 8cff1a09b7..d8334301bf 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hans.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hans.json @@ -16,8 +16,8 @@ "Permission:Edit": "编辑", "Permission:Delete": "删除", "Permission:ManageConnectionStrings": "管理连接字符串", - "Permission:ManageFeatures": "管理功能", + "Permission:ManageFeatures": "功能", "DisplayName:AdminEmailAddress": "管理员电子邮件地址", "DisplayName:AdminPassword": "管理员密码" } -} \ No newline at end of file +} diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hant.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hant.json index 59519aff67..0da3743f2e 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hant.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Domain.Shared/Volo/Abp/TenantManagement/Localization/Resources/zh-Hant.json @@ -16,8 +16,9 @@ "Permission:Edit": "編輯", "Permission:Delete": "刪除", "Permission:ManageConnectionStrings": "管理資料庫連線字串", - "Permission:ManageFeatures": "管理功能", + "Permission:ManageFeatures": "功能", "DisplayName:AdminEmailAddress": "管理者信箱", "DisplayName:AdminPassword": "管理者密碼" } } + diff --git a/npm/ng-packs/migrations.json b/npm/ng-packs/migrations.json index e89bc86d78..49df20928b 100644 --- a/npm/ng-packs/migrations.json +++ b/npm/ng-packs/migrations.json @@ -1,19 +1,45 @@ { "migrations": [ { - "version": "20.3.0-beta.1", - "description": "Update ESLint flat config to include .cjs, .mjs, .cts, and .mts files in overrides (if needed)", - "implementation": "./src/migrations/update-20-3-0/add-file-extensions-to-overrides", - "package": "@nx/eslint", - "name": "add-file-extensions-to-overrides" + "cli": "nx", + "version": "21.2.0-beta.3", + "requires": { "@angular/core": ">=20.0.0" }, + "description": "Update the @angular/cli package version to ~20.0.0.", + "factory": "./src/migrations/update-21-2-0/update-angular-cli", + "package": "@nx/angular", + "name": "update-angular-cli-version-20-0-0" }, { - "cli": "nx", - "version": "20.3.0-beta.2", - "description": "If workspace includes Module Federation projects, ensure the new @nx/module-federation package is installed.", - "factory": "./src/migrations/update-20-3-0/ensure-nx-module-federation-package", + "version": "21.2.0-beta.3", + "requires": { "@angular/core": ">=20.0.0" }, + "description": "Migrate imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`.", + "factory": "./src/migrations/update-21-2-0/migrate-provide-server-rendering-import", + "package": "@nx/angular", + "name": "migrate-provide-server-rendering-import" + }, + { + "version": "21.2.0-beta.3", + "requires": { "@angular/core": ">=20.0.0" }, + "description": "Replace `provideServerRouting` and `provideServerRoutesConfig` with `provideServerRendering` using `withRoutes`.", + "factory": "./src/migrations/update-21-2-0/replace-provide-server-routing", + "package": "@nx/angular", + "name": "replace-provide-server-routing" + }, + { + "version": "21.2.0-beta.3", + "requires": { "@angular/core": ">=20.0.0" }, + "description": "Update the generator defaults to maintain the previous style guide behavior.", + "factory": "./src/migrations/update-21-2-0/set-generator-defaults-for-previous-style-guide", + "package": "@nx/angular", + "name": "set-generator-defaults-for-previous-style-guide" + }, + { + "version": "21.2.0-beta.3", + "requires": { "@angular/core": ">=20.0.0" }, + "description": "Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this here: https://www.typescriptlang.org/tsconfig/#moduleResolution.", + "factory": "./src/migrations/update-21-2-0/update-module-resolution", "package": "@nx/angular", - "name": "ensure-nx-module-federation-package" + "name": "update-module-resolution" } ] } diff --git a/npm/ng-packs/nx.json b/npm/ng-packs/nx.json index c4603659b0..0c52ac4bfe 100644 --- a/npm/ng-packs/nx.json +++ b/npm/ng-packs/nx.json @@ -20,7 +20,8 @@ "strict": false }, "@nx/angular:component": { - "style": "scss" + "style": "scss", + "type": "component" }, "@nx/angular": { "application": { @@ -31,7 +32,58 @@ }, "storybook-configuration": { "linter": "eslint" + }, + "directive": { + "type": "directive" + }, + "service": { + "type": "service" + }, + "scam": { + "type": "component" + }, + "scam-directive": { + "type": "directive" + }, + "guard": { + "typeSeparator": "." + }, + "interceptor": { + "typeSeparator": "." + }, + "module": { + "typeSeparator": "." + }, + "pipe": { + "typeSeparator": "." + }, + "resolver": { + "typeSeparator": "." } + }, + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." } }, "$schema": "./node_modules/nx/schemas/nx-schema.json", diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index 3332193c7f..9e218f2fab 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -46,74 +46,74 @@ "devDependencies": { "@abp/ng.theme.lepton-x": "~4.3.0-rc.2", "@abp/utils": "~9.3.0-rc.2", - "@angular-devkit/build-angular": "~19.1.0", - "@angular-devkit/core": "~19.1.0", - "@angular-devkit/schematics": "~19.1.0", - "@angular-devkit/schematics-cli": "~19.1.0", - "@angular-eslint/eslint-plugin": "~19.0.0", - "@angular-eslint/eslint-plugin-template": "~19.0.0", - "@angular-eslint/template-parser": "~19.0.0", - "@angular/animations": "~19.1.0", - "@angular/cli": "~19.1.0", - "@angular/common": "~19.1.0", - "@angular/compiler": "~19.1.0", - "@angular/compiler-cli": "~19.1.0", - "@angular/core": "~19.1.0", - "@angular/forms": "~19.1.0", - "@angular/language-service": "~19.1.0", - "@angular/localize": "~19.1.0", - "@angular/platform-browser": "~19.1.0", - "@angular/platform-browser-dynamic": "~19.1.0", - "@angular/router": "~19.1.0", + "@angular-devkit/build-angular": "~20.0.0", + "@angular-devkit/core": "~20.0.0", + "@angular-devkit/schematics": "~20.0.0", + "@angular-devkit/schematics-cli": "~20.0.0", + "@angular-eslint/eslint-plugin": "~20.0.0", + "@angular-eslint/eslint-plugin-template": "~20.0.0", + "@angular-eslint/template-parser": "~20.0.0", + "@angular/animations": "~20.0.0", + "@angular/cli": "~20.0.1", + "@angular/common": "~20.0.0", + "@angular/compiler": "~20.0.0", + "@angular/compiler-cli": "~20.0.0", + "@angular/core": "~20.0.0", + "@angular/forms": "~20.0.0", + "@angular/language-service": "~20.0.0", + "@angular/localize": "~20.0.0", + "@angular/platform-browser": "~20.0.0", + "@angular/platform-browser-dynamic": "~20.0.0", + "@angular/router": "~20.0.0", "@fortawesome/fontawesome-free": "^6.0.0", - "@ng-bootstrap/ng-bootstrap": "~18.0.0", - "@ngneat/spectator": "~19.1.0", + "@ng-bootstrap/ng-bootstrap": "~19.0.0", + "@ngneat/spectator": "~19.6.2", "@ngx-validate/core": "^0.2.0", - "@nx/angular": "~20.3.0", - "@nx/cypress": "~20.3.0", - "@nx/devkit": "~20.3.0", - "@nx/eslint": "~20.3.0", - "@nx/eslint-plugin": "~20.3.0", - "@nx/jest": "~20.3.0", - "@nx/js": "~20.3.0", - "@nx/plugin": "~20.3.0", - "@nx/workspace": "~20.3.0", + "@nx/angular": "~21.2.0", + "@nx/cypress": "~21.2.0", + "@nx/devkit": "~21.2.0", + "@nx/eslint": "~21.2.0", + "@nx/eslint-plugin": "~21.2.0", + "@nx/jest": "~21.2.0", + "@nx/js": "~21.2.0", + "@nx/plugin": "~21.2.0", + "@nx/workspace": "~21.2.0", "@popperjs/core": "~2.11.0", - "@schematics/angular": "~19.0.0", + "@schematics/angular": "~20.0.0", "@swc-node/register": "1.9.2", - "@swc/cli": "0.3.12", + "@swc/cli": "0.6.0", "@swc/core": "~1.5.0", "@swc/helpers": "~0.5.0", - "@swimlane/ngx-datatable": "^20.0.0", + "@swimlane/ngx-datatable": "~21.1.0", "@types/jest": "29.5.14", "@types/node": "^20.0.0", "@typescript-eslint/eslint-plugin": "7.16.0", "@typescript-eslint/parser": "7.16.0", "@typescript-eslint/utils": "^7.16.0", - "angular-oauth2-oidc": "^19.0.0", - "autoprefixer": "^10.0.0", + "angular-oauth2-oidc": "^20.0.0", + "autoprefixer": "^10.4.21", "bootstrap": "^5.0.0", "bootstrap-icons": "^1.0.0", "chart.js": "^4.0.0", "cypress": "^7.0.0", "dotenv": "10.0.0", "eslint": "^8.0.0", - "eslint-config-prettier": "9.0.0", + "eslint-config-prettier": "10.0.0", "eslint-plugin-cypress": "^2.10.3", "got": "^11.0.0", "jest": "^29.0.0", "jest-canvas-mock": "^2.0.0", "jest-environment-jsdom": "^29.0.0", - "jest-preset-angular": "14.4.2", + "jest-preset-angular": "14.6.0", "jsonc-eslint-parser": "^2.0.0", "jsonc-parser": "^2.0.0", "just-clone": "^6.0.0", "just-compare": "^2.0.0", "lerna": "^4.0.0", "lint-staged": "^13.0.0", - "ng-packagr": "~19.1.0", - "ng-zorro-antd": "~19.0.0", - "nx": "~20.3.0", + "ng-packagr": "~20.0.0", + "ng-zorro-antd": "~20.0.0", + "nx": "~21.2.0", "postcss": "^8.0.0", "postcss-import": "14.1.0", "postcss-preset-env": "7.5.0", @@ -127,7 +127,7 @@ "ts-toolbelt": "^9.0.0", "tslib": "^2.0.0", "tslint": "~6.1.0", - "typescript": "~5.6.0", + "typescript": "~5.8.0", "zone.js": "~0.15.0" }, "lint-staged": { diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/date-time-picker/extensible-date-time-picker.component.ts b/npm/ng-packs/packages/components/extensible/src/lib/components/date-time-picker/extensible-date-time-picker.component.ts index a9902c8bae..9f4455b1c2 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/date-time-picker/extensible-date-time-picker.component.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/date-time-picker/extensible-date-time-picker.component.ts @@ -3,7 +3,7 @@ import { ChangeDetectorRef, Component, inject, - Input, + input, Optional, SkipSelf, ViewChild, @@ -17,6 +17,7 @@ import { NgbTimeAdapter, NgbTimepicker, NgbTimepickerModule, + Placement, } from '@ng-bootstrap/ng-bootstrap'; import { NgxValidateCoreModule } from '@ngx-validate/core'; import { DateTimeAdapter } from '@abp/ng.theme.shared'; @@ -35,8 +36,8 @@ import { selfFactory } from '../../utils/factory.util'; selector: 'abp-extensible-date-time-picker', template: ` + [meridian]="meridian()" + /> `, changeDetection: ChangeDetectionStrategy.OnPush, viewProviders: [ @@ -72,8 +74,9 @@ import { selfFactory } from '../../utils/factory.util'; export class ExtensibleDateTimePickerComponent { public readonly cdRef = inject(ChangeDetectorRef); - @Input() prop!: FormProp; - @Input() meridian = false; + prop = input(); + meridian = input(false); + placement = input('bottom-left'); @ViewChild(NgbInputDatepicker) date!: NgbInputDatepicker; @ViewChild(NgbTimepicker) time!: NgbTimepicker; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html index 85eb36ffee..9555c58f4b 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html +++ b/npm/ng-packs/packages/components/extensible/src/lib/components/extensible-table/extensible-table.component.html @@ -64,17 +64,24 @@ [prop]="prop.name" [sortable]="prop.sortable" > - + @if (prop.tooltip) { {{ column.name }} } @else { - {{ column.name }} + + {{ column.name }} + } diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/actions.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/actions.ts index 57a8e26bf0..a8a093f8de 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/models/actions.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/actions.ts @@ -1,5 +1,5 @@ import { LinkedList } from '@abp/utils'; -import { InjectFlags, InjectionToken, InjectOptions, Type } from '@angular/core'; +import { InjectionToken, InjectOptions, Type } from '@angular/core'; export abstract class ActionList> extends LinkedList {} @@ -7,7 +7,7 @@ export abstract class ActionData { abstract getInjected: ( token: Type | InjectionToken, notFoundValue?: T, - flags?: InjectOptions | InjectFlags, + flags?: InjectOptions, ) => T; index?: number; abstract record: R; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/entity-props.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/entity-props.ts index 8964bd9090..20620ef1c6 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/models/entity-props.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/entity-props.ts @@ -1,5 +1,5 @@ import { ABP, escapeHtmlChars } from '@abp/ng.core'; -import { InjectFlags, InjectOptions, InjectionToken, Type } from '@angular/core'; +import { InjectOptions, InjectionToken, Type } from '@angular/core'; import { Observable, of } from 'rxjs'; import { ActionCallback } from './actions'; import { @@ -93,6 +93,6 @@ export type ColumnPredicate = (getInjected: GetInjected, auxData?: any) => boole export type GetInjected = ( token: Type | InjectionToken, notFoundValue?: T, - options?: InjectOptions | InjectFlags, + options?: InjectOptions, ) => T; type PropDataObject = { [key: string]: any }; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/models/props.ts b/npm/ng-packs/packages/components/extensible/src/lib/models/props.ts index 7b1e2b7949..4161eaaea7 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/models/props.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/models/props.ts @@ -1,5 +1,5 @@ import { LinkedList } from '@abp/utils'; -import { InjectFlags, InjectionToken, InjectOptions, Type } from '@angular/core'; +import { InjectionToken, InjectOptions, Type } from '@angular/core'; import { ePropType } from '../enums/props.enum'; import { FormPropTooltip } from './form-props'; @@ -9,7 +9,7 @@ export abstract class PropData { abstract getInjected: ( token: Type | InjectionToken, notFoundValue?: T, - options?: InjectOptions | InjectFlags, + options?: InjectOptions, ) => T; index?: number; abstract record: R; diff --git a/npm/ng-packs/packages/components/extensible/src/lib/pipes/create-injector.pipe.ts b/npm/ng-packs/packages/components/extensible/src/lib/pipes/create-injector.pipe.ts index 3536fa4dad..a61802283a 100644 --- a/npm/ng-packs/packages/components/extensible/src/lib/pipes/create-injector.pipe.ts +++ b/npm/ng-packs/packages/components/extensible/src/lib/pipes/create-injector.pipe.ts @@ -1,12 +1,4 @@ -import { - InjectFlags, - InjectionToken, - InjectOptions, - Injector, - Pipe, - PipeTransform, - Type, -} from '@angular/core'; +import { InjectionToken, InjectOptions, Injector, Pipe, PipeTransform, Type } from '@angular/core'; import { HasCreateInjectorPipe, ToolbarComponent } from '../models/toolbar-actions'; import { EXTENSIONS_ACTION_CALLBACK, EXTENSIONS_ACTION_DATA } from '../tokens/extensions.token'; @@ -22,7 +14,7 @@ export class CreateInjectorPipe implements PipeTransform { const get = ( token: Type | InjectionToken, notFoundValue?: T, - options?: InjectOptions | InjectFlags, + options?: InjectOptions, ) => { const componentData = context.getData(); const componentDataCallback = (data: any) => { diff --git a/npm/ng-packs/packages/components/package.json b/npm/ng-packs/packages/components/package.json index d64d5eb585..a5cb97e8ae 100644 --- a/npm/ng-packs/packages/components/package.json +++ b/npm/ng-packs/packages/components/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "chart.js": "^3.5.1", - "ng-zorro-antd": "~19.0.0", + "ng-zorro-antd": "~20.0.0", "@ctrl/tinycolor": "^4.0.0", "tslib": "^2.0.0" }, diff --git a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts index 8b6c8b432e..2cc66b3f04 100644 --- a/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts +++ b/npm/ng-packs/packages/core/src/lib/pipes/sort.pipe.ts @@ -6,7 +6,7 @@ export type SortOrder = 'asc' | 'desc'; name: 'abpSort', }) export class SortPipe implements PipeTransform { - transform(value: any[], sortOrder: SortOrder | string = 'asc', sortKey?: string): any { + transform(value: any[], sortOrder: SortOrder | string = 'asc', sortKey?: string | number): any { sortOrder = sortOrder && (sortOrder.toLowerCase() as any); if (!value || (sortOrder !== 'asc' && sortOrder !== 'desc')) return value; diff --git a/npm/ng-packs/packages/core/src/lib/services/list.service.ts b/npm/ng-packs/packages/core/src/lib/services/list.service.ts index 027624ef88..204833a66d 100644 --- a/npm/ng-packs/packages/core/src/lib/services/list.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/list.service.ts @@ -65,12 +65,12 @@ export class ListService implements return this._totalCount; } - private _sortKey = ''; - set sortKey(value: string) { + private _sortKey: string | number = ''; + set sortKey(value: string | number) { this._sortKey = value; this.get(); } - get sortKey(): string { + get sortKey(): string | number { return this._sortKey; } diff --git a/npm/ng-packs/packages/core/src/lib/tests/utils/common.utils.ts b/npm/ng-packs/packages/core/src/lib/tests/utils/common.utils.ts index 01612a987e..f3b17082b9 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/utils/common.utils.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/utils/common.utils.ts @@ -1,4 +1,4 @@ -import { AbstractType, InjectFlags, InjectionToken, Injector, Type } from '@angular/core'; +import { AbstractType, InjectionToken, Injector, Type } from '@angular/core'; import { Subject } from 'rxjs'; export const mockActions = new Subject(); @@ -10,10 +10,10 @@ export class DummyInjector extends Injector { get( token: Type | InjectionToken | AbstractType, notFoundValue?: T, - flags?: InjectFlags, + flags?: { optional: true; host: true; skipSelf: true; self: true }, ): T; get(token: any, notFoundValue?: any): any; - get(token, notFoundValue?, flags?: InjectFlags): any { + get(token, notFoundValue?, flags?: { optional: true; host: true; skipSelf: true }): any { return this.payload[token.name || token._desc || token]; } } diff --git a/npm/ng-packs/packages/oauth/package.json b/npm/ng-packs/packages/oauth/package.json index 8998eb8017..6bb9006dc6 100644 --- a/npm/ng-packs/packages/oauth/package.json +++ b/npm/ng-packs/packages/oauth/package.json @@ -9,7 +9,7 @@ "dependencies": { "@abp/ng.core": "~9.3.0-rc.2", "@abp/utils": "~9.3.0-rc.2", - "angular-oauth2-oidc": "^17.0.0", + "angular-oauth2-oidc": "^20.0.0", "just-clone": "^6.0.0", "just-compare": "^2.0.0", "tslib": "^2.0.0" diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index 5994593e20..39035f18de 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -4,16 +4,16 @@ "author": "", "schematics": "./collection.json", "dependencies": { - "@angular-devkit/core": "~19.1.0", - "@angular-devkit/schematics": "~19.1.0", - "@angular/cli": "~19.1.0", + "@angular-devkit/core": "~20.0.0", + "@angular-devkit/schematics": "~20.0.0", + "@angular/cli": "~20.0.0", "got": "^11.5.2", "jsonc-parser": "^2.3.0", "should-quote": "^1.0.0", - "typescript": "~5.6.0" + "typescript": "~5.8.0" }, "devDependencies": { - "@schematics/angular": "~19.1.0", + "@schematics/angular": "~20.0.0", "@types/jest": "29.4.4", "@types/node": "20.2.5", "jest": "29.4.3", diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index e485fd3be8..f10bd9edc6 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -8,11 +8,11 @@ }, "dependencies": { "@abp/ng.core": "~9.3.0-rc.2", - "@fortawesome/fontawesome-free": "^5.0.0", - "@ng-bootstrap/ng-bootstrap": "~18.0.0", + "@fortawesome/fontawesome-free": "^6.0.0", + "@ng-bootstrap/ng-bootstrap": "~19.0.0", "@ngx-validate/core": "^0.2.0", "@popperjs/core": "~2.11.0", - "@swimlane/ngx-datatable": "^20.0.0", + "@swimlane/ngx-datatable": "^21.0.0", "bootstrap": "^5.0.0", "tslib": "^2.0.0" }, diff --git a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts index 5715a4f40b..87ac4b9ce1 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/directives/ngx-datatable-list.directive.ts @@ -111,8 +111,9 @@ export class NgxDatatableListDirective implements OnChanges, OnInit, DoCheck { const spinnerRef = this.viewContainerRef.createComponent(SpinnerComponent); const spinnerElement = spinnerRef.location.nativeElement; - - this.renderer.insertBefore(parent, spinnerElement, placeholder); + if (placeholder?.parentNode === parent) { + this.renderer.insertBefore(parent, spinnerElement, placeholder); + } this.renderer.removeChild(parent, placeholder); } diff --git a/templates/app-nolayers/angular/angular.json b/templates/app-nolayers/angular/angular.json index 7a6c2413d3..bfdb10c374 100644 --- a/templates/app-nolayers/angular/angular.json +++ b/templates/app-nolayers/angular/angular.json @@ -186,5 +186,31 @@ } } } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } } } diff --git a/templates/app-nolayers/angular/package.json b/templates/app-nolayers/angular/package.json index eb09f6d9ed..dad6781521 100644 --- a/templates/app-nolayers/angular/package.json +++ b/templates/app-nolayers/angular/package.json @@ -21,15 +21,15 @@ "@abp/ng.tenant-management": "~9.3.0-rc.2", "@abp/ng.theme.lepton-x": "~4.3.0-rc.2", "@abp/ng.theme.shared": "~9.3.0-rc.2", - "@angular/animations": "~19.1.0", - "@angular/common": "~19.1.0", - "@angular/compiler": "~19.1.0", - "@angular/core": "~19.1.0", - "@angular/forms": "~19.1.0", - "@angular/localize": "~19.1.0", - "@angular/platform-browser": "~19.1.0", - "@angular/platform-browser-dynamic": "~19.1.0", - "@angular/router": "~19.1.0", + "@angular/animations": "~20.0.0", + "@angular/common": "~20.0.0", + "@angular/compiler": "~20.0.0", + "@angular/core": "~20.0.0", + "@angular/forms": "~20.0.0", + "@angular/localize": "~20.0.0", + "@angular/platform-browser": "~20.0.0", + "@angular/platform-browser-dynamic": "~20.0.0", + "@angular/router": "~20.0.0", "bootstrap-icons": "~1.8.0", "rxjs": "~7.8.0", "tslib": "^2.0.0", @@ -37,15 +37,15 @@ }, "devDependencies": { "@abp/ng.schematics": "~9.3.0-rc.2", - "@angular-devkit/build-angular": "~19.1.0", - "@angular-eslint/builder": "~19.0.0", - "@angular-eslint/eslint-plugin": "~19.0.0", - "@angular-eslint/eslint-plugin-template": "~19.0.0", - "@angular-eslint/schematics": "~19.0.0", - "@angular-eslint/template-parser": "~19.0.0", - "@angular/cli": "~19.1.0", - "@angular/compiler-cli": "~19.1.0", - "@angular/language-service": "~19.1.0", + "@angular-devkit/build-angular": "~20.0.0", + "@angular-eslint/builder": "~20.0.0", + "@angular-eslint/eslint-plugin": "~20.0.0", + "@angular-eslint/eslint-plugin-template": "~20.0.0", + "@angular-eslint/schematics": "~20.0.0", + "@angular-eslint/template-parser": "~20.0.0", + "@angular/cli": "~20.0.0", + "@angular/compiler-cli": "~20.0.0", + "@angular/language-service": "~20.0.0", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "7.16.0", @@ -57,6 +57,6 @@ "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.7.0", - "typescript": "~5.6.0" + "typescript": "~5.8.0" } } diff --git a/templates/app-nolayers/angular/src/app/app.config.ts b/templates/app-nolayers/angular/src/app/app.config.ts index 5fcd465589..450a31aab8 100644 --- a/templates/app-nolayers/angular/src/app/app.config.ts +++ b/templates/app-nolayers/angular/src/app/app.config.ts @@ -1,6 +1,6 @@ -import {ApplicationConfig, importProvidersFrom} from '@angular/core'; -import {provideAnimations} from "@angular/platform-browser/animations"; -import {provideRouter} from "@angular/router"; +import { ApplicationConfig, importProvidersFrom } from '@angular/core'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { provideRouter } from '@angular/router'; import { appRoutes } from './app.routes'; import { APP_ROUTE_PROVIDER } from './route.provider'; import { provideAbpCore, withOptions } from '@abp/ng.core'; @@ -13,8 +13,9 @@ import { provideAccountConfig } from '@abp/ng.account/config'; import { provideIdentityConfig } from '@abp/ng.identity/config'; import { provideTenantManagementConfig } from '@abp/ng.tenant-management/config'; import { provideFeatureManagementConfig } from '@abp/ng.feature-management'; -import {ThemeLeptonXModule} from "@abp/ng.theme.lepton-x"; -import {SideMenuLayoutModule} from "@abp/ng.theme.lepton-x/layouts"; +import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x'; +import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts'; +import { provideLogo, withEnvironmentOptions } from '@volo/ngx-lepton-x.core'; export const appConfig: ApplicationConfig = { providers: [ @@ -34,9 +35,7 @@ export const appConfig: ApplicationConfig = { provideTenantManagementConfig(), provideFeatureManagementConfig(), provideAnimations(), - importProvidersFrom([ - ThemeLeptonXModule.forRoot(), - SideMenuLayoutModule.forRoot(), - ]) + provideLogo(withEnvironmentOptions(environment)), + importProvidersFrom([ThemeLeptonXModule.forRoot(), SideMenuLayoutModule.forRoot()]), ], }; diff --git a/templates/app-nolayers/angular/tsconfig.json b/templates/app-nolayers/angular/tsconfig.json index 9395506033..29df0877f5 100644 --- a/templates/app-nolayers/angular/tsconfig.json +++ b/templates/app-nolayers/angular/tsconfig.json @@ -8,10 +8,11 @@ "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "importHelpers": true, "target": "ES2022", "module": "es2020", + "skipLibCheck": true, "lib": [ "es2018", "dom" diff --git a/templates/app/angular/angular.json b/templates/app/angular/angular.json index 7a6c2413d3..bfdb10c374 100644 --- a/templates/app/angular/angular.json +++ b/templates/app/angular/angular.json @@ -186,5 +186,31 @@ } } } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } } } diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index eb09f6d9ed..dad6781521 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -21,15 +21,15 @@ "@abp/ng.tenant-management": "~9.3.0-rc.2", "@abp/ng.theme.lepton-x": "~4.3.0-rc.2", "@abp/ng.theme.shared": "~9.3.0-rc.2", - "@angular/animations": "~19.1.0", - "@angular/common": "~19.1.0", - "@angular/compiler": "~19.1.0", - "@angular/core": "~19.1.0", - "@angular/forms": "~19.1.0", - "@angular/localize": "~19.1.0", - "@angular/platform-browser": "~19.1.0", - "@angular/platform-browser-dynamic": "~19.1.0", - "@angular/router": "~19.1.0", + "@angular/animations": "~20.0.0", + "@angular/common": "~20.0.0", + "@angular/compiler": "~20.0.0", + "@angular/core": "~20.0.0", + "@angular/forms": "~20.0.0", + "@angular/localize": "~20.0.0", + "@angular/platform-browser": "~20.0.0", + "@angular/platform-browser-dynamic": "~20.0.0", + "@angular/router": "~20.0.0", "bootstrap-icons": "~1.8.0", "rxjs": "~7.8.0", "tslib": "^2.0.0", @@ -37,15 +37,15 @@ }, "devDependencies": { "@abp/ng.schematics": "~9.3.0-rc.2", - "@angular-devkit/build-angular": "~19.1.0", - "@angular-eslint/builder": "~19.0.0", - "@angular-eslint/eslint-plugin": "~19.0.0", - "@angular-eslint/eslint-plugin-template": "~19.0.0", - "@angular-eslint/schematics": "~19.0.0", - "@angular-eslint/template-parser": "~19.0.0", - "@angular/cli": "~19.1.0", - "@angular/compiler-cli": "~19.1.0", - "@angular/language-service": "~19.1.0", + "@angular-devkit/build-angular": "~20.0.0", + "@angular-eslint/builder": "~20.0.0", + "@angular-eslint/eslint-plugin": "~20.0.0", + "@angular-eslint/eslint-plugin-template": "~20.0.0", + "@angular-eslint/schematics": "~20.0.0", + "@angular-eslint/template-parser": "~20.0.0", + "@angular/cli": "~20.0.0", + "@angular/compiler-cli": "~20.0.0", + "@angular/language-service": "~20.0.0", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "7.16.0", @@ -57,6 +57,6 @@ "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.7.0", - "typescript": "~5.6.0" + "typescript": "~5.8.0" } } diff --git a/templates/app/angular/src/app/app.config.ts b/templates/app/angular/src/app/app.config.ts index 0d7e64c80c..4dc47f7f7c 100644 --- a/templates/app/angular/src/app/app.config.ts +++ b/templates/app/angular/src/app/app.config.ts @@ -1,6 +1,6 @@ -import {ApplicationConfig, importProvidersFrom} from '@angular/core'; +import { ApplicationConfig, importProvidersFrom } from '@angular/core'; import { provideRouter } from '@angular/router'; -import {provideAnimations} from "@angular/platform-browser/animations"; +import { provideAnimations } from '@angular/platform-browser/animations'; import { appRoutes } from './app.routes'; import { APP_ROUTE_PROVIDER } from './route.provider'; @@ -14,9 +14,10 @@ import { provideAccountConfig } from '@abp/ng.account/config'; import { provideIdentityConfig } from '@abp/ng.identity/config'; import { provideTenantManagementConfig } from '@abp/ng.tenant-management/config'; import { provideFeatureManagementConfig } from '@abp/ng.feature-management'; -import {ThemeLeptonXModule} from "@abp/ng.theme.lepton-x"; -import {SideMenuLayoutModule} from "@abp/ng.theme.lepton-x/layouts"; -import {AccountLayoutModule} from "@abp/ng.theme.lepton-x/account"; +import { ThemeLeptonXModule } from '@abp/ng.theme.lepton-x'; +import { SideMenuLayoutModule } from '@abp/ng.theme.lepton-x/layouts'; +import { AccountLayoutModule } from '@abp/ng.theme.lepton-x/account'; +import { provideLogo, withEnvironmentOptions } from '@volo/ngx-lepton-x.core'; export const appConfig: ApplicationConfig = { providers: [ @@ -37,10 +38,11 @@ export const appConfig: ApplicationConfig = { provideFeatureManagementConfig(), provideAnimations(), provideAbpCore(), + provideLogo(withEnvironmentOptions(environment)), importProvidersFrom([ ThemeLeptonXModule.forRoot(), SideMenuLayoutModule.forRoot(), AccountLayoutModule.forRoot(), - ]) + ]), ], }; diff --git a/templates/app/angular/tsconfig.json b/templates/app/angular/tsconfig.json index 9395506033..29df0877f5 100644 --- a/templates/app/angular/tsconfig.json +++ b/templates/app/angular/tsconfig.json @@ -8,10 +8,11 @@ "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "importHelpers": true, "target": "ES2022", "module": "es2020", + "skipLibCheck": true, "lib": [ "es2018", "dom" diff --git a/templates/module/angular/angular.json b/templates/module/angular/angular.json index 9547009278..b6145d12f1 100644 --- a/templates/module/angular/angular.json +++ b/templates/module/angular/angular.json @@ -188,5 +188,31 @@ } } } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } } } diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 5a0680d0dc..90cd54f25c 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -22,30 +22,30 @@ "@abp/ng.tenant-management": "~9.3.0-rc.2", "@abp/ng.theme.basic": "~9.3.0-rc.2", "@abp/ng.theme.shared": "~9.3.0-rc.2", - "@angular/animations": "~19.1.0", - "@angular/common": "~19.1.0", - "@angular/compiler": "~19.1.0", - "@angular/core": "~19.1.0", - "@angular/forms": "~19.1.0", - "@angular/localize": "~19.1.0", - "@angular/platform-browser": "~19.1.0", - "@angular/platform-browser-dynamic": "~19.1.0", - "@angular/router": "~19.1.0", + "@angular/animations": "~20.0.0", + "@angular/common": "~20.0.0", + "@angular/compiler": "~20.0.0", + "@angular/core": "~20.0.0", + "@angular/forms": "~20.0.0", + "@angular/localize": "~20.0.0", + "@angular/platform-browser": "~20.0.0", + "@angular/platform-browser-dynamic": "~20.0.0", + "@angular/router": "~20.0.0", "rxjs": "~7.8.0", "tslib": "^2.0.0", "zone.js": "~0.15.0" }, "devDependencies": { "@abp/ng.schematics": "~9.3.0-rc.2", - "@angular-devkit/build-angular": "~19.1.0", - "@angular-eslint/builder": "~19.0.0", - "@angular-eslint/eslint-plugin": "~19.0.0", - "@angular-eslint/eslint-plugin-template": "~19.0.0", - "@angular-eslint/schematics": "~19.0.0", - "@angular-eslint/template-parser": "~19.0.0", - "@angular/cli": "~19.1.0", - "@angular/compiler-cli": "~19.1.0", - "@angular/language-service": "~19.1.0", + "@angular-devkit/build-angular": "~20.0.0", + "@angular-eslint/builder": "~20.0.0", + "@angular-eslint/eslint-plugin": "~20.0.0", + "@angular-eslint/eslint-plugin-template": "~20.0.0", + "@angular-eslint/schematics": "~20.0.0", + "@angular-eslint/template-parser": "~20.0.0", + "@angular/cli": "~20.0.0", + "@angular/compiler-cli": "~20.0.0", + "@angular/language-service": "~20.0.0", "@types/jasmine": "~3.6.0", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "7.16.0", @@ -57,8 +57,8 @@ "karma-coverage": "~2.1.0", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.7.0", - "ng-packagr": "~19.1.0", + "ng-packagr": "~20.0.0", "symlink": "^2.0.0", - "typescript": "~5.6.0" + "typescript": "~5.8.0" } } diff --git a/templates/module/angular/tsconfig.json b/templates/module/angular/tsconfig.json index 5879bfbc59..92d2f24875 100644 --- a/templates/module/angular/tsconfig.json +++ b/templates/module/angular/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.prod.json", "compilerOptions": { + "skipLibCheck": true, "paths": { "@my-company-name/my-project-name": [ "projects/my-project-name/src/public-api.ts"