diff --git a/.github/workflows/auto-pr.yml b/.github/workflows/auto-pr.yml index f353a21e37..526cfc144b 100644 --- a/.github/workflows/auto-pr.yml +++ b/.github/workflows/auto-pr.yml @@ -1,13 +1,13 @@ -name: Merge branch rel-8.3 with rel-8.2 +name: Merge branch dev with rel-8.3 on: push: branches: - - rel-8.2 + - rel-8.3 permissions: contents: read jobs: - merge-rel-8-3-with-rel-8-2: + merge-dev-with-rel-8-3: permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR @@ -15,22 +15,22 @@ jobs: steps: - uses: actions/checkout@v2 with: - ref: rel-8.3 + ref: dev - name: Reset promotion branch run: | - git fetch origin rel-8.2:rel-8.2 - git reset --hard rel-8.2 + git fetch origin rel-8.3:rel-8.3 + git reset --hard rel-8.3 - name: Create Pull Request uses: peter-evans/create-pull-request@v3 with: - branch: auto-merge/rel-8-2/${{github.run_number}} - title: Merge branch rel-8.3 with rel-8.2 - body: This PR generated automatically to merge rel-8.3 with rel-8.2. Please review the changed files before merging to prevent any errors that may occur. + branch: auto-merge/rel-8-3/${{github.run_number}} + title: Merge branch dev with rel-8.3 + body: This PR generated automatically to merge dev with rel-8.3. Please review the changed files before merging to prevent any errors that may occur. reviewers: maliming token: ${{ github.token }} - name: Merge Pull Request env: GH_TOKEN: ${{ secrets.BOT_SECRET }} run: | - gh pr review auto-merge/rel-8-2/${{github.run_number}} --approve - gh pr merge auto-merge/rel-8-2/${{github.run_number}} --merge --auto --delete-branch + gh pr review auto-merge/rel-8-3/${{github.run_number}} --approve + gh pr merge auto-merge/rel-8-3/${{github.run_number}} --merge --auto --delete-branch \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 472fb034a5..854123b215 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -48,8 +48,8 @@ - - + + @@ -61,7 +61,6 @@ - @@ -164,7 +163,7 @@ - + diff --git a/LICENSE.md b/LICENSE.md index 65c5ca88a6..5da3c16f4c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,165 +1,89 @@ GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public. +The license is supplemented by the additional permissions listed below. - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - 0. Additional Definitions. - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. + **0. Additional Definitions.** + +As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. + +"The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided by the Library but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. + + + + **1. Exception to Section 3 of the GNU GPL.** + +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. + + + + **2. Conveying Modified Versions.** + +If you modify a copy of the Library and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + **a)** under this License, provided that you make a good-faith effort to ensure that, in the event an Application does not supply the + function or data, the facility still operates and performs whatever part of its purpose remains meaningful, or + + **b)** under the GNU GPL, with none of the additional permissions of this License is applicable to that copy. + + + + **3. Object Code Incorporating Material from Library Header Files.** + +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such an object code under terms of your choice, provided that if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: + + **a)** Give prominent notice with each copy of the object code that the Library is used in it, and the Library and its use are covered by this License. + + **b)** Accompany the object code with a copy of the GNU GPL and this license document. + + + + **4. Combined Works.** + +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + **a)** Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + **b)** Accompany the Combined Work with a copy of the GNU GPL and this license document. + + **c)** For a Combined Work that displays copyright notices during execution, including the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. + + **d)** Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this License and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + **e)** Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying the Corresponding Source.) + - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) 5. Combined Libraries. - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. +You may place library facilities that are work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License and convey such a combined library under the terms of your choice if you do both of the following: + + **a)** Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + **b)** Give prominent notice with the combined library that part of it is a work based on the Library and explains where to find the accompanying uncombined form of the same work. + + + + **6. Revised Versions of the GNU Lesser General Public License.** + +The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received, it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. diff --git a/README.md b/README.md index ccc31da4b5..dd953a6431 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,18 @@ # ABP Framework -![build and test](https://img.shields.io/github/actions/workflow/status/abpframework/abp/build-and-test.yml?branch=dev&style=flat-square) -[![codecov](https://codecov.io/gh/abpframework/abp/branch/dev/graph/badge.svg?token=jUKLCxa6HF)](https://codecov.io/gh/abpframework/abp) -[![NuGet](https://img.shields.io/nuget/v/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) -[![NuGet (with prereleases)](https://img.shields.io/nuget/vpre/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) -[![MyGet (nightly builds)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds) -[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) -[![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](https://github.com/abpframework/abp/blob/dev/CODE_OF_CONDUCT.md) -[![CLA Signed](https://cla-assistant.io/readme/badge/abpframework/abp)](https://cla-assistant.io/abpframework/abp) -[![ABP Discord server](https://img.shields.io/discord/951497912645476422?label=Discord)](https://discord.gg/abp) - -ABP Framework is a complete **infrastructure** based on **ASP.NET Core** to create **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**. Check out https://abp.io +![build and test](https://img.shields.io/github/actions/workflow/status/abpframework/abp/build-and-test.yml?branch=dev&style=flat-square) 🔹 [![codecov](https://codecov.io/gh/abpframework/abp/branch/dev/graph/badge.svg?token=jUKLCxa6HF)](https://codecov.io/gh/abpframework/abp) 🔹 [![NuGet](https://img.shields.io/nuget/v/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) 🔹 [![NuGet (with prereleases)](https://img.shields.io/nuget/vpre/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) 🔹 [![MyGet (nightly builds)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds) 🔹 +[![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) 🔹 [![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](https://github.com/abpframework/abp/blob/dev/CODE_OF_CONDUCT.md) 🔹 [![CLA Signed](https://cla-assistant.io/readme/badge/abpframework/abp)](https://cla-assistant.io/abpframework/abp) 🔹 [![Discord Shield](https://discord.com/api/guilds/951497912645476422/widget.png?style=shield)](https://discord.gg/abp) + +ABP Framework is a complete **infrastructure** based on **ASP.NET Core** that creates **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**. + +[![ABP Platform](https://github.com/abpframework/abp/assets/9526587/47531496-4088-406d-9c69-63cb0ffec2ba)](https://abp.io) + ## Getting Started -- [Quick Start](https://docs.abp.io/en/abp/latest/Tutorials/Todo/Index) is a single-part, quick-start tutorial to build a simple application with the ABP Framework. Start with this tutorial if you want to quickly understand how ABP works. -- [Getting Started guide](https://docs.abp.io/en/abp/latest/Getting-Started) can be used to create and run ABP based solutions with different options and details. -- [Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1) is a complete tutorial to develop a full stack web application with all aspects of a real-life solution. +- [Quick Start](https://docs.abp.io/en/abp/latest/Tutorials/Todo/Index) is a single-part, quick-start tutorial to build a simple application with the ABP Framework. Start with this tutorial if you want to understand how ABP works quickly. +- [Getting Started guide](https://docs.abp.io/en/abp/latest/Getting-Started) can be used to create and run ABP-based solutions with different options and details. +- [Web Application Development Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1) is a complete tutorial on developing a full-stack web application with all aspects of a real-life solution. ### Quick Start @@ -34,28 +30,26 @@ Create a new solution: > See the [CLI documentation](https://docs.abp.io/en/abp/latest/CLI) for all available options. + + ### UI Framework Options + + ### Database Provider Options -## The Book: Mastering ABP Framework -Written by the creator of ABP Framework, this book will help you to gain a complete understanding of the ABP Framework and modern web application development techniques. - -* [Buy on Amazon](https://www.amazon.com/gp/product/B097Z2DM8Q) -* [Buy on Packt Publishing](https://www.packtpub.com/product/mastering-abp-framework/9781801079242) -* [More details about the book](https://abp.io/books/mastering-abp-framework) - -![book-mastering-abp-framework](docs/en/images/book-mastering-abp-framework.png) ## What ABP Provides? ABP provides a **full stack developer experience**. + + ### Architecture @@ -64,30 +58,48 @@ ABP offers a complete, **modular** and **layered** software architecture based o ABP Framework is suitable for **[microservice solutions](https://docs.abp.io/en/abp/latest/Microservice-Architecture)** as well as monolithic applications. + + ### Infrastructure -There are a lot of features provided by the ABP Framework to achieve real world scenarios easier, like [Event Bus](https://docs.abp.io/en/abp/latest/Event-Bus), [Background Job System](https://docs.abp.io/en/abp/latest/Background-Jobs), [Audit Logging](https://docs.abp.io/en/abp/latest/Audit-Logging), [BLOB Storing](https://docs.abp.io/en/abp/latest/Blob-Storing), [Data Seeding](https://docs.abp.io/en/abp/latest/Data-Seeding), [Data Filtering](https://docs.abp.io/en/abp/latest/Data-Filtering), etc. +There are a lot of features provided by the ABP Framework to achieve real-world scenarios easier, like [Event Bus](https://docs.abp.io/en/abp/latest/Event-Bus), [Background Job System](https://docs.abp.io/en/abp/latest/Background-Jobs), [Audit Logging](https://docs.abp.io/en/abp/latest/Audit-Logging), [BLOB Storing](https://docs.abp.io/en/abp/latest/Blob-Storing), [Data Seeding](https://docs.abp.io/en/abp/latest/Data-Seeding), [Data Filtering](https://docs.abp.io/en/abp/latest/Data-Filtering), etc. + + + +### Cross-Cutting Concerns + +ABP also simplifies (and even automates wherever possible) cross-cutting concerns and common non-functional requirements like [Exception Handling](https://docs.abp.io/en/abp/latest/Exception-Handling), [Validation](https://docs.abp.io/en/abp/latest/Validation), [Authorization](https://docs.abp.io/en/abp/latest/Authorization), [Localization](https://docs.abp.io/en/abp/latest/Localization), [Caching](https://docs.abp.io/en/abp/latest/Caching), [Dependency Injection](https://docs.abp.io/en/abp/latest/Dependency-Injection), [Setting Management](https://docs.abp.io/en/abp/latest/Settings), etc. -### Cross Cutting Concerns -ABP also simplifies (and even automates wherever possible) cross cutting concerns and common non-functional requirements like [Exception Handling](https://docs.abp.io/en/abp/latest/Exception-Handling), [Validation](https://docs.abp.io/en/abp/latest/Validation), [Authorization](https://docs.abp.io/en/abp/latest/Authorization), [Localization](https://docs.abp.io/en/abp/latest/Localization), [Caching](https://docs.abp.io/en/abp/latest/Caching), [Dependency Injection](https://docs.abp.io/en/abp/latest/Dependency-Injection), [Setting Management](https://docs.abp.io/en/abp/latest/Settings), etc. ### Application Modules ABP is a modular framework and the Application Modules provide **pre-built application functionalities**; - [**Account**](https://docs.abp.io/en/abp/latest/Modules/Account): Provides UI for the account management and allows user to login/register to the application. -- **[Identity](https://docs.abp.io/en/abp/latest/Modules/Identity)**: Manages organization units, roles, users and their permissions, based on the Microsoft Identity library. +- **[Identity](https://docs.abp.io/en/abp/latest/Modules/Identity)**: Manages organization units, roles, users and their permissions based on the Microsoft Identity library. - [**OpenIddict**](https://docs.abp.io/en/abp/latest/Modules/OpenIddict): Integrates to OpenIddict. - [**Tenant Management**](https://docs.abp.io/en/abp/latest/Modules/Tenant-Management): Manages tenants for a [multi-tenant](https://docs.abp.io/en/abp/latest/Multi-Tenancy) (SaaS) application. See the [Application Modules](https://docs.abp.io/en/abp/latest/Modules/Index) document for all pre-built modules. + + ### Startup Templates The [Startup templates](https://docs.abp.io/en/abp/latest/Startup-Templates/Index) are pre-built Visual Studio solution templates. You can create your own solution based on these templates to **immediately start your development**. -## ABP Community + + +## Mastering ABP Framework Book + +This book will help you to gain a complete understanding of the ABP Framework and modern web application development techniques. It is written by the creator and team lead of the ABP Framework. You can buy from [Amazon](https://www.amazon.com/gp/product/B097Z2DM8Q) or [Packt Publishing](https://www.packtpub.com/product/mastering-abp-framework/9781801079242). Find further info about the book at [abp.io/books/mastering-abp-framework](https://abp.io/books/mastering-abp-framework). + +![book-mastering-abp-framework](docs/en/images/book-mastering-abp-framework.png) + + + +## The Community ### ABP Community Web Site @@ -103,28 +115,31 @@ See the [sample projects](https://docs.abp.io/en/abp/latest/Samples/Index) built ### Want to Contribute? -ABP is a community-driven open source project. See [the contribution guide](https://docs.abp.io/en/abp/latest/Contribution/Index) if you want to be a part of this project. +ABP is a community-driven open-source project. See [the contribution guide](https://docs.abp.io/en/abp/latest/Contribution/Index) if you want to participate in this project. + + ## Official Links -* Main Web Site - * Get Started - * Features -* Documentation -* Samples -* Blog -* Community -* Stack overflow -* Twitter +* [Home Website](https://abp.io) + * [Get Started](https://abp.io/get-started) + * [Features](https://abp.io/features) +* [Documents](https://docs.abp.io/) +* [Samples](https://docs.abp.io/en/abp/latest/Samples/Index) +* [Blog](https://blog.abp.io/) +* [Community](https://community.abp.io/) +* [Stackoverflow](https://stackoverflow.com/questions/tagged/abp) +* [Twitter](https://twitter.com/abpframework) + + + +## Support ABP -## Support the ABP Framework +GitHub repository stars are an important indicator of popularity and the size of the community. If you like ABP Framework, support us by clicking the star :star: on the repository. -Love ABP Framework? **Please give a star** to this repository :star: -## Discord Channel -You can use this link to join the ABP Community Discord Server: https://discord.gg/abp +## Discord Server -## ABP Commercial +We have a Discord server where you can chat with other ABP users. Share your ideas, report technical issues, showcase your creations, share the tips that worked for you and catch up with the latest news and announcements about ABP Framework. Join 👉 https://discord.gg/abp. -See also [ABP Commercial](https://commercial.abp.io/) if you are looking for pre-built application modules, professional themes, code generation tooling and premium support for the ABP Framework. diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json index 8470709c99..44ab5b3e31 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Account/Localization/Resources/en.json @@ -12,6 +12,7 @@ "CommunityWebSite": "ABP community website", "ManageAccount": "My Account | ABP.IO", "ManageYourProfile": "Manage your profile", - "ReturnToApplication": "Return to application" + "ReturnToApplication": "Return to application", + "IdentityUserNotAvailable:Deleted": "This email address is not available. Reason: Already deleted." } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json index 5be67a44b1..b8a92f19a4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ar.json @@ -816,6 +816,7 @@ "ErrorExceptionMessage": "حدث خطأ أثناء معالجة طلبك", "WatchTakeCodeGeneration": "شاهد فيديو \"استكشاف إمكانات إنشاء الأكواد البرمجية: ABP Suite\"!", "ExtendNow": "تمديد / تجديد", - "RegisterDemo": "يسجل" + "RegisterDemo": "يسجل", + "AbpStudio_ComingSoon": "إذا كنت ترغب في اختبار ABP Studio قبل أي شخص آخر، يمكنك أن تصبح أحد مختبري النسخة التجريبية" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json index c047dd7b65..e94c4108aa 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/cs.json @@ -431,6 +431,7 @@ "ErrorExceptionMessage": "Při zpracování vašeho požadavku nastala chyba", "WatchTakeCodeGeneration": "Podívejte se na video „Prozkoumejte potenciál generování kódu: ABP Suite“!", "ExtendNow": "Prodloužit / Obnovit", - "RegisterDemo": "Registrovat" + "RegisterDemo": "Registrovat", + "AbpStudio_ComingSoon": "Pokud chcete ABP Studio otestovat dříve než kdokoli jiný, staňte se BETA testerem" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json index 1663811cef..5d944a25c7 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/de.json @@ -411,6 +411,7 @@ "SendUsEmail": "Senden Sie uns eine E-Mail", "ErrorExceptionMessage": "Während Ihrer Anfrage ist ein Fehler aufgetreten", "WatchTakeCodeGeneration": "Sehen Sie sich das Video „Entdecken Sie das Potenzial der Codegenerierung: ABP Suite“ an!", - "ExtendNow": "Verlängern / Erneuern" + "ExtendNow": "Verlängern / Erneuern", + "AbpStudio_ComingSoon": "Wenn Sie ABP Studio vor allen anderen testen möchten, werden Sie BETA-Tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json index 3fc2d697ee..071f025a81 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/en.json @@ -916,7 +916,7 @@ "AbpStudio_Description": "ABP Studio is still under development. You can fill out the form below to be one of the first users.", "AbpStudio_Description1": "ABP Studio is a cross-platform desktop application for ABP developers.", "AbpStudio_Description2": "It is well integrated to the ABP Framework and aims to provide a comfortable development environment for you by automating things, providing insights about your solution, making develop, run and deploy your solutions much easier.", - "AbpStudio_ComingSoon": "Coming Soon Planned beta release date: Q4 of 2023.", + "AbpStudio_ComingSoon": "If you want to test ABP Studio before anyone else, become a BETA tester", "AbpStudio_PlannedPreviewDate": "Planned preview release date: Q4 of 2023.", "BetaRequest": "Beta Request", "CreateNewSolutions": "Create New Solutions", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json index 3f54749131..67fa883440 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/es.json @@ -411,6 +411,7 @@ "SendUsEmail": "Envíanos un correo electrónico", "ErrorExceptionMessage": "Se produjo un error al procesar su solicitud.", "WatchTakeCodeGeneration": "¡Mire el vídeo \"Explore el potencial de la generación de código: ABP Suite\"!", - "ExtendNow": "Ampliar / Renovar" + "ExtendNow": "Ampliar / Renovar", + "AbpStudio_ComingSoon": "Si quieres probar ABP Studio antes que nadie, conviértete en tester BETA" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json index f367f63c30..57d76027b2 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fi.json @@ -852,6 +852,7 @@ "ErrorExceptionMessage": "Virhe pyynnön käsittelyn yhteydessä", "WatchTakeCodeGeneration": "Katso \"Tutki koodin luomisen potentiaalia: ABP Suite\" -video!", "ExtendNow": "Laajenna / Uusi", - "RegisterDemo": "Rekisteröidy" + "RegisterDemo": "Rekisteröidy", + "AbpStudio_ComingSoon": "Jos haluat testata ABP Studiota ennen muita, ryhdy BETA-testaajaksi" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json index b044837aac..7bc6e12e95 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/fr.json @@ -431,6 +431,7 @@ "ErrorExceptionMessage": "Une erreur s'est produite lors du traitement de votre demande", "WatchTakeCodeGeneration": "Regardez la vidéo « Explorez le potentiel de la génération de code : ABP Suite » !", "ExtendNow": "Prolonger / Renouveler", - "RegisterDemo": "Registre" + "RegisterDemo": "Registre", + "AbpStudio_ComingSoon": "Si vous souhaitez tester ABP Studio avant tout le monde, devenez BETA testeur" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json index 958e3d533f..9d202f3d5d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hi.json @@ -430,6 +430,7 @@ "ErrorExceptionMessage": "आपका अनुरोध संसाधित करते समय एक त्रुटि पाई गई", "WatchTakeCodeGeneration": "\"कोड जनरेशन की क्षमता का अन्वेषण करें: एबीपी सुइट\" वीडियो देखें!", "ExtendNow": "विस्तार/नवीनीकरण", - "RegisterDemo": "पंजीकरण करवाना" + "RegisterDemo": "पंजीकरण करवाना", + "AbpStudio_ComingSoon": "यदि आप किसी और से पहले एबीपी स्टूडियो का परीक्षण करना चाहते हैं, तो बीटा परीक्षक बनें" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json index 6f8c1930cf..b51e3b94ee 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hr.json @@ -915,7 +915,6 @@ "AbpStudio_Description": "ABP Studio je još uvijek u razvoju. Možete ispuniti obrazac ispod i biti jedan od prvih korisnika.", "AbpStudio_Description1": "ABP Studio je stolna aplikacija za više platformi za ABP programere.", "AbpStudio_Description2": "Dobro je integriran u ABP Framework i ima za cilj pružiti vam ugodno razvojno okruženje automatiziranjem stvari, pružanjem uvida u vaše rješenje, čineći razvoj, pokretanje i implementaciju vaših rješenja puno lakšim.", - "AbpStudio_ComingSoon": "Uskoro. Planirani datum izdavanja beta verzije: 4. tromjesečje 2023.", "AbpStudio_PlannedPreviewDate": "Planirani datum izdavanja pregleda: 4. kvartal 2023.", "BetaRequest": "Zahtjev za beta verziju", "CreateNewSolutions": "Stvorite nova rješenja", @@ -1107,6 +1106,7 @@ "SendUsEmail": "Pošaljite nam e-mail", "ErrorExceptionMessage": "Pojavila se greška prilikom obrade Vašeg zahtjeva", "WatchTakeCodeGeneration": "Pogledajte video \"Istražite potencijal generiranja koda: ABP Suite\"!", - "ExtendNow": "Produži / obnovi" + "ExtendNow": "Produži / obnovi", + "AbpStudio_ComingSoon": "Ako želite testirati ABP Studio prije svih ostalih, postanite BETA tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json index 79889c0d1b..ac1f74eafc 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/hu.json @@ -777,6 +777,7 @@ "SendUsEmail": "Küldjön nekünk e-mailt", "ErrorExceptionMessage": "Hiba történt a kérése feldolgozása során", "WatchTakeCodeGeneration": "Nézze meg a \"Fedezze fel a kódgenerálás lehetőségét: ABP Suite\" videót!", - "ExtendNow": "Bővítés / Megújítás" + "ExtendNow": "Bővítés / Megújítás", + "AbpStudio_ComingSoon": "Ha bárki más előtt szeretné tesztelni az ABP Studio-t, legyen BÉTA tesztelő" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json index d6744a9603..475ef5f98f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/is.json @@ -408,6 +408,7 @@ "SendUsEmail": "Sendu okkur tölvupóst", "ErrorExceptionMessage": "Villa kom upp við úrvinnslu beiðni þinnar", "WatchTakeCodeGeneration": "Horfðu á \"Kannaðu möguleika kóðaframleiðslu: ABP Suite\" myndbandið!", - "ExtendNow": "Framlengja / endurnýja" + "ExtendNow": "Framlengja / endurnýja", + "AbpStudio_ComingSoon": "Ef þú vilt prófa ABP Studio á undan öllum öðrum skaltu gerast BETA prófari" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json index addb4b54e1..c668db7414 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/it.json @@ -430,6 +430,7 @@ "ErrorExceptionMessage": "Si è verificato un errore durante l'elaborazione della richiesta", "WatchTakeCodeGeneration": "Guarda il video \"Esplora il potenziale della generazione di codice: ABP Suite\"!", "ExtendNow": "Estendi/Rinnova", - "RegisterDemo": "Registrati" + "RegisterDemo": "Registrati", + "AbpStudio_ComingSoon": "Se vuoi provare ABP Studio prima di chiunque altro, diventa un BETA tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json index 298e59d573..d99901743e 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/nl.json @@ -408,6 +408,7 @@ "SendUsEmail": "Stuur ons een e-mail", "ErrorExceptionMessage": "Er is een fout opgetreden bij het verwerken van uw verzoek", "WatchTakeCodeGeneration": "Bekijk de video 'Ontdek het potentieel van codegeneratie: ABP Suite'!", - "ExtendNow": "Verlengen / Verlengen" + "ExtendNow": "Verlengen / Verlengen", + "AbpStudio_ComingSoon": "Als u ABP Studio als eerste wilt testen, wordt dan BETA-tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json index 757fc93ec1..1b6f200b66 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pl-PL.json @@ -409,6 +409,7 @@ "ErrorExceptionMessage": "Podczas przetwarzania żądania wystąpił błąd", "WatchTakeCodeGeneration": "Obejrzyj film „Odkryj potencjał generowania kodu: pakiet ABP”!", "ExtendNow": "Przedłuż/Odnów", - "RegisterDemo": "Rejestr" + "RegisterDemo": "Rejestr", + "AbpStudio_ComingSoon": "Jeśli chcesz przetestować ABP Studio zanim ktokolwiek inny, zostań testerem BETA" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json index fe72c04d15..43d57e4184 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/pt-BR.json @@ -431,6 +431,7 @@ "SendUsEmail": "Envie-nos um e-mail", "ErrorExceptionMessage": "Um erro ocorreu durante o processamento do seu pedido", "WatchTakeCodeGeneration": "Assista ao vídeo \"Explore o potencial de geração de código: ABP Suite\"!", - "ExtendNow": "Estender/Renovar" + "ExtendNow": "Estender/Renovar", + "AbpStudio_ComingSoon": "Se você quiser testar o ABP Studio antes de todo mundo, torne-se um testador BETA" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json index 6bb638c703..3e66a513d6 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/ro-RO.json @@ -408,6 +408,7 @@ "SendUsEmail": "Trimite-ne e-mail", "ErrorExceptionMessage": "A apărut o eroare în timpul procesării cererii dumneavoastră", "WatchTakeCodeGeneration": "Urmărește videoclipul „Explorați potențialul generării codului: ABP Suite”!", - "ExtendNow": "Extinde/Reînnoiește" + "ExtendNow": "Extinde/Reînnoiește", + "AbpStudio_ComingSoon": "Dacă doriți să testați ABP Studio înaintea oricui, deveniți un tester BETA" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json index 26f1c79704..6abcbcf1bf 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sk.json @@ -427,6 +427,7 @@ "SendUsEmail": "Pošlite nám e-mail", "ErrorExceptionMessage": "Nastala chyba pri spracovaní vašej žiadosti", "WatchTakeCodeGeneration": "Pozrite si video „Preskúmajte potenciál generovania kódu: ABP Suite“!", - "ExtendNow": "Predĺžiť / obnoviť" + "ExtendNow": "Predĺžiť / obnoviť", + "AbpStudio_ComingSoon": "Ak chcete otestovať ABP Studio skôr ako ktokoľvek iný, staňte sa BETA testerom" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json index 0d25e94113..880006579c 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/sl.json @@ -409,6 +409,7 @@ "ErrorExceptionMessage": "Pri obdelavi vaše zahteve je prišlo do napake", "WatchTakeCodeGeneration": "Oglejte si video »Raziščite potencial generiranja kode: zbirka ABP«!", "ExtendNow": "Podaljšaj / obnovi", - "RegisterDemo": "Registrirajte se" + "RegisterDemo": "Registrirajte se", + "AbpStudio_ComingSoon": "Če želite preizkusiti ABP Studio pred vsemi drugimi, postanite BETA tester" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json index 9282239bee..22ef2ddc0f 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/tr.json @@ -1058,7 +1058,6 @@ "AbpStudio_Description": "ABP Studio hala geliştirme aşamasında. İlk kullanıcılardan biri olmak için aşağıdaki formu doldurabilirsiniz.", "AbpStudio_Description1": "ABP Studio, ABP geliştiricileri için çok platformlu masaüstü uygulamasıdır.", "AbpStudio_Description2": "ABP Studio, ABP Framework'e iyi entegre edilmiştir ve sizin için işleri otomatikleştirerek, projeniz hakkında bilgi sağlayarak, projelerinizi geliştirmeyi, çalıştırmayı ve dağıtımını çok daha kolay hale getirmeyi amaçlamaktadır.", - "AbpStudio_ComingSoon": "Yakında Planlanan beta sürüm tarihi: 2023'ün 4. çeyreği.", "AbpStudio_PlannedPreviewDate": "Planlanan beta sürüm tarihi: 2023'ün 4. çeyreği.", "BetaRequest": "Beta Talebi", "CreateNewSolutions": "Yeni Projeler Oluştur", @@ -1140,6 +1139,7 @@ "RemoveBasket": "Sepetten kaldır", "TrainingPack": "Eğitim paketi", "TrainingPackDiscount": "Eğitim paketi indirimi", - "LinkExpiredMessage": "Ödeme bağlantısının süresi doldu! Bağlantıyı güncellemek için sales@volosoft.com adresinden bizimle iletişime geçin veya iletişim sayfasına gitmek için buraya tıklayın." + "LinkExpiredMessage": "Ödeme bağlantısının süresi doldu! Bağlantıyı güncellemek için sales@volosoft.com adresinden bizimle iletişime geçin veya iletişim sayfasına gitmek için buraya tıklayın.", + "AbpStudio_ComingSoon": "ABP Studio'yu herkesten önce test etmek istiyorsanız BETA test kullanıcısı olun" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json index 6834f26f24..a944e8e193 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/vi.json @@ -408,6 +408,7 @@ "SendUsEmail": "Gửi email cho chúng tôi", "ErrorExceptionMessage": "Đã xảy ra lỗi trong khi xử lý yêu cầu của bạn", "WatchTakeCodeGeneration": "Xem video \"Khám phá tiềm năng tạo mã: ABP Suite\"!", - "ExtendNow": "Gia hạn/Gia hạn" + "ExtendNow": "Gia hạn/Gia hạn", + "AbpStudio_ComingSoon": "Nếu bạn muốn thử nghiệm ABP Studio trước bất kỳ ai khác, hãy trở thành người thử nghiệm BETA" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json index 57caf8322d..a6af2260cb 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hans.json @@ -915,7 +915,6 @@ "AbpStudio_Description": "ABP Studio 仍在开发中。您可以填写下面的表单,成为首批用户之一。", "AbpStudio_Description1": "ABP Studio 是专为 ABP 开发人员设计的跨平台桌面应用程序。", "AbpStudio_Description2": "它与 ABP 框架集成得很好,旨在通过自动化、提供有关解决方案的见解,为您提供舒适的开发环境,使开发、运行部署解决方案变得更加容易。", - "AbpStudio_ComingSoon": "即将发布 计划测试版发布日期:计划测试版发布日期:2023年第四季度。", "AbpStudio_PlannedPreviewDate": "计划预览版发布日期:2023 年第四季度。", "BetaRequest": "测试版请求", "CreateNewSolutions": "创建新的解决方案", @@ -1110,6 +1109,7 @@ "ErrorExceptionMessage": "处理您的请求时发生错误", "WatchTakeCodeGeneration": "观看“探索代码生成的潜力:ABP Suite”视频!", "PreBuiltApplicationModulesTitle": "预构建应用程序 模块", - "RegisterDemo": "登记" + "RegisterDemo": "登记", + "AbpStudio_ComingSoon": "如果您想先于其他人测试 ABP Studio,请成为 BETA 测试员" } } diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json index e9ee4ef230..ae5ba6708c 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Commercial/Localization/Resources/zh-Hant.json @@ -897,7 +897,6 @@ "AbpStudio_Description": "ABP Studio 仍在開發中。您可以填寫下面的表单,成為第一批使用者之一。", "AbpStudio_Description1": "ABP Studio 是一款針對 ABP 開發人員的跨平台桌面應用程式。", "AbpStudio_Description2": "它與 ABP 框架很好地集成,旨在透過自動化為您提供一個舒適的開發環境,提供有關您的解決方案的見解,使開發、運行部署您的解決方案變得更加容易。", - "AbpStudio_ComingSoon": "即將推出計劃測試版發布日期:2023 年第四季。", "AbpStudio_PlannedPreviewDate": "計畫預覽版發布日期:2023 年第四季。", "BetaRequest": "測試版請求", "CreateNewSolutions": "建立新的解決方案", @@ -1100,6 +1099,7 @@ "WatchTakeCodeGeneration": "观看“探索代码生成的潜力:ABP Suite”视频!", "ExtendNow": "延长 / 续订", "PreBuiltApplicationModulesTitle": "预构建应用程序 模块", - "RegisterDemo": "登记" + "RegisterDemo": "登记", + "AbpStudio_ComingSoon": "如果您想先于其他人测试 ABP Studio,请成为 BETA 测试员" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json index 2b6f5db40c..fef40ce7c1 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ar.json @@ -259,6 +259,9 @@ "MarkdownSupported": "Markdown أيد.", "Preview": "معاينة", "VisitVideoCourseDescription": "إذا كنت ترغب في تعلم أساسيات إطار عمل برنامج ABP، فاطلع على دورات الفيديو الخاصة ببرنامج ABP Essentials.", - "VisitPage": "زر الصفحة" + "VisitPage": "زر الصفحة", + "ConfirmEmailForPost": "لتتمكن من النشر، تحتاج إلى تأكيد بريدك الإلكتروني. انتقل إلى account.abp.io/Account/Manage وتحقق من بريدك الإلكتروني في علامة التبويب \"المعلومات الشخصية\".", + "DailyPostCreateLimitation": "لقد وصلت إلى الحد الأقصى اليومي لإنشاء المنشورات. يمكنك إنشاء مشاركة جديدة في {0}.", + "YourAccountDisabled": "تم تعطيل حساب المستخدم الخاص بك!" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json index 35119a1408..86910d5557 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/cs.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podporováno.", "Preview": "Náhled", "VisitPage": "Navštivte stránku", - "VisitVideoCourseDescription": "Pokud se chcete naučit základy rámce ABP, podívejte se na videokurzy ABP Essentials." + "VisitVideoCourseDescription": "Pokud se chcete naučit základy rámce ABP, podívejte se na videokurzy ABP Essentials.", + "ConfirmEmailForPost": "Abyste mohli přidávat příspěvky, musíte potvrdit svůj e-mail. Přejděte na stránku account.abp.io/Account/Manage a ověřte svůj e-mail na kartě Osobní údaje.", + "DailyPostCreateLimitation": "Dosáhli jste denního limitu pro vytváření příspěvků. Nový příspěvek můžete vytvořit v {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json index d26c081de5..99c60d5199 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/de.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown unterstützt.", "Preview": "Vorschau", "VisitPage": "Seite besuchen", - "VisitVideoCourseDescription": "Wenn Sie die Grundlagen des ABP Framework erlernen möchten, schauen Sie sich die ABP Essentials-Videokurse an." + "VisitVideoCourseDescription": "Wenn Sie die Grundlagen des ABP Framework erlernen möchten, schauen Sie sich die ABP Essentials-Videokurse an.", + "ConfirmEmailForPost": "Um Beiträge verfassen zu können, müssen Sie Ihre E-Mail-Adresse bestätigen. Gehen Sie zu account.abp.io/Account/Manage und bestätigen Sie Ihre E-Mail-Adresse auf der Registerkarte „Persönliche Daten“.", + "DailyPostCreateLimitation": "Sie haben das tägliche Limit für die Erstellung von Beiträgen erreicht. Sie können in {0} einen neuen Beitrag erstellen." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json index 347b18ce4b..dc59234777 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en-GB.json @@ -59,7 +59,7 @@ "MilestoneErrorMessage": "Couldn't get the current milestone details from Github.", "QuestionItemErrorMessage": "Couldn't get the latest question details from Stackoverflow.", "Oops": "Oops!", - "CreatePostSuccessMessage": "The Post has been successfully submitted. It will be published once it has been reviewed by the site admin.", + "CreatePostSuccessMessage": "Your post has been successfully submitted. It will be published after review by the site administrator.", "ChooseCoverImage": "Choose a cover image...", "CoverImage": "Cover Image", "ShareYourExperiencesWithTheABPFramework": "Share your experiences with the ABP Framework!", diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index 80fbc8539b..78231bb1af 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -16,7 +16,7 @@ "Title": "Title", "CreationTime": "Creation time", "Save": "Save", - "SameUrlAlreadyExist": "Same url already exists if you want to add this post, you should change the url!", + "SameUrlAlreadyExist": "Same URL already exists! You must change the URL!", "UrlIsNotValid": "Url is not valid.", "UrlNotFound": "Url not found.", "UrlContentNotFound": "Url content not found.", @@ -33,7 +33,7 @@ "FeatureRequest": "Feature Request", "CreatePostTitleInfo": "Title of the post to be shown on the post list.", "CreatePostSummaryInfo": "A short summary of the post to be shown on the post list. Maximum length: {0}", - "CreatePostCoverInfo": "For creating an effective post, add a cover photo. Upload 16:9 aspect ratio pictures for the best view.
Maximum file size: 1MB.", + "CreatePostCoverInfo": "To create an effective post, add a cover photo. Upload 16:9 aspect ratio pictures for the best view.
Maximum file size is 1MB.", "ThisExtensionIsNotAllowed": "This extension is not allowed.", "TheFileIsTooLarge": "The file is too large.", "GoToThePost": "Go to the Post", @@ -56,7 +56,7 @@ "MilestoneErrorMessage": "Could not get the current milestone details from Github.", "QuestionItemErrorMessage": "Could not get the latest question details from Stackoverflow.", "Oops": "Oops!", - "CreatePostSuccessMessage": "The Post has been successfully submitted. It will be published after a review from the site admin.", + "CreatePostSuccessMessage": "Your post has been successfully submitted. It will be published after review by the site administrator.", "Browse": "Browse", "CoverImage": "Cover Image", "ShareYourExperiencesWithTheABPFramework": "Share your experiences with the ABP Framework!", @@ -125,7 +125,7 @@ "YoutubeVideoSubmitStepTwo": "2. Submit the video URL using the form.", "YoutubeVideoSubmitStepThree": "3. Visitors will be able to watch your video content directly on this website.", "ExternalContent": "External Content", - "ExternalContentSubmitStepOne": "1. Create a content on any public platform (Medium, your own blog or anywhere you like).", + "ExternalContentSubmitStepOne": "1. Create content on any public platform (Medium, your own blog or anywhere you like).", "ExternalContentSubmitStepTwo": "2. Submit your content URL using the form.", "ExternalContentSubmitStepThree": "3. Visitors are redirected to the content on the original website.", "ChooseYourContentType": "Please choose the way you want to add your content.", @@ -135,12 +135,12 @@ "GitHubUserNameValidationMessage": "Your Github username can not include whitespace, please make sure your Github username is correct.", "PersonalSiteUrlValidationMessage": "Your personal site URL can not include whitespace, please make sure your personal site URL is correct.", "TwitterUserNameValidationMessage": "Your Twitter username can not include whitespace, please make sure your Twitter username is correct.", - "LinkedinUrlValidationMessage": "Your Linkedin URL can not include whitespace, please make sure your Linkedin URL is correct.", + "LinkedinUrlValidationMessage": "Your LinkedIn URL can not include whitespace, please make sure your LinkedIn URL is correct.", "NoPostsFound": "No posts found!", "SearchInPosts": "Search in posts...", "MinimumSearchContent": "You must enter at least 3 characters!", "Volo.AbpIo.Domain:060001": "Source URL(\"{PostUrl}\") is not Github URL", - "Volo.AbpIo.Domain:060002": "Post Content is not available from Github(\"{PostUrl}\") resource.", + "Volo.AbpIo.Domain:060002": "Post Content is not available from GitHub(\"{PostUrl}\") resource.", "Volo.AbpIo.Domain:060003": "No post content found!", "SeeMore": "See More", "JoinTheABPCommunity": "Join the ABP Community", @@ -171,14 +171,14 @@ "Discord_Page_Announce": "We are happy to announce ABP Community Discord Server!", "Discord_Page_Description_1": "ABP Community has been growing since day one. We wanted to take it to the next step by creating an official ABP Discord server so the ABP Community can interact with each other using the wonders of instant messaging.", "Discord_Page_Description_2": "ABP Community Discord Server is the place where you can showcase your creations using ABP Framework, share the tips that worked for you, catch up with the latest news and announcements about ABP Framework, just chat with community members to exchange ideas, and have fun!", - "Discord_Page_Description_3": "This ABP Community Discord Server is the official one with the ABP Core Team is present on the server to monitor.", + "Discord_Page_Description_3": "This ABP Community Discord Server is the official one with the ABP Core Team present on the server to monitor.", "Discord_Page_JoinToServer": "Join ABP Discord Server", "Events_Page_MetaTitle": "ABP Community Events", "Events_Page_MetaDescription": "The live shows, hosted by the ABP Team, are casual sessions full of community content, demos, Q&A, and discussions around what's happening in ABP.", "Events_Page_Title": "ABP Community Talks", "Members_Page_WritingFromUser": "Read writing from {0} on ABP Community.", "Post_Create_Page_MetaTitle": "New Post", - "Post_Create_Page_MetaDescription": "Create your post for sharing your experiences about ABP framework and contributing the ABP Community.", + "Post_Create_Page_MetaDescription": "Create your post for sharing your experiences about the ABP framework and contributing to the ABP Community.", "Post_Create_Page_CreateNewPost": "Create New Post", "Post_Index_Page_MetaDescription": "ABP Community's purpose is to create a contribution environment for developers who use the ABP framework.", "Layout_Title": "{0} | ABP Community", @@ -193,7 +193,7 @@ "AbpCommunityTitleContent": "ABP Community - Open Source ABP Framework", "CommunitySlogan": "A unique community platform for ABP Lovers", "RaffleIsNotActive": "Raffle is not active", - "YouAreAlreadyJoinedToThisRaffle": "You already joined to this raffle!", + "YouAreAlreadyJoinedToThisRaffle": "You already joined this raffle!", "InvalidSubscriptionCode": "Invalid subscription code", "Raffle:{0}": "Raffle: {0}", "Join": "Join", @@ -261,6 +261,9 @@ "Preview": "Preview", "VisitPage": "Visit Page", "VisitVideoCourseDescription": "If you want to learn the basics of the ABP Framework, check out the ABP Essentials Video courses.", - "EditProfile": "Edit Profile" + "EditProfile": "Edit Profile", + "ConfirmEmailForPost": "To be able to post, you need to confirm your email. Go to account.abp.io/Account/Manage and verify your email in the Personal Info tab.", + "DailyPostCreateLimitation": "You have reached the daily post creation limit. You can create a new post in {0}.", + "YourAccountDisabled": "Your user account is disabled!" } -} \ No newline at end of file +} diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json index bf045dea81..2403cb7681 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/es.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown soportado.", "Preview": "Avance", "VisitPage": "Visita la página", - "VisitVideoCourseDescription": "Si desea aprender los conceptos básicos del marco ABP, consulte los cursos en vídeo de ABP Essentials." + "VisitVideoCourseDescription": "Si desea aprender los conceptos básicos del marco ABP, consulte los cursos en vídeo de ABP Essentials.", + "ConfirmEmailForPost": "Para poder publicar, debe confirmar su correo electrónico. Vaya a account.abp.io/Account/Manage y verifique su correo electrónico en la pestaña Información personal.", + "DailyPostCreateLimitation": "Has alcanzado el límite diario de creación de publicaciones. Puedes crear una nueva publicación en {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json index acf70381ff..3f420b1822 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fi.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown tuettu.", "Preview": "Esikatselu", "VisitPage": "Vieraile sivulla", - "VisitVideoCourseDescription": "Jos haluat oppia ABP Frameworkin perusteet, katso ABP Essentials Video -kurssit." + "VisitVideoCourseDescription": "Jos haluat oppia ABP Frameworkin perusteet, katso ABP Essentials Video -kurssit.", + "ConfirmEmailForPost": "Jotta voit lähettää viestejä, sinun on vahvistettava sähköpostiosoitteesi. Siirry osoitteeseen account.abp.io/Account/Manage ja vahvista sähköpostiosoitteesi Henkilökohtaiset tiedot -välilehdessä.", + "DailyPostCreateLimitation": "Olet saavuttanut päivittäisen viestien luomisrajan. Voit luoda uuden viestin kohteessa {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json index 963bde3048..a7821015b4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/fr.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown prise en charge.", "Preview": "Aperçu", "VisitPage": "Page de visite", - "VisitVideoCourseDescription": "Si vous souhaitez apprendre les bases du framework ABP, consultez les cours vidéo ABP Essentials." + "VisitVideoCourseDescription": "Si vous souhaitez apprendre les bases du framework ABP, consultez les cours vidéo ABP Essentials.", + "ConfirmEmailForPost": "Pour pouvoir publier, vous devez confirmer votre e-mail. Accédez à account.abp.io/Account/Manage et vérifiez votre e-mail dans l'onglet Informations personnelles.", + "DailyPostCreateLimitation": "Vous avez atteint la limite quotidienne de création de publications. Vous pouvez créer une nouvelle publication dans {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json index 940d92573f..87919eef8b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hi.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown का समर्थन किया।", "Preview": "पूर्व दर्शन", "VisitPage": "यात्रा पेज", - "VisitVideoCourseDescription": "यदि आप एबीपी फ्रेमवर्क की मूल बातें सीखना चाहते हैं, तो एबीपी एसेंशियल वीडियो पाठ्यक्रम देखें।" + "VisitVideoCourseDescription": "यदि आप एबीपी फ्रेमवर्क की मूल बातें सीखना चाहते हैं, तो एबीपी एसेंशियल वीडियो पाठ्यक्रम देखें।", + "ConfirmEmailForPost": "पोस्ट करने में सक्षम होने के लिए, आपको अपने ईमेल की पुष्टि करनी होगी। account.abp.io/Account/Manage पर जाएं और व्यक्तिगत जानकारी टैब में अपना ईमेल सत्यापित करें।", + "DailyPostCreateLimitation": "आप दैनिक पोस्ट निर्माण सीमा तक पहुंच गए हैं। आप {0} में एक नई पोस्ट बना सकते हैं।" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json index f0b3a5941c..5cb7ff44a0 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hr.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podržan.", "Preview": "Pregled", "VisitPage": "Posjetite stranicu", - "VisitVideoCourseDescription": "Ako želite naučiti osnove ABP okvira, pogledajte video tečajeve ABP Essentials." + "VisitVideoCourseDescription": "Ako želite naučiti osnove ABP okvira, pogledajte video tečajeve ABP Essentials.", + "ConfirmEmailForPost": "Da biste mogli objavljivati, morate potvrditi svoju e-poštu. Idite na account.abp.io/Account/Manage i potvrdite svoju e-poštu na kartici Osobni podaci.", + "DailyPostCreateLimitation": "Dosegli ste dnevno ograničenje za izradu postova. Možete stvoriti novi post u {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json index 84117ededd..95433c6d92 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/hu.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown támogatott.", "Preview": "Előnézet", "VisitPage": "Látogassa meg az oldalt", - "VisitVideoCourseDescription": "Ha meg szeretné tanulni az ABP Framework alapjait, nézze meg az ABP Essentials Video tanfolyamokat." + "VisitVideoCourseDescription": "Ha meg szeretné tanulni az ABP Framework alapjait, nézze meg az ABP Essentials Video tanfolyamokat.", + "ConfirmEmailForPost": "A bejegyzések közzétételéhez meg kell erősítenie e-mail-címét. Nyissa meg az account.abp.io/Account/Manage oldalt, és ellenőrizze e-mail-címét a Személyes adatok lapon.", + "DailyPostCreateLimitation": "Elérte a napi bejegyzéslétrehozási korlátot. Új bejegyzést itt hozhat létre: {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json index 953186b239..8f2a733a2d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/is.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown stutt.", "Preview": "Forskoðun", "VisitPage": "Heimsæktu síðu", - "VisitVideoCourseDescription": "Ef þú vilt læra grunnatriði ABP Framework skaltu skoða ABP Essentials Video námskeiðin." + "VisitVideoCourseDescription": "Ef þú vilt læra grunnatriði ABP Framework skaltu skoða ABP Essentials Video námskeiðin.", + "ConfirmEmailForPost": "Til að geta sent færslur þarftu að staðfesta netfangið þitt. Farðu á account.abp.io/Account/Manage og staðfestu tölvupóstinn þinn á Persónulegum upplýsingum flipanum.", + "DailyPostCreateLimitation": "Þú hefur náð daglegu takmörkunum fyrir færslu færslu. Þú getur búið til nýja færslu í {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json index 88bf43fc0e..45021c315d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/it.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown supportato.", "Preview": "Anteprima", "VisitPage": "Visita la pagina", - "VisitVideoCourseDescription": "Se vuoi apprendere le nozioni di base del Framework ABP, dai un'occhiata ai corsi video ABP Essentials." + "VisitVideoCourseDescription": "Se vuoi apprendere le nozioni di base del Framework ABP, dai un'occhiata ai corsi video ABP Essentials.", + "ConfirmEmailForPost": "Per poter pubblicare, devi confermare la tua email. Vai su account.abp.io/Account/Manage e verifica la tua email nella scheda Informazioni personali.", + "DailyPostCreateLimitation": "Hai raggiunto il limite giornaliero di creazione di post. Puoi creare un nuovo post in {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json index 5d18bde830..958c68263d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/nl.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown ondersteund.", "Preview": "Voorbeeld", "VisitPage": "Bezoek pagina", - "VisitVideoCourseDescription": "Als je de basis van het ABP Framework wilt leren, bekijk dan de ABP Essentials Videocursussen." + "VisitVideoCourseDescription": "Als je de basis van het ABP Framework wilt leren, bekijk dan de ABP Essentials Videocursussen.", + "ConfirmEmailForPost": "Om te kunnen posten, moet u uw e-mailadres bevestigen. Ga naar account.abp.io/Account/Manage en verifieer uw e-mailadres op het tabblad Persoonlijke informatie.", + "DailyPostCreateLimitation": "Je hebt de dagelijkse limiet voor het maken van berichten bereikt. Je kunt een nieuw bericht maken in {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json index a86b93515c..16af07e6e4 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pl-PL.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown utrzymany.", "Preview": "Zapowiedź", "VisitPage": "Odwiedź stronę", - "VisitVideoCourseDescription": "Jeśli chcesz poznać podstawy ABP Framework, sprawdź kursy wideo ABP Essentials." + "VisitVideoCourseDescription": "Jeśli chcesz poznać podstawy ABP Framework, sprawdź kursy wideo ABP Essentials.", + "ConfirmEmailForPost": "Aby móc publikować, musisz potwierdzić swój adres e-mail. Przejdź do account.abp.io/Account/Manage i zweryfikuj swój adres e-mail w zakładce Dane osobowe.", + "DailyPostCreateLimitation": "Osiągnąłeś dzienny limit tworzenia postów. Możesz utworzyć nowy post w {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json index af28a46228..249e758a53 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/pt-BR.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown suportado.", "Preview": "Visualização", "VisitPage": "Visite a página", - "VisitVideoCourseDescription": "Se você quiser aprender o básico do ABP Framework, confira os cursos em vídeo ABP Essentials." + "VisitVideoCourseDescription": "Se você quiser aprender o básico do ABP Framework, confira os cursos em vídeo ABP Essentials.", + "ConfirmEmailForPost": "Para poder postar, você precisa confirmar seu e-mail. Acesse account.abp.io/Account/Manage e verifique seu e-mail na guia Informações pessoais.", + "DailyPostCreateLimitation": "Você atingiu o limite diário de criação de postagens. Você pode criar uma nova postagem em {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json index e9d89b398b..7d33ecdb90 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/ro-RO.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown sprijinit.", "Preview": "previzualizare", "VisitPage": "Vizitați pagina", - "VisitVideoCourseDescription": "Dacă doriți să aflați elementele de bază ale cadrului ABP, consultați cursurile video ABP Essentials." + "VisitVideoCourseDescription": "Dacă doriți să aflați elementele de bază ale cadrului ABP, consultați cursurile video ABP Essentials.", + "ConfirmEmailForPost": "Pentru a putea posta, trebuie să vă confirmați adresa de e-mail. Accesați account.abp.io/Account/Manage și verificați e-mailul în fila Informații personale.", + "DailyPostCreateLimitation": "Ați atins limita zilnică de creare a postărilor. Puteți crea o postare nouă în {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json index dcfbc84bf9..6c842ef388 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sk.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podporované.", "Preview": "Náhľad", "VisitPage": "Navštívte stránku", - "VisitVideoCourseDescription": "Ak sa chcete naučiť základy rámca ABP, pozrite si video kurzy ABP Essentials." + "VisitVideoCourseDescription": "Ak sa chcete naučiť základy rámca ABP, pozrite si video kurzy ABP Essentials.", + "ConfirmEmailForPost": "Aby ste mohli uverejňovať príspevky, musíte potvrdiť svoj e-mail. Prejdite na stránku account.abp.io/Account/Manage a overte svoj e-mail na karte Osobné informácie.", + "DailyPostCreateLimitation": "Dosiahli ste denný limit na vytváranie príspevkov. Nový príspevok môžete vytvoriť v {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json index 8ed4d53271..eb6a616017 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/sl.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown podprt.", "Preview": "Predogled", "VisitPage": "Obiščite stran", - "VisitVideoCourseDescription": "Če se želite naučiti osnov ogrodja ABP, si oglejte video tečaje ABP Essentials." + "VisitVideoCourseDescription": "Če se želite naučiti osnov ogrodja ABP, si oglejte video tečaje ABP Essentials.", + "ConfirmEmailForPost": "Če želite objavljati, morate potrditi svoj e-poštni naslov. Pojdite na account.abp.io/Account/Manage in potrdite svoj e-poštni naslov na zavihku Osebni podatki.", + "DailyPostCreateLimitation": "Dosegli ste dnevno omejitev za ustvarjanje objav. Novo objavo lahko ustvarite v {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json index 711d068087..8d42d61eb0 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/tr.json @@ -244,6 +244,9 @@ "BuyTicket": "Bilet al", "SeeEvent": "Etkinliği Gör", "Cancel": "Vazgeç", - "Continue": "Devam" + "Continue": "Devam", + "ConfirmEmailForPost": "Gönderi paylaşabilmek için e-posta adresinizi onaylamanız gerekir. account.abp.io/Account/Manage adresine gidin ve Kişisel Bilgiler sekmesinden e-posta adresinizi doğrulayın.", + "DailyPostCreateLimitation": "Günlük gönderi paylaşma sınırına ulaştınız. {0}'da yeni bir gönderi paylaşabilirsiniz.", + "YourAccountDisabled": "Kullanıcı hesabınız devre dışı bırakıldı!" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json index bd6baa67bb..4ca0c8ca49 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/vi.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown được hỗ trợ.", "Preview": "Xem trước", "VisitPage": "Ghé thăm trang", - "VisitVideoCourseDescription": "Nếu bạn muốn tìm hiểu những kiến ​​thức cơ bản về Khung ABP, hãy xem các khóa học Video Cơ bản về ABP." + "VisitVideoCourseDescription": "Nếu bạn muốn tìm hiểu những kiến ​​thức cơ bản về Khung ABP, hãy xem các khóa học Video Cơ bản về ABP.", + "ConfirmEmailForPost": "Để có thể đăng bài, bạn cần xác nhận email của mình. Hãy truy cập account.abp.io/Account/Quản lý và xác minh email của bạn trong tab Thông tin cá nhân.", + "DailyPostCreateLimitation": "Bạn đã đạt đến giới hạn tạo bài đăng hàng ngày. Bạn có thể tạo bài đăng mới trong {0}." } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json index 2bf0e9fb50..c6ef5a2c3d 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown 支持的。", "Preview": "预览", "VisitPage": "访问页面", - "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。" + "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。", + "ConfirmEmailForPost": "为了能够发帖,您需要确认您的电子邮件。转到 account.abp.io/Account/Manage 并在“个人信息”选项卡中验证您的电子邮件。", + "DailyPostCreateLimitation": "您已达到每日帖子创建限制。您可以在 {0} 中创建新帖子。" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json index 61bb0aa8a0..b72c927296 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hant.json @@ -259,6 +259,8 @@ "MarkdownSupported": "Markdown 支持的。", "Preview": "预览", "VisitPage": "访问页面", - "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。" + "VisitVideoCourseDescription": "如果您想学习 ABP 框架的基础知识,请查看 ABP Essentials 视频课程。", + "ConfirmEmailForPost": "为了能够发帖,您需要确认您的电子邮件。转到 account.abp.io/Account/Manage 并在“个人信息”选项卡中验证您的电子邮件。", + "DailyPostCreateLimitation": "您已达到每日帖子创建限制。您可以在 {0} 中创建新帖子。" } } \ No newline at end of file diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json index 7f440cf205..984a3d28a7 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json @@ -158,7 +158,7 @@ "IndexPageHeroSection": "open sourceWeb Application
Framework
for asp.net core", "UiFramework": "UI框架", "EmailAddress": "电子邮件地址", - "Mobile": "移动电话", + "Mobile": "选择移动应用程序框架", "ReactNative": "React Native", "Strong": "强大", "Complete": "完整", @@ -476,4 +476,4 @@ "ABPVideoCoursesPageDescription": "通过ABP团队创建的视频课程学习ABP框架的基础知识。在本视频系列中,您将学习ABP框架的基本主题。每个视频上的数字都是根据学习流程编写的。", "ABPVideoCoursesTitle": "ABP 基本视频" } -} \ No newline at end of file +} diff --git a/common.props b/common.props index 26b2c2f91a..fd23245b0b 100644 --- a/common.props +++ b/common.props @@ -1,8 +1,8 @@ latest - 8.2.3 - 3.2.3 + 8.3.0-rc.3 + 3.3.0-rc.3 $(NoWarn);CS1591;CS0436 https://abp.io/assets/abp_nupkg.png https://abp.io/ diff --git a/docs/en/Blog-Posts/2024-05 ABP Unification/post.md b/docs/en/Blog-Posts/2024-05 ABP Unification/post.md index 7046c53855..9da50ac06b 100644 --- a/docs/en/Blog-Posts/2024-05 ABP Unification/post.md +++ b/docs/en/Blog-Posts/2024-05 ABP Unification/post.md @@ -51,7 +51,7 @@ You can easily download and install ABP Studio, login with your abp.io account a ### The New Pricing Page -Since the [ABP Commercial website](https://commercial.abp.io/) has merged with the main website, you will see the *Pricing* page located on the main menu of the abp.io website. We have completely revised the design and content of this page to better reflect which features are open source and free, and what is included in the commercial licenses. +Since the [ABP Commercial website](https://commercial.abp.io/) has merged with the main website, you will see the *Pricing* page located on the main menu of the abp.io website. We have completely revised the design and content of this page to better reflect which features are open source and free, and what is included in the paid licenses. ![new-pricing](new-pricing.png) @@ -68,7 +68,7 @@ Based on our and customers' experiences, there are some problems with that appro * Getting started, development tutorials, release notes, road map and some other documents are duplicated (or very similar) among ABP Framework and ABP Commercial documents. * For ABP Commercial users, it is not clear if they also need to read the ABP Framework (open source) documentation or not. Also, when they read the framework document, some parts are different for ABP Commercial users, and it is also not clear in some cases. -We are currently working to completely merge the ABP Framework (open source) and ABP Commercial documentation, remove duplications and revisit the contents. We will clearly indicate if a part of a document requires a commercial license. +We are currently working to completely merge the ABP Framework (open source) and ABP Commercial documentation, remove duplications and revisit the contents. We will clearly indicate if a part of a document requires a paid license. The left navigation panel tree is also completely revisited and simplified: diff --git a/docs/en/cli/index.md b/docs/en/cli/index.md index 8e3649e386..ba3e6aabf8 100644 --- a/docs/en/cli/index.md +++ b/docs/en/cli/index.md @@ -105,6 +105,8 @@ abp cli update abp cli update --preview abp cli update --version 1.0.0 abp cli remove +abp cli check-version +abp cli clear-cache ```` ### new @@ -138,10 +140,10 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md * `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios. (*Available for* ***Team*** *or higher licenses*) * `angular`: Angular UI. There are some additional options for this template: * `--tiered`: The Auth Server project comes as a separate project and runs at a different endpoint. It separates the Auth Server from the API Host application. If not specified, you will have a single endpoint in the server side. (*Available for* ***Team*** *or higher licenses*) - * `--pwa`: Specifies the project as Progressive Web Application. + * `--progressive-web-app` or `-pwa`: Specifies the project as Progressive Web Application. * `blazor`: Blazor UI. There are some additional options for this template: * `--tiered`The Auth Server project comes as a separate project and runs at a different endpoint. It separates the Auth Server from the API Host application. If not specified, you will have a single endpoint in the server side. (*Available for* ***Team*** *or higher licenses*) - * `--pwa`: Specifies the project as Progressive Web Application. + * `--progressive-web-app` or `-pwa`: Specifies the project as Progressive Web Application. * `blazor-server`: Blazor Server UI. There are some additional options for this template: * `--tiered`: The Auth Server and the API Host project comes as separate projects and run at different endpoints. It has 3 startup projects: *HttpApi.Host*, *AuthServer* and *Blazor* and and each runs on different endpoints. If not specified, you will have a single endpoint for your web project. (*Available for* ***Team*** *or higher licenses*) * `maui-blazor`: Blazor Maui UI (*Available for* ***Team*** *or higher licenses*). There are some additional options for this template: @@ -156,8 +158,12 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md * `ef`: Entity Framework Core. * `mongodb`: MongoDB. * `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](../framework/data/entity-framework-core/other-dbms.md) (after creating the solution). + * `--skip-migrations` or `-sm`: Skips the creating initial database migration step. + * `--skip-migrator` or `-smr`: Skips the run database migrator step. * `--public-website`: Public Website is a front-facing website for describing your project, listing your products and doing SEO for marketing purposes. Users can login and register on your website with this website. This option is only included in PRO templates. + * `--without-cms-kit`: When you add a public website to your solution, it automatically includes the [CmsKit](./../modules/cms-kit-pro/index.md) module. If you don't want to include *CmsKit*, you can use this parameter. * `--separate-tenant-schema`: Creates a different DbContext for tenant schema. If not specified, the tenant schema is shared with the host schema. This option is only included in PRO templates. + * `--sample-crud-page` or `-scp`: It adds the [BookStore](./../tutorials/book-store/index.md) sample to your solution. * `--theme` or `-th`: Specifes the theme. Default theme is `leptonx`. Available themes: * `leptonx`: LeptonX Theme. (*Available for* ***Team*** *or higher licenses*) * `leptonx-lite`: LeptonX-Lite Theme. @@ -174,6 +180,9 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md * `ef`: Entity Framework Core. * `mongodb`: MongoDB. * `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](../framework/data/entity-framework-core/other-dbms.md) (after creating the solution). + * `--skip-migrations` or `-sm`: Skips the creating initial database migration step. + * `--skip-migrator` or `-smr`: Skips the run database migrator step. + * `--sample-crud-page` or `-scp`: It adds the [BookStore](./../tutorials/book-store/index.md) sample to your solution. * `--theme`: Specifes the theme. Default theme is `leptonx`. Available themes: * `leptonx`: LeptonX Theme. (*Available for* ***Team*** *or higher licenses*) * `leptonx-lite`: LeptonX-Lite Theme. @@ -211,6 +220,7 @@ For more samples, go to [ABP CLI Create Solution Samples](new-command-samples.md * `--dont-run-install-libs`: Skip installing client side packages. * `--dont-run-bundling`: Skip bundling for Blazor packages. * `--no-kubernetes-configuration` or `-nkc`: Skips the Kubernetes configuration files. +* `--no-social-logins` or `-nsl`: Skipts the social login configuration. * *Module Options*: You can skip some modules if you don't want to add them to your solution (*Available for* ***Team*** *or higher licenses*). Available commands: * `-no-saas`: Skips the Saas module. * `-no-gdpr`: Skips the GDPR module. diff --git a/docs/en/framework/architecture/multi-tenancy/index.md b/docs/en/framework/architecture/multi-tenancy/index.md index 1431e12aa4..7a8f23c53e 100644 --- a/docs/en/framework/architecture/multi-tenancy/index.md +++ b/docs/en/framework/architecture/multi-tenancy/index.md @@ -10,8 +10,8 @@ Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) the multi-tenanc There are two main side of a typical SaaS / Multi-tenant application: -* A **Tenant** is a customer of the SaaS application that pays money to use the service. -* **Host** is the company that owns the SaaS application and manages the system. +- A **Tenant** is a customer of the SaaS application that pays money to use the service. +- **Host** is the company that owns the SaaS application and manages the system. The Host and the Tenant terms will be used for that purpose in the rest of the document. @@ -36,9 +36,9 @@ Configure(options => ABP supports all the following approaches to store the tenant data in the database; -* **Single Database**: All tenants are stored in a single database. -* **Database per Tenant**: Every tenant has a separate, dedicated database to store the data related to that tenant. -* **Hybrid**: Some tenants share a single databases while some tenants may have their own databases. +- **Single Database**: All tenants are stored in a single database. +- **Database per Tenant**: Every tenant has a separate, dedicated database to store the data related to that tenant. +- **Hybrid**: Some tenants share a single database while some tenants may have their own databases. [Tenant management module](../../../modules/tenant-management.md) (which comes pre-installed with the startup projects) allows you to set a connection string for any tenant (as optional), so you can achieve any of the approaches. @@ -50,9 +50,9 @@ Multi-tenancy system is designed to **work seamlessly** and make your applicatio You should implement the `IMultiTenant` interface for your [entities](../domain-driven-design/entities.md) to make them **multi-tenancy ready**. -**Example: A multi-tenant *Product* entity** +**Example: A multi-tenant _Product_ entity** -````csharp +```csharp using System; using Volo.Abp.Domain.Entities; using Volo.Abp.MultiTenancy; @@ -68,7 +68,7 @@ namespace MultiTenancyDemo.Products public float Price { get; set; } } } -```` +``` `IMultiTenant` interface just defines a `TenantId` property. When you implement this interface, ABP **automatically** [filters](../../infrastructure/data-filtering.md) entities for the current tenant when you query from database. So, you don't need to manually add `TenantId` condition while performing queries. A tenant can not access to data of another tenant by default. @@ -96,9 +96,9 @@ If you set the `TenantId` value for a specific entity object, it will override t `ICurrentTenant` defines the following properties; -* `Id` (`Guid`): Id of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request. -* `Name` (`string`): Name of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request. -* `IsAvailable` (`bool`): Returns `true` if the `Id` is not `null`. +- `Id` (`Guid`): Id of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request. +- `Name` (`string`): Name of the current tenant. Can be `null` if the current user is a host user or the tenant could not be determined from the request. +- `IsAvailable` (`bool`): Returns `true` if the `Id` is not `null`. #### Change the Current Tenant @@ -108,7 +108,7 @@ ABP automatically filters the resources (database, cache...) based on the `ICurr **Example: Get product count of a specific tenant** -````csharp +```csharp using System; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; @@ -134,11 +134,11 @@ namespace MultiTenancyDemo.Products } } } -```` +``` -* `Change` method can be used in a **nested way**. It restores the `CurrentTenant.Id` to the previous value after the `using` statement. -* When you use `CurrentTenant.Id` inside the `Change` scope, you get the `tenantId` provided to the `Change` method. So, the repository also get this `tenantId` and can filter the database query accordingly. -* Use `CurrentTenant.Change(null)` to change scope to the host context. +- `Change` method can be used in a **nested way**. It restores the `CurrentTenant.Id` to the previous value after the `using` statement. +- When you use `CurrentTenant.Id` inside the `Change` scope, you get the `tenantId` provided to the `Change` method. So, the repository also get this `tenantId` and can filter the database query accordingly. +- Use `CurrentTenant.Change(null)` to change scope to the host context. > Always use the `Change` method with a `using` statement like done in this example. @@ -148,7 +148,7 @@ As mentioned before, ABP handles data isolation between tenants using the [Data **Example: Get count of products in the database, including all the products of all the tenants.** -````csharp +```csharp using System; using System.Threading.Tasks; using Volo.Abp.Data; @@ -181,7 +181,7 @@ namespace MultiTenancyDemo.Products } } -```` +``` See the [Data Filtering document](../../infrastructure/data-filtering.md) for more. @@ -201,15 +201,15 @@ ABP provides an extensible **Tenant Resolving** system for that purpose. Tenant The following resolvers are provided and configured by default; -* `CurrentUserTenantResolveContributor`: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for the security**. -* `QueryStringTenantResolveContributor`: Tries to find current tenant id from query string parameters. The parameter name is `__tenant` by default. -* `RouteTenantResolveContributor`: Tries to find current tenant id from route (URL path). The variable name is `__tenant` by default. If you defined a route with this variable, then it can determine the current tenant from the route. -* `HeaderTenantResolveContributor`: Tries to find current tenant id from HTTP headers. The header name is `__tenant` by default. -* `CookieTenantResolveContributor`: Tries to find current tenant id from cookie values. The cookie name is `__tenant` by default. +- `CurrentUserTenantResolveContributor`: Gets the tenant id from claims of the current user, if the current user has logged in. **This should always be the first contributor for the security**. +- `QueryStringTenantResolveContributor`: Tries to find current tenant id from query string parameters. The parameter name is `__tenant` by default. +- `RouteTenantResolveContributor`: Tries to find current tenant id from route (URL path). The variable name is `__tenant` by default. If you defined a route with this variable, then it can determine the current tenant from the route. +- `HeaderTenantResolveContributor`: Tries to find current tenant id from HTTP headers. The header name is `__tenant` by default. +- `CookieTenantResolveContributor`: Tries to find current tenant id from cookie values. The cookie name is `__tenant` by default. ###### Problems with the NGINX -You may have problems with the `__tenant` in the HTTP Headers if you're using the [nginx](https://www.nginx.com/) as the reverse proxy server. Because it doesn't allow to use underscore and some other special characters in the HTTP headers and you may need to manually configure it. See the following documents please: +You may have problems with the `__tenant` in the HTTP Headers if you're using the [nginx](https://www.nginx.com/) as the reverse proxy server. Because it doesn't allow to use underscore and some other special characters in the HTTP headers and you may need to manually configure it. See the following documents please: http://nginx.org/en/docs/http/ngx_http_core_module.html#ignore_invalid_headers http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers @@ -219,22 +219,24 @@ http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers **Example:** -````csharp +```csharp services.Configure(options => { options.TenantKey = "MyTenantKey"; }); -```` +``` -If you change the `TenantKey`, make sure to pass it to `CoreModule` in the Angular client as follows: +If you change the `TenantKey`, make sure to pass it to `provideAbpCore` via `withOptions` method in the Angular client as follows: ```js @NgModule({ - imports: [ - CoreModule.forRoot({ - // ... - tenantKey: 'MyTenantKey' - }), + providers: [ + provideAbpCore( + withOptions({ + // ... + tenantKey: "MyTenantKey", + }) + ), ], // ... }) @@ -249,7 +251,7 @@ import { TENANT_KEY } from '@abp/ng.core'; class SomeComponent { constructor(@Inject(TENANT_KEY) private tenantKey: string) {} -} +} ``` > However, we don't suggest to change this value since some clients may assume the the `__tenant` as the parameter name and they might need to manually configure then. @@ -277,12 +279,12 @@ In a real application, most of times you will want to determine the current tena **Example: Add a subdomain resolver** -````csharp +```csharp Configure(options => { options.AddDomainTenantResolver("{0}.mydomain.com"); }); -```` +``` * `{0}` is the placeholder to determine the current tenant's unique name. * Add this code to the `ConfigureServices` method of your [module](../modularity/basics.md). @@ -293,14 +295,14 @@ Openiddict is the default Auth Server in ABP (since v6.0). When you use OpenIddi ```csharp // using Volo.Abp.OpenIddict.WildcardDomains -PreConfigure(options => +PreConfigure(options => { options.EnableWildcardDomainSupport = true; options.WildcardDomainsFormat.Add("https://{0}.mydomain.com"); }); ``` -You must add this code to the `Configure` method as well. +You must add this code to the `Configure` method as well. ```csharp // using Volo.Abp.MultiTenancy; @@ -312,7 +314,7 @@ Configure(options => ``` -> There is an [example](https://github.com/abpframework/abp-samples/tree/master/DomainTenantResolver) that uses the subdomain to determine the current tenant. +> There is an [example](https://github.com/abpframework/abp-samples/tree/master/DomainTenantResolver) that uses the subdomain to determine the current tenant. If you use a sepereted Auth server, you must install `[Owl.TokenWildcardIssuerValidator](https://www.nuget.org/packages/Owl.TokenWildcardIssuerValidator)` on the `HTTPApi.Host` project @@ -332,7 +334,7 @@ context.Services options.Authority = configuration["AuthServer:Authority"]; options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]); options.Audience = "ExampleProjectName"; - + // start of added block options.TokenValidationParameters.IssuerValidator = TokenWildcardIssuerValidator.IssuerValidator; options.TokenValidationParameters.ValidIssuers = new[] @@ -348,16 +350,16 @@ context.Services You can add implement your custom tenant resolver and configure the `AbpTenantResolveOptions` in your module's `ConfigureServices` method as like below: -````csharp +```csharp Configure(options => { options.TenantResolvers.Add(new MyCustomTenantResolveContributor()); }); -```` +``` `MyCustomTenantResolveContributor` must inherit from the `TenantResolveContributorBase` (or implement the `ITenantResolveContributor`) as shown below: -````csharp +```csharp using System.Threading.Tasks; using Volo.Abp.MultiTenancy; @@ -373,7 +375,7 @@ 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. @@ -384,9 +386,9 @@ Multi-Tenancy middleware is an ASP.NET Core request pipeline [middleware](https: Multi-Tenancy middleware is typically placed just under the [authentication](https://docs.microsoft.com/en-us/aspnet/core/security/authentication) middleware (`app.UseAuthentication()`): -````csharp +```csharp app.UseMultiTenancy(); -```` +``` > This middleware is already configured in the startup templates, so you normally don't need to manually add it. @@ -406,7 +408,7 @@ The [tenant management module](../../../modules/tenant-management.md) is **inclu **Example: Define tenants in appsettings.json** -````json +```json "Tenants": [ { "Id": "446a5211-3d72-4339-9adc-845151f8ada0", @@ -422,7 +424,7 @@ The [tenant management module](../../../modules/tenant-management.md) is **inclu } } ] -```` +``` > It is recommended to **use the Tenant Management module**, which is already pre-configured when you create a new application with the ABP startup templates. diff --git a/docs/en/framework/ui/angular/localization.md b/docs/en/framework/ui/angular/localization.md index 5f8f3c4332..9f2b71bc7b 100644 --- a/docs/en/framework/ui/angular/localization.md +++ b/docs/en/framework/ui/angular/localization.md @@ -11,7 +11,7 @@ The Localization key format consists of 2 sections which are **Resource Name** a const environment = { //... localization: { - defaultResourceName: 'MyProjectName', + defaultResourceName: "MyProjectName", }, }; ``` @@ -38,7 +38,8 @@ You can also specify a default value as shown below: ```html

- {%{{{ { key: 'Resource::Key', defaultValue: 'Default Value' } | abpLocalization }}}%} + {%{{{ { key: 'Resource::Key', defaultValue: 'Default Value' } | + abpLocalization }}}%}

``` @@ -82,17 +83,17 @@ After that, you are able to use localization service. ```js this.localizationService.instant( - 'AbpIdentity::UserDeletionConfirmation', - 'John' + "AbpIdentity::UserDeletionConfirmation", + "John" ); // with fallback value this.localizationService.instant( { - key: 'AbpIdentity::UserDeletionConfirmation', - defaultValue: 'Default Value', + key: "AbpIdentity::UserDeletionConfirmation", + defaultValue: "Default Value", }, - 'John' + "John" ); // Output @@ -102,12 +103,12 @@ this.localizationService.instant( To get a localized text as [_Observable_](https://rxjs.dev/guide/observable) use `get` method instead of `instant`: ```js -this.localizationService.get('Resource::Key'); +this.localizationService.get("Resource::Key"); // with fallback value this.localizationService.get({ - key: 'Resource::Key', - defaultValue: 'Default Value', + key: "Resource::Key", + defaultValue: "Default Value", }); ``` @@ -118,41 +119,46 @@ Localizations can be determined on backend side. Angular UI gets the localizatio See an example: ```ts -// app.module.ts +import { provideAbpCore, withOptions } from '@abp/ng.core'; @NgModule({ - imports: [ - //...other imports - CoreModule.forRoot({ - localizations: [ - { - culture: 'en', - resources: [ - { - resourceName: 'MyProjectName', - texts: { - Administration: 'Administration', - HomePage: 'Home', + providers: [ + // ... + provideAbpCore( + withOptions({ + ..., + localizations: [ + { + culture: 'en', + resources: [ + { + resourceName: 'MyProjectName', + texts: { + Administration: 'Administration', + HomePage: 'Home', + }, }, - }, - ], - }, - { - culture: 'de', - resources: [ - { - resourceName: 'MyProjectName', - texts: { - Administration: 'Verwaltung', - HomePage: 'Startseite', + ], + }, + { + culture: 'de', + resources: [ + { + resourceName: 'MyProjectName', + texts: { + Administration: 'Verwaltung', + HomePage: 'Startseite', + }, }, - }, - ], - }, - ], - }), - ] + ], + }, + ], + }), + ), + ... + ], }) +export class AppModule {} ``` ...or, you can determine the localizations in a feature module: @@ -256,10 +262,10 @@ Find [styles configuration in angular.json](https://angular.io/guide/workspace-c If you have created and injected chunks for Fontawesome as seen above, you no longer need the lazy loading in the `AppComponent` which was implemented before v2.9. Simply remove them. The `AppComponent` in the template of the new version looks like this: ```js -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; @Component({ - selector: 'app-root', + selector: "app-root", template: ` @@ -274,33 +280,32 @@ Since ABP has more than one language, Angular locale files loads lazily using [W ### registerLocaleFn -`registerLocale` function that exported from `@abp/ng.core/locale` package is a higher order function that accepts `cultureNameLocaleFileMap` object and `errorHandlerFn` function as params and returns Webpack `import` function. A `registerLocale` function must be passed to the `forRoot` of the `CoreModule` as shown below: - -```js -// app.module.ts +`registerLocale` function that exported from `@abp/ng.core/locale` package is a higher order function that accepts `cultureNameLocaleFileMap` object and `errorHandlerFn` function as params and returns Webpack `import` function. A `registerLocale` function must be passed to the `withOptions` of the `provideAbpCore` as shown below: +```ts +import { provideAbpCore, withOptions } from '@abp/ng.core'; import { registerLocale } from '@abp/ng.core/locale'; -// if you have commercial license and the language management module, add the below import -// import { registerLocale } from '@volo/abp.ng.language-management/locale'; - @NgModule({ - imports: [ - // ... - CoreModule.forRoot({ - // ...other options, - registerLocaleFn: registerLocale( - // you can pass the cultureNameLocaleFileMap and errorHandlerFn as optionally - { - cultureNameLocaleFileMap: { 'pt-BR': 'pt' }, - errorHandlerFn: ({ resolve, reject, locale, error }) => { - // the error can be handled here + providers: [ + provideAbpCore( + withOptions({ + ..., + registerLocaleFn: registerLocale( + // you can pass the cultureNameLocaleFileMap and errorHandlerFn as optionally + { + cultureNameLocaleFileMap: { 'pt-BR': 'pt' }, + errorHandlerFn: ({ resolve, reject, locale, error }) => { + // the error can be handled here + }, }, - }, - ) - }), - //... - ] + ), + }), + ), + ... + ], +}) +export class AppModule {} ``` ### Mapping of Culture Name to Angular Locale File Name @@ -320,20 +325,23 @@ import { registerLocale } from '@abp/ng.core/locale'; @NgModule({ - imports: [ + providers: [ // ... - CoreModule.forRoot({ - // ...other options, - registerLocaleFn: registerLocale( - { - cultureNameLocaleFileMap: { - "DotnetCultureName": "AngularLocaleFileName", - "pt-BR": "pt" // example + provideAbpCore( + withOptions({ + ..., + registerLocaleFn: registerLocale( + { + cultureNameLocaleFileMap: { + "DotnetCultureName": "AngularLocaleFileName", + "pt-BR": "pt" // example + }, }, - }, - ) - }), - //... + ) + }), + ), + ] +}) ``` See [all locale files in Angular](https://github.com/angular/angular/tree/master/packages/common/locales). @@ -345,12 +353,12 @@ Add the below code to the `app.module.ts` by replacing `your-locale` placeholder ```js //app.module.ts -import { storeLocaleData } from '@abp/ng.core/locale'; +import { storeLocaleData } from "@abp/ng.core/locale"; import( /* webpackChunkName: "_locale-your-locale-js"*/ /* webpackMode: "eager" */ - '@angular/common/locales/your-locale.js' -).then((m) => storeLocaleData(m.default, 'your-locale')); + "@angular/common/locales/your-locale.js" +).then((m) => storeLocaleData(m.default, "your-locale")); ``` ...or a custom `registerLocale` function can be passed to the `CoreModule`: @@ -373,14 +381,17 @@ export function registerLocale(locale: string) { import { registerLocale } from './register-locale'; @NgModule({ - imports: [ + providers: [ // ... - CoreModule.forRoot({ - // ...other options, - registerLocaleFn: registerLocale - }), + provideAbpCore( + withOptions({ + ..., + registerLocaleFn: registerLocale, + }), + ), //... ] +}) ``` After this custom `registerLocale` function, since the en and fr added to the `webpackInclude`, only en and fr locale files will be created as chunks: @@ -392,4 +403,4 @@ Which locale files you add to `webpackInclude` magic comment, they will be inclu ## See Also * [Localization in ASP.NET Core](../../fundamentals/localization.md) -* [Video tutorial](https://abp.io/video-courses/essentials/localization) \ No newline at end of file +* [Video tutorial](https://abp.io/video-courses/essentials/localization) diff --git a/docs/en/framework/ui/angular/quick-start.md b/docs/en/framework/ui/angular/quick-start.md index 2a8986f965..df2d6331b9 100644 --- a/docs/en/framework/ui/angular/quick-start.md +++ b/docs/en/framework/ui/angular/quick-start.md @@ -4,8 +4,8 @@ Please follow the steps below to prepare your development environment for Angular. -1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js v16 or v18 installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system. -2. **[Optional] Install Yarn:** You may install Yarn v1 (not v2) following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 delivers an arguably better developer experience compared to npm v6 and below. You may skip this step and work with npm, which is built-in in Node.js, instead. +1. **Install Node.js:** Please visit [Node.js downloads page](https://nodejs.org/en/download/) and download proper Node.js `v18.19+` installer for your OS. An alternative is to install [NVM](https://github.com/nvm-sh/nvm) and use it to have multiple versions of Node.js in your operating system. +2. **[Optional] Install Yarn:** You may install Yarn v1.22+ (not v2) following the instructions on [the installation page](https://classic.yarnpkg.com/en/docs/install). Yarn v1 delivers an arguably better developer experience compared to npm v10 and below. You may skip this step and work with npm, which is built-in in Node.js, instead. 3. **[Optional] Install VS Code:** [VS Code](https://code.visualstudio.com/) is a free, open-source IDE which works seamlessly with TypeScript. Although you can use any IDE including Visual Studio or Rider, VS Code will most likely deliver the best developer experience when it comes to Angular projects. ABP project templates even contain plugin recommendations for VS Code users, which VS Code will ask you to install when you open the Angular project folder. Here is a list of recommended extensions: - [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=angular.ng-template) - [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) diff --git a/docs/en/get-started/layered-web-application.md b/docs/en/get-started/layered-web-application.md index 43a067ed6a..e1c00fd336 100644 --- a/docs/en/get-started/layered-web-application.md +++ b/docs/en/get-started/layered-web-application.md @@ -22,8 +22,8 @@ The following tools should be installed on your development machine: * [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) (v17.3+) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). [1](#f-editor) * [.NET 8.0+](https://dotnet.microsoft.com/en-us/download/dotnet) {{ if UI != "Blazor" }} -* [Node v16 or v18](https://nodejs.org/) -* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) [2](#f-yarn) or npm v6+ (already installed with Node) +* [Node v18.19+](https://nodejs.org/) +* [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) [2](#f-yarn) or npm v10+ (already installed with Node) {{ end }} {{ if Tiered == "Yes" }} * [Redis](https://redis.io/) (as the [distributed cache](../framework/fundamentals/caching.md)). diff --git a/docs/en/get-started/microservice.md b/docs/en/get-started/microservice.md index 9a90be00da..82db8dcc62 100644 --- a/docs/en/get-started/microservice.md +++ b/docs/en/get-started/microservice.md @@ -125,6 +125,8 @@ In the *Solution Runner* section (on the left side) you can see all the runnable > All the leaf items in the *Solution Runner* is called as an *Application* as they are executable applications. +> For a faster start process, first start the *Docker-Dependencies*, then you can start all applications. + As shown in the figure above, the executable applications are grouped into folders like `apps`, `gateways`, `infrastructure`, and `services`. You can start/stop them all, a group (folder) of them, or one by one. Before running the applications, it is good to be sure that all applications are built. To do that, right-click the root item in the *Solution Runner* and select *Build* -> *Build All* action. diff --git a/docs/en/get-started/single-layer-web-application.md b/docs/en/get-started/single-layer-web-application.md index fc86aad667..319fb19c9a 100644 --- a/docs/en/get-started/single-layer-web-application.md +++ b/docs/en/get-started/single-layer-web-application.md @@ -21,8 +21,8 @@ The following tools should be installed on your development machine: * [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) (v17.3+) for Windows / [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/). [1](#f-editor) * [.NET 8.0+](https://dotnet.microsoft.com/en-us/download/dotnet) {{ if UI != "Blazor" }} -* [Node v16 or v18](https://nodejs.org/) -* [Yarn v1.20+ (not v2)](https://classic.yarnpkg.com/en/docs/install) [2](#f-yarn) or npm v6+ (already installed with Node) +* [Node v18.19+](https://nodejs.org/) +* [Yarn v1.22+ (not v2)](https://classic.yarnpkg.com/en/docs/install) [2](#f-yarn) or npm v10+ (already installed with Node) {{ end }} {{ if Tiered == "Yes" }} * [Redis](https://redis.io/) (as the [distributed cache](../framework/fundamentals/caching.md)). diff --git a/docs/en/images/cmskit-module-markedItems.png b/docs/en/images/cmskit-module-markedItems.png new file mode 100644 index 0000000000..52eb61f285 Binary files /dev/null and b/docs/en/images/cmskit-module-markedItems.png differ diff --git a/docs/en/images/cmskit-module-markedItemsFilter.gif b/docs/en/images/cmskit-module-markedItemsFilter.gif new file mode 100644 index 0000000000..641975c892 Binary files /dev/null and b/docs/en/images/cmskit-module-markedItemsFilter.gif differ diff --git a/docs/en/images/docs-referencing.png b/docs/en/images/docs-referencing.png new file mode 100644 index 0000000000..2bc3f5da3c Binary files /dev/null and b/docs/en/images/docs-referencing.png differ diff --git a/docs/en/modules/account/authority-delegation.md b/docs/en/modules/account/authority-delegation.md index 9a973d65e0..fafe297677 100644 --- a/docs/en/modules/account/authority-delegation.md +++ b/docs/en/modules/account/authority-delegation.md @@ -77,6 +77,8 @@ You can click the `Delegate New User` button to delegate a new user: * You can specify a time range to ensure the delegation is only available within the time range. * You can make multiple delegates to the same user and set different delegate time ranges. +> You have to enter the full username to query and delegate a user. + > The delegation has three states: `Expired`, `Active`, and `Future`. These states are set automatically by checking the specified time interval. ### My delegated users @@ -87,4 +89,4 @@ You can see a list of users who delegated you to login on their behalf of them, You can click the `Login` button to log in to the application as a delegated user and go back to your account by clicking the `Back to my account` icon: -![account-pro-module-delegated-impersonate](../../images/account-pro-module-delegated-impersonate.jpg) \ No newline at end of file +![account-pro-module-delegated-impersonate](../../images/account-pro-module-delegated-impersonate.jpg) diff --git a/docs/en/modules/cms-kit/index.md b/docs/en/modules/cms-kit/index.md index ad86be8e04..5c30806028 100644 --- a/docs/en/modules/cms-kit/index.md +++ b/docs/en/modules/cms-kit/index.md @@ -17,6 +17,7 @@ The following features are currently available: * Provides a [**menu**](./menus.md) system to manage public menus dynamically. * Provides a [**global resources**](./global-resources.md) system to add global styles and scripts dynamically. * Provides a [**Dynamic Widget**](./dynamic-widget.md) system to create dynamic widgets for page and blog posts. +* Provides a [**Marked Item**](./marked-items.md) system to mark any kind of resource, like a blog post or a product, as a favorite, starred, flagged, or bookmarked. > You can click on the any feature links above to understand and learn how to use it. diff --git a/docs/en/modules/cms-kit/marked-items.md b/docs/en/modules/cms-kit/marked-items.md new file mode 100644 index 0000000000..b6b0af4173 --- /dev/null +++ b/docs/en/modules/cms-kit/marked-items.md @@ -0,0 +1,123 @@ +# CMS Kit: Marked Item System + +CMS kit provides a marking system to mark any kind of resource, like a blog post or a product, as a favorite, starred, flagged, or bookmarked. + +Marked toggling component allows users to mark your items via pre-defined icons/emojis. Here how the marked toggling components may look like: + +![markedItems](../../images/cmskit-module-markedItems.png) + + +you can also customize the marking icons shown in the toggling components. + +## Enabling the Marked Item Feature + +By default, CMS Kit features are disabled. Therefore, you need to enable the features you want, before starting to use it. You can use the [Global Feature](../../Global-Features.md) system to enable/disable CMS Kit features on development time. Alternatively, you can use the ABP Framework's [Feature System](https://docs.abp.io/en/abp/latest/Features) to disable a CMS Kit feature on runtime. + +> Check the ["How to Install" section of the CMS Kit Module documentation](Index.md#how-to-install) to see how to enable/disable CMS Kit features on development time. + +## Options + +Marking system provides a simple approach to define your entity type with mark types like favorite or starred. For example, if you want to use the marking system for products, you need to define an entity type named `product` with the icon name. + +`CmsKitMarkedItemOptions` can be configured in YourModule.cs, in the `ConfigureServices` method of your [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics). Example: + +```csharp +Configure(options => +{ + options.EntityTypes.Add( + new MarkedItemEntityTypeDefinition( + "product", + StandardMarkedItems.Favorite + ) + ); +}); +``` + +`CmsKitMarkedItemOptions` properties: + +- `EntityTypes`: List of defined entity types (`CmsKitMarkedItemOptions`) in the marking system. + +`MarkedItemEntityTypeDefinition` properties: + +- `EntityType`: Name of the entity type. +- `IconName`: The name of the icon. + +## The Marked Item widget + +The marking system provides a toggle widget to allow users to add/remove the marks from an item. You can place the widget with the item as shown below: + +``` csharp +@await Component.InvokeAsync(typeof (MarkedItemToggleViewComponent), new +{ + entityId = "...", + entityType = "product", + needsConfirmation = true // (optional) +}) +``` +* `entityType` was explained in the previous section. +* `entityId` should be the unique id of the product, in this example. If you have a Product entity, you can use its Id here. +* `needsConfirmation` An optional parameter to let the user confirm when removing the mark. + +# Internals + +## Domain Layer + +#### Aggregates + +This module follows the [Entity Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Entities) guide. + +##### UserMarkedItem + +A user markedItem represents a user has marking on the item. + +- `UserMarkedItem` (aggregate root): Represents a marked item in the system. + +#### Repositories + +This module follows the [Repository Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Repositories) guide. + +Following custom repositories are defined for this feature: + +- `IUserMarkedItemRepository` + + +#### Domain services + +This module follows the [Domain Services Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services) guide. + +##### Marked Item Manager + +`MarkedItemManager` is used to perform some operations for the `UserMarkedItem` aggregate root. + +### Application layer + +#### Application services + +- `MarkedItemPublicAppService` (implements `IMarkedItemPublicAppService`): Implements the use cases of marking system. + +### Database providers + +#### Common + +##### Table / collection prefix & schema + +All tables/collections use the `Cms` prefix by default. Set static properties on the `CmsKitDbProperties` class if you need to change the table prefix or set a schema name (if supported by your database provider). + +##### Connection string + +This module uses `CmsKit` for the connection string name. If you don't define a connection string with this name, it fallbacks to the `Default` connection string. + +See the [connection strings](https://docs.abp.io/en/abp/latest/Connection-Strings) documentation for details. + +#### Entity Framework Core + +##### Tables + +- CmsUserMarkedItems + +#### MongoDB + +##### Collections + +- **CmsUserMarkedItems** + diff --git a/docs/en/modules/docs.md b/docs/en/modules/docs.md index 6aaf1f2f0a..6ab4f566c0 100644 --- a/docs/en/modules/docs.md +++ b/docs/en/modules/docs.md @@ -666,9 +666,61 @@ See the following examples: ``` ``` ---- +## Referencing Next & Previous Documents +The **Docs Module** supports referencing previous and next documents. It's useful if you have a series of documents that are strictly related to each other and need to be followed one after the other. +To reference the previous and next documents from a document, you should specify the documentation titles and their paths as follows: + +``` + + ````json + //[doc-nav] + { + "Previous": { + "Name": "Overall", + "Path": "testing/overall" + }, + "Next": { + "Name": "Integration tests", + "Path": "testing/integration-tests" + } + } + ```` + +``` + +After you specify the next & previous documents, they will appear at the end of the current documentation like in the following figure: + +![](../images/docs-referencing.png) + +## Single Project Mode + +The **single project mode** allows you to use a single name as a project name in your application. If you are not considering supporting multiple projects with their multiple docs and instead if you have a single project and want to have documentation only for it, it's especially useful for you. + +You just need to configure the `DocsUiOptions`, set the single project mode as **enabled** and also define a constant project name: + +```csharp +Configure(options => +{ + options.RoutePrefix = "docs"; + options.SingleProjectMode.Enable = true; + options.SingleProjectMode.ProjectName = "abp"; +}); +``` + +## Multi Language Mode + +The **multi language mode** allows you to show a combobox that lists and shows all documentation languages and configures the related languages in routes. + +It's enabled by default and supports multiple languages, but if you are considering only supporting a single language, and don't want to show the language combobox in the sidebar of your docs system, you can configure the `DocsUiOptions` and set the multi language mode support as **false** to disable it: + +```csharp +Configure(options => +{ + options.MultiLanguageMode = false; +}); +``` ## See Also diff --git a/docs/en/modules/openiddict-pro.md b/docs/en/modules/openiddict-pro.md index e2c2715d62..b9102cee6d 100644 --- a/docs/en/modules/openiddict-pro.md +++ b/docs/en/modules/openiddict-pro.md @@ -415,6 +415,17 @@ public override void PreConfigureServices(ServiceConfigurationContext context) } ``` +### Disable Transport Security Requirement + +By default, OpenIddict requires the use of HTTPS for all endpoints. You can disable it if it's needed. You just need to configure the `OpenIddictServerAspNetCoreOptions` and set `DisableTransportSecurityRequirement` as **true**: + +```cs +Configure(options => +{ + options.DisableTransportSecurityRequirement = true; +}); +``` + https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption ### Request/Response Process diff --git a/docs/en/modules/openiddict.md b/docs/en/modules/openiddict.md index 0d126b42c0..80f1a54776 100644 --- a/docs/en/modules/openiddict.md +++ b/docs/en/modules/openiddict.md @@ -375,6 +375,17 @@ public override void PreConfigureServices(ServiceConfigurationContext context) } ``` +### Disable Transport Security Requirement + +By default, OpenIddict requires the use of HTTPS for all endpoints. You can disable it if it's needed. You just need to configure the `OpenIddictServerAspNetCoreOptions` and set `DisableTransportSecurityRequirement` as **true**: + +```cs +Configure(options => +{ + options.DisableTransportSecurityRequirement = true; +}); +``` + https://documentation.openiddict.com/configuration/token-formats.html#disabling-jwt-access-token-encryption ### Request/Response Process diff --git a/docs/en/release-info/migration-guides/abp-8-3.md b/docs/en/release-info/migration-guides/abp-8-3.md new file mode 100644 index 0000000000..50a7f274a4 --- /dev/null +++ b/docs/en/release-info/migration-guides/abp-8-3.md @@ -0,0 +1,68 @@ +# ABP Version 8.3 Migration Guide + +This document is a guide for upgrading ABP v8.x solutions to ABP v8.3. There are some changes in this version that may affect your applications, please read it carefully and apply the necessary changes to your application. + +## Open-Source (Framework) + +### Added `AdminUserName` parameter to `IIdentityDataSeeder.SeedAsync` method + +Prior to this version, `IIdentityDataSeeder.SeedAsync` was used to seed the initial user data with the `admin` as the username, to be able to easily test all applications with all permissions granted. + +In this version on, we allow you to set the default **adminUserName**. So, you can set the default user name to any name you want or leave it as it is and let the username be `admin` as before. + +For example, in the `MyProjectNameDbMigrationService.SeedDataAsync` method (under the domain project's **Data** folder), you can pass the username as below: + +```diff + private async Task SeedDataAsync(Tenant? tenant = null) + { + Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed..."); + + await _dataSeeder.SeedAsync(new DataSeedContext(tenant?.Id) + .WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, IdentityDataSeedContributor.AdminEmailDefaultValue) + .WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, IdentityDataSeedContributor.AdminPasswordDefaultValue) ++ .WithProperty(IdentityDataSeedContributor.AdminUserNamePropertyName, "your-admin-user-name") + ); + } +``` + +Normally, you don't need to make any changes in your code related to this change. However, if you used the `IIdentityDataSeeder.SeedAsync` method in your application, now also gets additional (and optional) `adminUserName` parameter that you can pass the value on. + +See the PR for more info: https://github.com/abpframework/abp/pull/20131/ + +### Docs Module: Improvements & Single Project Mode Support + +In this version, we made some improvements in the [Docs Module](../../modules/docs.md) and introduced single project mode, which allows you to use a single project name for all of your documents. + +While implementing this new feature, we made some small changes to the existing classes. For example, the constructor methods of the following classes have been changed, and need to be updated in your code, if you use them: + +* `TreeTagHelper.cs`: https://github.com/abpframework/abp/pull/19419/files#diff-e0fb91c1b564d61dedf9dfc60d08e7e0af57b433797fcb3bd664f3fd4768ea0d + +* `MarkdownDocumentToHtmlConverter.cs`: https://github.com/abpframework/abp/pull/19419/files#diff-948e25b2d8851576888f8053b5f2c9416e23576aff95a88ef8ec8ca2841b3622 + +See the PR for more info: https://github.com/abpframework/abp/pull/19419 + +### Updated `datatables.net` to 2.0.2 + +In this version, the `datatables.net` library version was updated from v1.11.4 to v2.0.2. We made all related changes at the framework level and updated the dependency and its resource mapping configurations. Also, we made it backward compatible so it should not directly affect your application. + +However, notice this package is used by the `@abp/aspnetcore.mvc.ui.theme.shared` package, which is used by the official themes. Therefore, if you used any methods from the **datatables.net** library it might be already deprecated or removed. + +See the PR for more info: https://github.com/abpframework/abp/pull/19340 + +### Angular UI + +We wish to formally inform you that ABP version 8.3 has been upgraded to incorporate Angular version 18. Consequently, we recommend migrating your application to [Angular v18](https://angular.dev/update-guide) to ensure compatibility. + +## PRO + +> Please check the **Open-Source (Framework)** section before reading this section. The listed topics might affect your application and you might need to take care of them. + +If you are a paid-license owner and using the ABP's paid version, then please follow the following sections to get informed about the breaking changes and apply the necessary ones: + +### Added RegEx Support to CMS Kit's (PRO) URL Forwarding System + +ABP provides a [URL Forwarding System](../../modules/cms-kit-pro/url-forwarding.md), which allows you to create URLs that redirect to other pages or external websites. Prior to this version, the URL Forwarding System was only working for static URLs and didn't support RegEx. In this version, we introduced the RegEx support for the URL Forwarding System and now you can pass regular expressions for not static URLs. + +Since a new property has been added to the `CmsShortenedUrl` entity, you should create a new migration and apply it to your database. + +> After creating a new migration and applying it to your database, typically you don't need to make any changes, however, if you have overridden a page/method or class related to the URL Forwarding System, you might need to update it accordingly. For this purpose, you can get the source code of the [CMS Kit Module's](../../modules/cms-kit-pro/index.md) and update the related parts in your application. \ No newline at end of file diff --git a/docs/en/release-info/migration-guides/index.md b/docs/en/release-info/migration-guides/index.md index 6670e9ba3a..804b17b376 100644 --- a/docs/en/release-info/migration-guides/index.md +++ b/docs/en/release-info/migration-guides/index.md @@ -2,6 +2,7 @@ The following documents explain how to migrate your existing ABP applications. We write migration documents only if you need to take an action while upgrading your solution. Otherwise, you can easily upgrade your solution using the [abp update command](../upgrading.md). +- [8.x to 8.3](abp-8-3.md) - [8.1 to 8.2](abp-8-2.md) - [8.0 to 8.1](abp-8-1.md) - [7.x to 8.0](abp-8-0.md) diff --git a/docs/en/release-info/release-notes.md b/docs/en/release-info/release-notes.md index d091a5840b..a94ed9d0b8 100644 --- a/docs/en/release-info/release-notes.md +++ b/docs/en/release-info/release-notes.md @@ -4,11 +4,20 @@ This document contains **brief release notes** for each release. Release notes o > If you want to read detailed release posts for each version, please see the [official ABP blog](https://abp.io/blog). -## 8.2 (2024-05-25) +## 8.3.0-rc.1 (2024-07-31) -> This version is currently in preview. The final release date is planned for July, 2024. +> This version is currently in preview. The final release date is planned for August, 2024. -See the detailed **[blog post / announcement](https://abp.io/blog/announcing-abp-8-2-release-candidate)** for the v8.2 release. +See the detailed **[blog post / announcement](https://abp.io/blog/announcing-abp-8-3-release-candidate)** for the v8.3 release. + +* CMS Kit: Marked Items & Approvement System for Commenting Feature +* Enhancements on the Docs Module (Google Translation support & new single project mode) +* Using DbFunction for generating more precise SQL commands for Global Query Filters +* CMS Kit (Pro): FAQ System + +## 8.2 (2024-06-26) + +See the detailed **[blog post / announcement](https://abp.io/blog/announcing-abp-8-2-stable-release)** for the v8.2 release. * Blazor Full-Stack Web App UI. * Introducing the `IBlockUiService` for Blazor UI (disables/blocks the page or a part of the page). diff --git a/docs/en/release-info/road-map.md b/docs/en/release-info/road-map.md index 6c14efefda..82da0a0b8e 100644 --- a/docs/en/release-info/road-map.md +++ b/docs/en/release-info/road-map.md @@ -4,55 +4,119 @@ This document provides a road map, release schedule, and planned features for th ## Next Versions -### v8.3 - -The next version will be 8.3 and we will be mostly working on the following topics: - -* CMS Kit: Marked Items/Favorites & Approvement System for Comments -* Docs Module: Added Google Translation Support & Introducing the Single Project Mode -* Using DBFunction for Global Query Filters -* Upgrade Angular Version to v18.1.x -* Improvements on the existing features and provide more guides... - -See the [8.3 milestone](https://github.com/abpframework/abp/milestone/106) for all the issues we've planned to work on. +### v9.0 + +The next version will be 9.0 and planned to release the stable 9.0 version in November 2024. We will be mostly working on the following topics: + +* Framework + * Upgrading to .NET 9.0 + * Google Cloud Storage BLOB Provider + * Better handling localization resources in a microservice scenario + * SSR support for the Angular UI + * Upgrading 3rd-party dependencies + +* ABP Suite + * Multiple navigation properties to the same target entity + * Define navigation properties without target string property dependency + * Improvements one-to-many scenarios + * Access to default code generation templates for customized templates + +* ABP Studio + * Blazor WebApp UI option for the new startup templates + * Execution order (dependency management) for Solution Runner + * Automate more details on new service creation for a microservice solution + * Support multiple concurrent Kubernetes deployment/integration scenarios + * Show the README file when you create a new solution or open an existing solution + * Improve ABP Suite code-generation possibilities for microservice solutions + * Improve the module installation experience + * Auto-install 3rd-party dependencies + * Better handle long log files + * Allow to directly create new solutions with ABP's RC (Release Candidate) versions + * Support Intel processors for Mac computers, support ARM chipset for Windows and support Linux OS + * Improve client proxy generation experience + +* Application modules + * Account module: Support mixed social/local login scenarios + * UI/UX improvements on existing application modules + +* New tutorials + * Modular monolith development + * Microservice development ## Backlog Items -The *Next Versions* section above shows the main focus of the planned versions. However, in each release, we add new features to the core framework and the [application modules](../modules). - -Here is a list of major items in the backlog we are considering working on in the next versions. +The *Next Versions* section above shows the main focus of the planned versions. However, in each release, we add new features to the ABP platform. ### Framework -* [#86](https://github.com/abpframework/abp/issues/86) / GrapQL Integration +The ABP framework is [open source](https://github.com/abpframework/abp) and free for everyone. You can see its [public backlog](https://github.com/abpframework/abp/milestone/2). Here, are some of the important features you can expect from next versions: + * [#236](https://github.com/abpframework/abp/issues/236) / Resource based authorization system -* [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a gRPC integration infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](../modules)) +* [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a gRPC integration infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](../modules/index.md)) * [#57](https://github.com/abpframework/abp/issues/57) / Built-in CQRS infrastructure +* [#58](https://github.com/abpframework/abp/issues/58) / Content localization system (multilingual entities) * [#4223](https://github.com/abpframework/abp/issues/4223) / WebHook system * [#162](https://github.com/abpframework/abp/issues/162) / Azure ElasticDB Integration for multitenancy * [#2296](https://github.com/abpframework/abp/issues/2296) / Feature toggling infrastructure -* [#16342](https://github.com/abpframework/abp/issues/16342) / CMS Kit: Meta information for SEO -* [#16260](https://github.com/abpframework/abp/issues/16260) / GCP Blob Storage Provider * [#15932](https://github.com/abpframework/abp/issues/15932) / Introduce ABP Diagnostics Module -* [#16756](https://github.com/abpframework/abp/issues/16756) / Blob Storing - Provider configuration UI * [#16744](https://github.com/abpframework/abp/issues/16744) / State Management API - -### Modules / Themes - -* New styles, components and features for the LeptonX theme. -* Payment module: Invoice system. -* Dynamic dashboard system. -* User guiding module. -* gRPC integration and implementation for all the pre-built modules. -* Chat Module: Deleting messages & conversations - -### Tooling - -* Suite: File, DateOnly and TimeOnly types -* Suite: Export child/detail entity records -* CMS Kit features, including FAQ, spam protection, social media feeds, multi-language support, and so on. - -You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones) for a detailed road map. The backlog items are subject to change. We are adding new items and changing priorities based on the community feedbacks and goals of the project. +* [#17815](https://github.com/abpframework/abp/issues/17815) / Operation Rate Limiting + +### Application Modules / UI Themes + +ABP Platform provides many (free and commercial) [pre-built application modules](../modules/index.md) and modern [UI themes](../ui-themes/index.md). In every release, many enhancements and bugfixes are already done on the existing modules. In addition, here some of the planned features for next versions: + +* LeptonX theme: New layouts, styles and components +* CMS Kit module: Meta information for SEO, media gallery, RSS feed, content versioning, social media streams +* Identity module: Idle session warning +* Payment module: Invoice system +* New module: User notification +* New module: Dynamic dashboard +* New module: User guiding +* New module: Keycloak integration + +### ABP Studio + +[ABP Studio](../studio/index.md) is a cross-platform desktop application for ABP and .NET developers to simplify and automate daily tasks of developers. It has a community (free) edition as well as commercial editions. It is released and versioned independently from the ABP platform and frequently released. + +Here, are some of the important planned features for next ABP Studio versions: + +* Theme builder for the LeptonX theme +* Analyze user solutions to explore entities, domain services, application services, pages and other fundamental objects. +* Add status bar to the built-in browser to easily see errors +* Swagger authentication support for the built-in browser +* Show related requests/events (traces) together in the solution runner panel +* Integrate common tool dashboards into ABP Studio (such a Garana, Redis, RabbitMQ, Kibana, etc) +* Built-in command terminal +* Automate all steps of new service creation for microservice solutions +* Container application type support for Solution Runner (to individually control docker dependencies) +* More options while creating new solutions +* Downloading samples in ABP studio +* Built-in ABP documentation experience +* Auto-execute terminal commands in markdown files +* Compare changed on the startup templates when a new ABP version is published +* Remove unused projects while downloading source code of an existing module +* Testing/hosting applications for module templates +* Easily explore all module and package dependencies of a large solution +* Built-in deployment options +* Rapid application development features +* ABP support integration + +### ABP Suite + +[ABP Suite](../suite/index.md) is a GUI application that is mainly used to generate CRUD style pages in your application. You define your entity and it can generate all the code from the database layer to the UI layer. The generated code is clean and a perfect starting point to implement your custom requirements on top of it. + +Here, are some of the important planned features for next ABP Suite versions: + +* Handle image properties for entities +* Allow to define extra properties for DTOs those are not a part of the entity +* Allow to create pages instead of modals for CRUD page generation +* View-only (detail view) modal/page for an entity +* Export child/detail entity records as a part of export operation for a main (master) entity +* Allow to accept attachments (files) for an entity +* Allow to add custom entity actions for an entity +* Allow to inherit from an existing entity class +* Custom form layouts on CRUD page generation ## Feature Requests diff --git a/docs/en/solution-templates/microservice/adding-new-api-gateways.md b/docs/en/solution-templates/microservice/adding-new-api-gateways.md index a026f6e884..415452a3bd 100644 --- a/docs/en/solution-templates/microservice/adding-new-api-gateways.md +++ b/docs/en/solution-templates/microservice/adding-new-api-gateways.md @@ -155,12 +155,6 @@ We should configure the AuthServer for **CORS** and **RedirectAllowedUrls**. } ``` -### Add the New Gateway to the Solution Runner - -We should add the new gateway to the solution runner [profile](../../studio/running-applications.md#profile) for running applications in the ABP Studio. You can follow the steps explained in the [Solution Runner](../../studio/running-applications.md#c-application) document to add the new gateway to the solution runner profile. Afterwards, you can start the new gateway by selecting it in the solution runner. - -![public-gateway-solution-runner](images/public-gateway-solution-runner.png) - ## Creating Helm Chart for the New Gateway If you want to deploy the new gateway to Kubernetes, you should create a Helm chart for the new application. diff --git a/docs/en/solution-templates/microservice/adding-new-applications.md b/docs/en/solution-templates/microservice/adding-new-applications.md index 7e3a53cbae..352b18f763 100644 --- a/docs/en/solution-templates/microservice/adding-new-applications.md +++ b/docs/en/solution-templates/microservice/adding-new-applications.md @@ -120,12 +120,6 @@ Add the new application URL to the `appsettings.json` file in the `Identity` ser } ``` -### Add the New Application to the Solution Runner - -We should add the new application to the solution runner [profile](../../studio/running-applications.md#profile) for running applications in the ABP Studio. You can follow the steps explained in the [Solution Runner](../../studio/running-applications.md#c-application) document to add the new application to the solution runner profile. Afterwards, you can start the new application by selecting it in the solution runner. - -![web-public-solution-runner](images/web-public-solution-runner.png) - ## Docker Configuration for Prometheus If you want to monitor the new application with Prometheus when you debug the solution, you should add the new application to the `prometheus.yml` file in the `etc/docker/prometheus` folder. You can copy the configurations from the existing applications and modify them according to the new application. Below is an example of the `prometheus.yml` file for the `WebPublic` application. diff --git a/docs/en/solution-templates/microservice/adding-new-microservices.md b/docs/en/solution-templates/microservice/adding-new-microservices.md index 2ff864cd0d..458457a109 100644 --- a/docs/en/solution-templates/microservice/adding-new-microservices.md +++ b/docs/en/solution-templates/microservice/adding-new-microservices.md @@ -16,6 +16,8 @@ This document explains how to add new microservices to the microservice solution Additionally, there is a folder named `_templates` in the root directory. This folder contains templates you can use to create new microservices, API gateways, and applications. These templates can be customized according to your needs. +![folder-structure](images/folder-structure.png) + ## Adding a New Microservice To add a new microservice to the solution, you can use the `service_nolayers` template. This template creates a new ASP.NET Core application with the necessary configurations and dependencies. Follow the steps below to add a new microservice: @@ -40,29 +42,34 @@ The new microservice is created and added to the solution. You can see the new m The new microservice is created with the necessary configurations and dependencies. We should configure several sections by modifying the `appsettings.json` file: * Set the `Administration` & `AbpBlobStoring` connection strings. - * Set the correct `StringEncryption` key. * Set the `CorsOrigins` to allow the web gateway to access the microservice. * Set the `AuthServer` configurations to enable the microservice to authenticate and authorize users. + * Set the correct `StringEncryption` key. You can copy the configurations from the existing microservices and modify them according to the new microservice. Below is an example of the `appsettings.json` file for the `ProductService` microservice. -```json +```diff { "ConnectionStrings": { - "Administration": "Server=localhost,1434; User Id=sa; Password=myPassw@rd; Database=Bookstore_Administration; TrustServerCertificate=true", - "AbpBlobStoring": "Server=localhost,1434; User Id=sa; Password=myPassw@rd; Database=Bookstore_BlobStoring; TrustServerCertificate=true", +- "Administration": "Administration_Database", +- "AbpBlobStoring": "BlobStoring_Database", ++ "Administration": "Server=localhost,1434; User Id=sa; Password=myPassw@rd; Database=Bookstore_Administration; TrustServerCertificate=true", ++ "AbpBlobStoring": "Server=localhost,1434; User Id=sa; Password=myPassw@rd; Database=Bookstore_BlobStoring; TrustServerCertificate=true", "ProductService": "Server=localhost,1434; User Id=sa; Password=myPassw@rd; Database=Bookstore_ProductService; TrustServerCertificate=true" }, "App": { - "CorsOrigins": "http://localhost:44333", +- "CorsOrigins": "http://localhost:webgateway_port", ++ "CorsOrigins": "http://localhost:44333", "EnablePII": false }, "Swagger": { "IsEnabled": true }, "AuthServer": { - "Authority": "http://localhost:44387", - "MetaAddress": "http://localhost:44387", +- "Authority": "http://localhost:authserver_port", +- "MetaAddress": "http://localhost:authserver_port", ++ "Authority": "http://localhost:44387", ++ "MetaAddress": "http://localhost:44387", "RequireHttpsMetadata": "false", "SwaggerClientId": "SwaggerTestUI", "Audience": "ProductService" @@ -93,24 +100,25 @@ You can copy the configurations from the existing microservices and modify them "Url": "http://localhost:9200" }, "StringEncryption": { - "DefaultPassPhrase": "PDAWjbshpwlOwNB6" +- "DefaultPassPhrase": "string_encrryption_key" ++ "DefaultPassPhrase": "PDAWjbshpwlOwNB6" } } ``` ### Configuring the OpenId Options -We should configure the OpenId options by modifying the `OpenIddictDataSeeder` in the `Identity` service. Below is an example of the `OpenIddictDataSeeder` options for the `ProductService` microservice. +We should configure the OpenId options by modifying the `OpenIddictDataSeeder` in the `Identity` service. Below is an example of the `OpenIddictDataSeeder` options for the `Product` microservice. Create API scopes and add the required API scope for Swagger clients in the `CreateApiScopesAsync` and `CreateSwaggerClientsAsync` methods in the `OpenIddictDataSeeder` class. -```csharp +```diff private async Task CreateApiScopesAsync() { await CreateScopesAsync("AuthServer"); await CreateScopesAsync("IdentityService"); await CreateScopesAsync("AdministrationService"); - await CreateScopesAsync("ProductService"); // new service ++ await CreateScopesAsync("ProductService"); } private async Task CreateSwaggerClientsAsync() @@ -120,21 +128,21 @@ private async Task CreateSwaggerClientsAsync() "AuthServer", "IdentityService", "AdministrationService", - "ProductService" // new service ++ "ProductService" }); } ``` Add the redirect URL for the new service in the `CreateSwaggerClientAsync` method. -```csharp +```diff private async Task CreateSwaggerClientAsync(string clientId, string[] scopes) { ... ... ... var administrationServiceRootUrl = _configuration["OpenIddict:Resources:AdministrationService:RootUrl"]!.TrimEnd('/'); - var productServiceServiceRootUrl = _configuration["OpenIddict:Resources:ProductService:RootUrl"]!.TrimEnd('/'); // new service ++ var productServiceRootUrl = _configuration["OpenIddict:Resources:ProductService:RootUrl"]!.TrimEnd('/'); await CreateOrUpdateApplicationAsync( name: clientId, @@ -152,15 +160,15 @@ private async Task CreateSwaggerClientAsync(string clientId, string[] scopes) $"{authServerRootUrl}/swagger/oauth2-redirect.html", $"{identityServiceRootUrl}/swagger/oauth2-redirect.html", $"{administrationServiceRootUrl}/swagger/oauth2-redirect.html", - $"{productServiceServiceRootUrl}/swagger/oauth2-redirect.html", // new service ++ $"{productServiceRootUrl}/swagger/oauth2-redirect.html" } ); } ``` -Add the allowed scope for the web (front-end) application(s) in the `CreateClientsAsync` method. You might have different clients for different UI applications such as web, Angular, React, etc. Ensure you add the new service to the allowed scopes of these clients. +Add the allowed scope for the web (front-end) application(s) in the `CreateClientsAsync` method. You might have different clients for different UI applications such as web, Angular, React, etc. Ensure you add the new microservice to the allowed scopes of these clients. -```csharp +```diff private async Task CreateClientsAsync() { var commonScopes = new List @@ -192,7 +200,7 @@ private async Task CreateClientsAsync() "SaasService", "AuditLoggingService", "AdministrationService", - "ProductService" // new service ++ "ProductService" }).ToList(), redirectUris: new List { $"{webClientRootUrl}signin-oidc" }, postLogoutRedirectUris: new List() { $"{webClientRootUrl}signout-callback-oidc" }, @@ -202,110 +210,130 @@ private async Task CreateClientsAsync() } ``` -Add the new service URL to the `appsettings.json` file in the `Identity` service. +Add the new service URL to the `appsettings.json` file in the `Identity` microservice. In this example we're gonna edit the *Acme.Bookstore.IdentityService* project `appsettings.json` file. -```json +```diff "OpenIddict": { "Applications": { ... }, "Resources": { ... - "ProductService": { - "RootUrl": "http://localhost:44350" - } ++ "ProductService": { ++ "RootUrl": "http://localhost:44350" ++ } } } ``` ### Configuring the AuthServer -We should configure the AuthServer for **CORS** and **RedirectAllowedUrls**. +We should configure the *AuthServer* application `appsettings.json` file for the **CorsOrigins** and **RedirectAllowedUrls** sections. -```json +```diff +... "App": { "SelfUrl": "http://localhost:***", - "CorsOrigins": "...... ,http://localhost:44350", +- "CorsOrigins": "http://localhost:44358,..", ++ "CorsOrigins": "http://localhost:44358,..,http://localhost:44350", "EnablePII": false, - "RedirectAllowedUrls": "...... ,http://localhost:44350" -} +- "RedirectAllowedUrls": "http://localhost:44358,..", ++ "RedirectAllowedUrls": "http://localhost:44358,..,http://localhost:44350" +}, +... ``` ### Configuring the API Gateway -We should configure the API Gateway to allow the web gateway to access the new microservice. First, we should add the **ProductService** sections to the `appsettings.json` file in the `WebGateway` project. +We should configure the API Gateway to access the new microservice. First, add the **Product** sections to the `appsettings.json` file in the `WebGateway` project. In this example we're gonna edit the *Acme.Bookstore.WebGateway* project `appsettings.json` file. -```json +```diff "ReverseProxy": { "Routes": { ... - "ProductService": { - "ClusterId": "ProductService", - "Match": { - "Path": "/api/productservice/{**catch-all}" - } - }, - "ProductServiceSwagger": { - "ClusterId": "ProductService", - "Match": { - "Path": "/swagger-json/ProductService/swagger/v1/swagger.json" - }, - "Transforms": [ - { "PathRemovePrefix": "/swagger-json/ProductService" } - ] - } ++ "Product": { ++ "ClusterId": "Product", ++ "Match": { ++ "Path": "/api/product/{**catch-all}" ++ } ++ }, ++ "ProductSwagger": { ++ "ClusterId": "Product", ++ "Match": { ++ "Path": "/swagger-json/Product/swagger/v1/swagger.json" ++ }, ++ "Transforms": [ ++ { "PathRemovePrefix": "/swagger-json/Product" } ++ ] ++ } }, "Clusters": { ... - "ProductService": { - "Destinations": { - "ProductService": { - "Address": "http://localhost:44350/" - } - } - } ++ "Product": { ++ "Destinations": { ++ "Product": { ++ "Address": "http://localhost:44350/" ++ } ++ } ++ } } } ``` -Afterwards, open the `ProjectNameWebGatewayModule` class in the `WebGateway` and add the `ProductService` to the `ConfigureSwaggerUI` method. +Afterwards, open the `ProjectNameWebGatewayModule` class in the `WebGateway` project and add the `ProductService` to the `ConfigureSwaggerUI` method. In this example we're gonna edit the `BookstoreWebGatewayModule` file. + +```diff +private static void ConfigureSwaggerUI( + IProxyConfig proxyConfig, + SwaggerUIOptions options, + IConfiguration configuration) +{ + foreach (var cluster in proxyConfig.Clusters) + { + options.SwaggerEndpoint($"/swagger-json/{cluster.ClusterId}/swagger/v1/swagger.json", $"{cluster.ClusterId} API"); + } -```csharp -options.OAuthScopes( - "AdministrationService", - "AuthServer", - "IdentityService", - "ProductService" // new service -); + options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]); + options.OAuthScopes( + "AdministrationService", + "AuthServer", + ..., ++ "ProductService" + ); +} ``` ### Configuring the UI Services -We should configure the UI application(s) to allow the new microservice to access through the web gateway. To do this, we should add the new service scope to the `ConfigureAuthentication` method in the `ProjectName...Module` class in the `Web` or `Blazor` application. +We should configure the UI application(s) to allow the new microservice to access through the web gateway. To do this, we should add the new microservice scope to the `ConfigureAuthentication` method in the `ProjectName...Module` class in the `Web` or `Blazor` application. In this example we're gonna edit the `BookstoreWebModule` file. -```csharp -context.Services.AddAuthentication(options => -{ - options.DefaultScheme = "Cookies"; - options.DefaultChallengeScheme = "oidc"; -}) -.AddCookie("Cookies", options => -{ - options.ExpireTimeSpan = TimeSpan.FromDays(365); -}) -.AddAbpOpenIdConnect("oidc", options => +```diff +private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration) { - ... - options.Scope.Add("AuthServer"); - options.Scope.Add("IdentityService"); - options.Scope.Add("AdministrationService"); - options.Scope.Add("ProductService"); // new servie -}); + context.Services.AddAuthentication(options => + { + options.DefaultScheme = "Cookies"; + options.DefaultChallengeScheme = "oidc"; + }) + .AddCookie("Cookies", options => + { + options.ExpireTimeSpan = TimeSpan.FromDays(365); + }) + .AddAbpOpenIdConnect("oidc", options => + { + ... + options.Scope.Add("AuthServer"); + options.Scope.Add("IdentityService"); + options.Scope.Add("AdministrationService"); ++ options.Scope.Add("ProductService"); + }); + ... +} ``` Similarly, if you have an Angular application, you should add the new service scope to the `oAuthConfig` in `environment.ts`: -```typescript +```diff const baseUrl = 'http://localhost:4200'; const oAuthConfig = { @@ -313,69 +341,83 @@ const oAuthConfig = { redirectUri: baseUrl, clientId: 'Angular', responseType: 'code', - scope: 'openid profile email roles AuthServer IdentityService AdministrationService ProductService', // new service +- scope: 'openid profile email roles AuthServer IdentityService AdministrationService', ++ scope: 'openid profile email roles AuthServer IdentityService AdministrationService ProductService', requireHttps: false }; ``` -### Add the New Microservice to the Solution Runner - -We should add the new microservice to the solution runner [profile](../../studio/running-applications.md#profile) for running applications in the ABP Studio. You can follow the steps explained in the [Solution Runner](../../studio/running-applications.md#c-application) document to add the new microservice to the solution runner profile. Afterwards, you can start the new microservice by selecting it in the solution runner. - -![product-service-solution-runner](images/product-service-solution-runner.png) - ## Docker Configuration for Prometheus -If you want to monitor the new microservice with Prometheus when you debug the solution, you should add the new microservice to the `prometheus.yml` file in the `etc/docker/prometheus` folder. You can copy the configurations from the existing microservices and modify them according to the new microservice. Below is an example of the `prometheus.yml` file for the `ProductService` microservice. +If you want to monitor the new microservice with Prometheus when you debug the solution, you should add the new microservice to the `prometheus.yml` file in the `etc/docker/prometheus` folder. You can copy the configurations from the existing microservices and modify them according to the new microservice. Below is an example of the `prometheus.yml` file for the `Product` microservice. -```yml - - job_name: 'product' +```diff + - job_name: 'authserver' scheme: http metrics_path: 'metrics' static_configs: - - targets: ['host.docker.internal:44350'] + - targets: ['host.docker.internal:44398'] + ... ++ - job_name: 'product' ++ scheme: http ++ metrics_path: 'metrics' ++ static_configs: ++ - targets: ['host.docker.internal:44350'] ``` ## Creating Helm Chart for the New Microservice If you want to deploy the new microservice to Kubernetes, you should create a Helm chart for the new microservice. -First, we need to add the new microservice to the `build-all-images.ps1` script in the `etc/helm` folder. You can copy the configurations from the existing microservices and modify them according to the new microservice. Below is an example of the `build-all-images.ps1` script for the `ProductService` microservice. +First, we need to add the new microservice to the `build-all-images.ps1` script in the `etc/helm` folder. You can copy the configurations from the existing microservices and modify them according to the new microservice. Below is an example of the `build-all-images.ps1` script for the `Product` microservice. -```powershell -./build-image.ps1 -ProjectPath "../../services/product/Acme.Bookstore.ProductService/Acme.Bookstore.ProductService.csproj" -ImageName bookstore/productservice +```diff +... + ./build-image.ps1 -ProjectPath "../../apps/auth-server/Acme.Bookstore.AuthServer/Acme.Bookstore.AuthServer.csproj" -ImageName bookstore/authserver ++ ./build-image.ps1 -ProjectPath "../../services/product/Acme.Bookstore.ProductService/Acme.Bookstore.ProductService.csproj" -ImageName bookstore/product ``` -Then, we need to add the connection string to the `values.projectname-local.yaml` file in the `etc/helm/projectname` folder. Below is an example of the `values.bookstore-local.yaml` file for the `ProductService` microservice. +Then, we need to add the connection string to the `values.projectname-local.yaml` file in the `etc/helm/projectname` folder. Below is an example of the `values.bookstore-local.yaml` file for the `Product` microservice. -```yaml +```diff global: ... connectionStrings: ... - productService: "Server=[RELEASE_NAME]-sqlserver,1433; Database=Bookstore_ProductService; User Id=sa; Password=myPassw@rd; TrustServerCertificate=True" ++ product: "Server=[RELEASE_NAME]-sqlserver,1433; Database=Bookstore_ProductService; User Id=sa; Password=myPassw@rd; TrustServerCertificate=True" ``` -Afterwards, we need to create a new Helm chart for the new microservice. You can copy the configurations from the existing microservices and modify them according to the new microservice. Below is an example of the `productservice` Helm chart for the `ProductService` microservice. +Afterwards, we need to create a new Helm chart for the new microservice. You can copy the configurations from the existing microservices and modify them according to the new microservice. Below is an example of the `product` Helm chart for the `Product` microservice. + +Product microservice `values.yaml` file. {%{ ```yaml -# values.yaml image: - repository: "bookstore/productservice" + repository: "bookstore/product" tag: "latest" pullPolicy: IfNotPresent swagger: isEnabled: "true" +``` +}%} -# Chart.yaml +Product microservice `Chart.yaml` file. + +{%{ +```yaml apiVersion: v2 -name: productservice +name: product version: 1.0.0 appVersion: "1.0" description: Bookstore Product Service +``` +}%} + +Product microservice `product.yaml` file. -# product.yaml +{%{ +```yaml apiVersion: apps/v1 kind: Deployment metadata: @@ -404,10 +446,15 @@ spec: - name: "ConnectionStrings__AbpBlobStoring" value: "{{ .Values.global.connectionStrings.blobStoring | replace "[RELEASE_NAME]" .Release.Name }}" - name: "ConnectionStrings__ProductService" - value: "{{ .Values.global.connectionStrings.productService | replace "[RELEASE_NAME]" .Release.Name }}" + value: "{{ .Values.global.connectionStrings.product | replace "[RELEASE_NAME]" .Release.Name }}" ... +``` +}%} -# product-service.yaml +Product microservice `product-service.yaml` file. + +{%{ +```yaml apiVersion: v1 kind: Service metadata: @@ -427,32 +474,113 @@ After creating the Helm chart, you can *Refresh Sub Charts* in the ABP Studio. ![kubernetes-refresh-sub-charts](images/kubernetes-refresh-sub-charts.png) -Then, update *Metadata* information right-click the *microservicename* [sub-chart](../../studio/kubernetes.md#subchart), select *Properties* it open *Chart Properties* window. You can edit in the *Metadata* tab. +Then, update *Metadata* information right-click the *microservicename* [sub-chart](../../studio/kubernetes.md#subchart), select *Properties* it open *Chart Properties* window. You should add the following keys in the *Metadata* tab. ![microservice-chart-properties](images/microservice-chart-properties-metadata.png) +- `projectPath`: The path to the microservice host application project. In the bookstore example, this value is `../../services/product/Acme.Bookstore.ProductService/Acme.Bookstore.ProductService.csproj`. +- `imageName`: When we build the Docker image, it uses this value as the Docker image name. We're going to use it in the Helm chart values. +- `projectType`: You can add a Helm chart for Angular and .NET projects, which is why we should explicitly specify the project type. + Add the *Kubernetes Services* in the *Chart Properties* -> *Kubernetes Services* tab. ![microservice-chart-properties-kubernetes-services](images/microservice-chart-properties-kubernetes-services.png) +> This value should be the same as the [solution runner application](./../../studio/running-applications.md#properties) *Kubernetes service* value. It's necessary for browsing because when we connect to the Kubernetes cluster, we should browse the Kubernetes services instead of using the Launch URL. + Last but not least, we need to configure the helm chart environments for identity, auth-server, and gateway applications. +Below is an example of the *Identity* microservice `identity.yaml` file. + {%{ -```yaml -# identity.yaml -# Add this line to the "env:" section -- name: "OpenIddict__Resources__ProductService__RootUrl" - value: "http://{{ .Release.Name }}-productservice" - -# authserver.yaml -# Concat the following lines for "App__CorsOrigins" section -- name: "App__CorsOrigins" - value: "...,http://{{ .Release.Name }}-administration,http://{{ .Release.Name }}-productservice" - -# webapigateway.yaml -# Add this line to the "env:" section -- name: "ReverseProxy__Clusters__ProductService__Destinations__ProductService__Address" - value: "http://{{ .Release.Name }}-productservice" +```diff +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Release.Name }}-{{ .Chart.Name }}" +spec: + selector: + matchLabels: + app: "{{ .Release.Name }}-{{ .Chart.Name }}" + template: + metadata: + labels: + app: "{{ .Release.Name }}-{{ .Chart.Name }}" + spec: + containers: + - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + name: "{{ .Release.Name }}-{{ .Chart.Name }}" + ports: + - name: "http" + containerPort: 80 + env: + ... ++ - name: "OpenIddict__Resources__ProductService__RootUrl" ++ value: "http://{{ .Release.Name }}-product" +``` +}%} + +Below is an example of the *AuthServer* application `authserver.yaml` file. + +{%{ +```diff +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Release.Name }}-{{ .Chart.Name }}" +spec: + selector: + matchLabels: + app: "{{ .Release.Name }}-{{ .Chart.Name }}" + template: + metadata: + labels: + app: "{{ .Release.Name }}-{{ .Chart.Name }}" + spec: + containers: + - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + name: "{{ .Release.Name }}-{{ .Chart.Name }}" + ports: + - name: "http" + containerPort: 80 + env: + ... + - name: "App__CorsOrigins" +- value: "...,http://{{ .Release.Name }}-administration" ++ value: "...,http://{{ .Release.Name }}-administration,http://{{ .Release.Name }}-product" +``` +}%} + +Below is an example of the *WebApiGateway* application `webapigateway.yaml` file. + +{%{ +```diff +apiVersion: apps/v1 +kind: Deployment +metadata: + name: "{{ .Release.Name }}-{{ .Chart.Name }}" +spec: + selector: + matchLabels: + app: "{{ .Release.Name }}-{{ .Chart.Name }}" + template: + metadata: + labels: + app: "{{ .Release.Name }}-{{ .Chart.Name }}" + spec: + containers: + - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + name: "{{ .Release.Name }}-{{ .Chart.Name }}" + ports: + - name: "http" + containerPort: 80 + env: + ... ++ - name: "ReverseProxy__Clusters__Product__Destinations__Product__Address" ++ value: "http://{{ .Release.Name }}-product" ``` }%} @@ -465,11 +593,11 @@ You can customize the microservice template if needed. Add new configurations, d After adding the new microservice to the solution, you can develop the UI for the new microservice. For .NET applications, you can add the microservice *Contracts* package to the UI application(s) to access the services provided by the new microservice. Afterwards, you can use the [generate-proxy](../../cli/index.md#generate-proxy) command to generate the proxy classes for the new microservice. ```bash -abp generate-proxy -t csharp -url http://localhost:44333/ -m productService --without-contracts +abp generate-proxy -t csharp -url http://localhost:44333/ -m product --without-contracts ``` Next, start creating *Pages* and *Components* for the new microservice in the UI application(s). Similarly, if you have an Angular application, you can use the [generate-proxy](../../cli/index.md#generate-proxy) command to generate the proxy classes for the new microservice and start developing the UI. ```bash -abp generate-proxy -t ng -url http://localhost:44333/ -m productService +abp generate-proxy -t ng -url http://localhost:44333/ -m product ``` \ No newline at end of file diff --git a/docs/en/solution-templates/microservice/images/folder-structure.png b/docs/en/solution-templates/microservice/images/folder-structure.png new file mode 100644 index 0000000000..926bfaa3d5 Binary files /dev/null and b/docs/en/solution-templates/microservice/images/folder-structure.png differ diff --git a/docs/en/studio/installation.md b/docs/en/studio/installation.md index 11f390f9f3..4dc158e464 100644 --- a/docs/en/studio/installation.md +++ b/docs/en/studio/installation.md @@ -8,7 +8,7 @@ Before you begin the installation process for ABP Studio, ensure that your system meets the following pre-requirements: ### Node -Make sure [Node.js](https://nodejs.org/en) is installed on your system. If you have not installed Node.js, you can download the `v16` or `v18` version from the official [Node.js website](https://nodejs.org/en/download/prebuilt-installer). +Make sure [Node.js](https://nodejs.org/en) is installed on your system. If you have not installed Node.js, you can download the `v18.19+` version from the official [Node.js website](https://nodejs.org/en/download/prebuilt-installer). ### WireGuard (Optional) ABP Studio needs [WireGuard](https://www.wireguard.com/) for Kubernetes operations. You can find the installation instructions for your specific operating system below: diff --git a/docs/en/tutorials/todo/layered/index.md b/docs/en/tutorials/todo/layered/index.md index 78f49df9dd..b58d95a034 100644 --- a/docs/en/tutorials/todo/layered/index.md +++ b/docs/en/tutorials/todo/layered/index.md @@ -53,7 +53,7 @@ This documentation has a video tutorial on **YouTube**!! You can watch it here: ## Pre-Requirements * An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 8.0+](https://dotnet.microsoft.com/download/dotnet) development. -* [Node v16.x](https://nodejs.org/) +* [Node v18.19+](https://nodejs.org/) {{if DB=="Mongo"}} diff --git a/docs/en/tutorials/todo/single-layer/index.md b/docs/en/tutorials/todo/single-layer/index.md index 9afbbd8833..3b4f755c70 100644 --- a/docs/en/tutorials/todo/single-layer/index.md +++ b/docs/en/tutorials/todo/single-layer/index.md @@ -49,7 +49,7 @@ This documentation has a video tutorial on **YouTube**!! You can watch it here: ## Pre-Requirements * An IDE (e.g. [Visual Studio](https://visualstudio.microsoft.com/vs/)) that supports [.NET 8.0+](https://dotnet.microsoft.com/download/dotnet) development. -* [Node v16.x](https://nodejs.org/) +* [Node v18.19+](https://nodejs.org/) {{if DB=="Mongo"}} diff --git a/framework/src/Volo.Abp.AspNetCore.Abstractions/Volo/Abp/AspNetCore/Authentication/AbpAspNetCoreTokenUnauthorizedErrorInfo.cs b/framework/src/Volo.Abp.AspNetCore.Abstractions/Volo/Abp/AspNetCore/Authentication/AbpAspNetCoreTokenUnauthorizedErrorInfo.cs new file mode 100644 index 0000000000..2702f4f19f --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Abstractions/Volo/Abp/AspNetCore/Authentication/AbpAspNetCoreTokenUnauthorizedErrorInfo.cs @@ -0,0 +1,12 @@ +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Authentication; + +public class AbpAspNetCoreTokenUnauthorizedErrorInfo : IScopedDependency +{ + public string? Error { get; set; } + + public string? ErrorDescription { get; set; } + + public string? ErrorUri { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/Extensions/DependencyInjection/AbpJwtBearerExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/Extensions/DependencyInjection/AbpJwtBearerExtensions.cs index 5f48cc92d5..8973b68a1f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/Extensions/DependencyInjection/AbpJwtBearerExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Microsoft/Extensions/DependencyInjection/AbpJwtBearerExtensions.cs @@ -1,6 +1,7 @@ using System; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Volo.Abp.AspNetCore.Authentication; using Volo.Abp.Security.Claims; namespace Microsoft.Extensions.DependencyInjection; @@ -31,6 +32,33 @@ public static class AbpJwtBearerExtensions return builder.AddJwtBearer(authenticationScheme, displayName, options => { configureOptions?.Invoke(options); + + options.Events ??= new JwtBearerEvents(); + var previousOnChallenge = options.Events.OnChallenge; + options.Events.OnChallenge = async eventContext => + { + await previousOnChallenge(eventContext); + + if (eventContext.Handled || + !string.IsNullOrEmpty(eventContext.Error) || + !string.IsNullOrEmpty(eventContext.ErrorDescription) || + !string.IsNullOrEmpty(eventContext.ErrorUri)) + { + return; + } + + var tokenUnauthorizedErrorInfo = eventContext.HttpContext.RequestServices.GetRequiredService(); + if (string.IsNullOrEmpty(tokenUnauthorizedErrorInfo.Error) && + string.IsNullOrEmpty(tokenUnauthorizedErrorInfo.ErrorDescription) && + string.IsNullOrEmpty(tokenUnauthorizedErrorInfo.ErrorUri)) + { + return; + } + + eventContext.Error = tokenUnauthorizedErrorInfo.Error; + eventContext.ErrorDescription = tokenUnauthorizedErrorInfo.ErrorDescription; + eventContext.ErrorUri = tokenUnauthorizedErrorInfo.ErrorUri; + }; }); } } diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj index 4d7b852b9c..839053bc90 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo.Abp.AspNetCore.Authentication.JwtBearer.csproj @@ -19,6 +19,7 @@ + diff --git a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs index 89d957fe57..c0b9122b7d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Authentication.JwtBearer/Volo/Abp/AspNetCore/Authentication/JwtBearer/AbpAspNetCoreAuthenticationJwtBearerModule.cs @@ -7,7 +7,7 @@ using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Authentication.JwtBearer; -[DependsOn(typeof(AbpSecurityModule), typeof(AbpCachingModule))] +[DependsOn(typeof(AbpSecurityModule), typeof(AbpCachingModule), typeof(AbpAspNetCoreAbstractionsModule))] public class AbpAspNetCoreAuthenticationJwtBearerModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) @@ -21,5 +21,6 @@ public class AbpAspNetCoreAuthenticationJwtBearerModule : AbpModule context.Services.AddTransient(); context.Services.AddTransient(); } + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs index e50d0a791a..b8462f4b5b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Card/AbpCardBodyTagHelperService.cs @@ -32,7 +32,7 @@ public class AbpCardBodyTagHelperService : AbpTagHelperService var small = new TagBuilder("small"); small.Attributes.Add("id", idAttr?.Value?.ToString() + "InfoText"); - small.AddCssClass("form-text text-muted"); + small.AddCssClass("form-text"); small.InnerHtml.Append(localizedText); return small.ToHtmlString(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs index bdd4cb2a27..f0f7f94439 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/AbpAspNetCoreMvcUiBundlingModule.cs @@ -1,4 +1,6 @@ -using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; +using Volo.Abp.AspNetCore.Mvc.Libs; +using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; +using Volo.Abp.Data; using Volo.Abp.Minify; using Volo.Abp.Modularity; @@ -11,5 +13,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; )] public class AbpAspNetCoreMvcUiBundlingModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + if (!context.Services.IsDataMigrationEnvironment()) + { + Configure(options => + { + options.CheckLibs = true; + }); + } + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs index daea15328f..2ad3bb2bdb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/DatatablesNet/DatatablesNetScriptContributor.cs @@ -10,6 +10,14 @@ public class DatatablesNetScriptContributor : BundleContributor { public override void ConfigureBundle(BundleConfigurationContext context) { - context.Files.AddIfNotContains("/libs/datatables.net/js/jquery.dataTables.js"); + if (context.FileProvider.GetFileInfo("/libs/datatables.net/js/dataTables.min.js").Exists) + { + context.Files.AddIfNotContains("/libs/datatables.net/js/dataTables.min.js"); + } + else + { + //TODO: Compatible code, Remove it after 8.3 rc1. + context.Files.AddIfNotContains("/libs/datatables.net/js/jquery.dataTables.js"); + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js index b4000d6cbc..e43c7caad9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js @@ -90,7 +90,7 @@ var abp = abp || {}; return ""; } - var $button = $(''); + var $button = $(''); if (firstItem.displayNameHtml) { $button.html(abp.utils.isFunction(firstItem.text) ? firstItem.text(record, tableInstance) : firstItem.text); @@ -240,10 +240,17 @@ var abp = abp || {}; if (tableInstance.aoColumns) { columns = tableInstance.aoColumns; - } else { + } else if (abp.utils.isFunction(tableInstance.fnSettings)) { columns = tableInstance.fnSettings().aoColumns; } + if (!columns && abp.utils.isFunction(tableInstance.api)) { + var settings = tableInstance.api().settings(); + if (settings.length === 1 && settings[0].aoColumns) { + columns = settings[0].aoColumns; + } + } + if (!columns) { return; } @@ -271,8 +278,9 @@ var abp = abp || {}; } }; - var _existingApiRenderRowActionsFunction = $.fn.dataTableExt.oApi.renderRowActions; - $.fn.dataTableExt.oApi.renderRowActions = + if ($.fn.dataTableExt.oApi) { + var _existingApiRenderRowActionsFunction = $.fn.dataTableExt.oApi.renderRowActions; + $.fn.dataTableExt.oApi.renderRowActions = function (tableInstance, nRow, aData, iDisplayIndex, iDisplayIndexFull) { if (_existingApiRenderRowActionsFunction) { _existingApiRenderRowActionsFunction(tableInstance, nRow, aData, iDisplayIndex, iDisplayIndexFull); @@ -280,7 +288,8 @@ var abp = abp || {}; renderRowActions(tableInstance, nRow, aData, iDisplayIndex, iDisplayIndexFull); }; - + } + if (!$.fn.dataTable) { return; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 7b68fca32d..379523b659 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -32,6 +32,7 @@ using Volo.Abp.AspNetCore.Mvc.DataAnnotations; using Volo.Abp.AspNetCore.Mvc.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Infrastructure; using Volo.Abp.AspNetCore.Mvc.Json; +using Volo.Abp.AspNetCore.Mvc.Libs; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.DependencyInjection; @@ -231,6 +232,7 @@ public class AbpAspNetCoreMvcModule : AbpModule public override void OnApplicationInitialization(ApplicationInitializationContext context) { AddApplicationParts(context); + CheckLibs(context); } private static void AddApplicationParts(ApplicationInitializationContext context) @@ -277,4 +279,9 @@ public class AbpAspNetCoreMvcModule : AbpModule partManager.ApplicationParts.AddIfNotContains(moduleAssembly); } } + + private static void CheckLibs(ApplicationInitializationContext context) + { + context.ServiceProvider.GetRequiredService().CheckLibs(context); + } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs index d2abae0902..3b60e74d78 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs @@ -75,10 +75,18 @@ public class AbpExceptionFilter : IAsyncExceptionFilter, IAbpFilter, ITransientD } else { - context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); - context.HttpContext.Response.StatusCode = (int)context - .GetRequiredService() - .GetStatusCode(context.HttpContext, context.Exception); + if (!context.HttpContext.Response.HasStarted) + { + context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); + context.HttpContext.Response.StatusCode = (int)context + .GetRequiredService() + .GetStatusCode(context.HttpContext, context.Exception); + } + else + { + var logger = context.GetService>(NullLogger.Instance)!; + logger.LogWarning("HTTP response has already started, cannot set headers and status code!"); + } context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo)); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs index f404f52e5d..56814718b6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs @@ -103,10 +103,17 @@ public class AbpExceptionPageFilter : IAsyncPageFilter, IAbpFilter, ITransientDe } else { - context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); - context.HttpContext.Response.StatusCode = (int)context - .GetRequiredService() - .GetStatusCode(context.HttpContext, context.Exception!); + if (!context.HttpContext.Response.HasStarted) + { + context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); + context.HttpContext.Response.StatusCode = (int)context + .GetRequiredService() + .GetStatusCode(context.HttpContext, context.Exception!); + } + else + { + logger.LogWarning("HTTP response has already started, cannot set headers and status code!"); + } context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo)); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsOptions.cs new file mode 100644 index 0000000000..d166528a05 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsOptions.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.AspNetCore.Mvc.Libs; + +public class AbpMvcLibsOptions +{ + public bool CheckLibs { get; set; } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsService.cs new file mode 100644 index 0000000000..1d12254934 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/AbpMvcLibsService.cs @@ -0,0 +1,93 @@ +using System; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.FileProviders; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.Libs; + +public class AbpMvcLibsService : IAbpMvcLibsService, ITransientDependency +{ + private Task? _checkLibsTask; + + public virtual void CheckLibs(ApplicationInitializationContext context) + { + var options = context.ServiceProvider.GetRequiredService>().Value; + if (options.CheckLibs) + { + var app = context.GetApplicationBuilderOrNull(); + if (app == null) + { + var logger = context.ServiceProvider.GetRequiredService>(); + logger.LogWarning($"The {nameof(IApplicationBuilder)} is not available. The 'CheckLibs' feature is disabled!"); + return; + } + + app.Use(async (httpContext, next) => + { + if (!await CheckLibsAsyncOnceAsync(httpContext)) + { + httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + httpContext.Response.ContentType = "text/html"; + await httpContext.Response.WriteAsync( + "" + + " " + + " Error - The Libs folder is missing!" + + " " + + " " + + "

⚠️ The Libs folder under the wwwroot/libs directory is empty!

" + + "

The Libs folder contains mandatory NPM Packages for running the project.

" + + "

Make sure you run the abp install-libs CLI tool command.

" + + "

For more information, check out the ABP CLI documentation

" + + " " + + "", + Encoding.UTF8 + ); + return; + } + + await next(httpContext); + }); + } + } + + protected virtual Task CheckLibsAsyncOnceAsync(HttpContext httpContext) + { + if (_checkLibsTask == null) + { + _checkLibsTask = CheckLibsAsync(httpContext); + } + + return _checkLibsTask; + } + + protected virtual Task CheckLibsAsync(HttpContext httpContext) + { + var logger = httpContext.RequestServices.GetRequiredService>(); + try + { + var fileProvider = new PhysicalFileProvider(httpContext.RequestServices.GetRequiredService().WebRootPath); + var libsFolder = fileProvider.GetDirectoryContents("/libs"); + if (!libsFolder.Exists || !libsFolder.Any()) + { + logger.LogError("The 'wwwroot/libs' folder does not exist or empty!"); + return Task.FromResult(false); + } + } + catch (Exception e) + { + // In case of any exception, log it and return true to prevent crashing the application. + logger.LogError(e, "An error occurred while checking the libs folder!"); + } + + return Task.FromResult(true); + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/IAbpMvcLibsService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/IAbpMvcLibsService.cs new file mode 100644 index 0000000000..98a88184e8 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Libs/IAbpMvcLibsService.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.AspNetCore.Mvc.Libs; + +public interface IAbpMvcLibsService +{ + void CheckLibs(ApplicationInitializationContext context); +} diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs index 593992aed4..730fe98533 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs @@ -102,6 +102,7 @@ public static class AbpApplicationBuilderExtensions return app.UseMiddleware(); } + [Obsolete("Replace with AbpClaimsTransformation")] public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app) { return app.UseMiddleware(); diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs index bda87a4e32..c6f8dd7ddc 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/Extensions/DependencyInjection/AbpAspNetCoreServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ -using System.Linq; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; +using Volo.Abp.AspNetCore.Security.Claims; namespace Microsoft.Extensions.DependencyInjection; @@ -20,4 +21,9 @@ public static class AbpAspNetCoreServiceCollectionExtensions return hostingEnvironment; } + + public static IServiceCollection TransformAbpClaims(this IServiceCollection services) + { + return services.AddTransient(); + } } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs index 045b293489..1f6e3c17bf 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/ApplicationInitializationContextExtensions.cs @@ -12,7 +12,14 @@ public static class ApplicationInitializationContextExtensions { public static IApplicationBuilder GetApplicationBuilder(this ApplicationInitializationContext context) { - return context.ServiceProvider.GetRequiredService>().Value!; + var applicationBuilder = context.ServiceProvider.GetRequiredService>().Value; + Check.NotNull(applicationBuilder, nameof(applicationBuilder)); + return applicationBuilder; + } + + public static IApplicationBuilder? GetApplicationBuilderOrNull(this ApplicationInitializationContext context) + { + return context.ServiceProvider.GetRequiredService>().Value; } public static IWebHostEnvironment GetEnvironment(this ApplicationInitializationContext context) diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs index bb2bedf901..2104edab0a 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -10,6 +11,7 @@ using Volo.Abp.Security.Claims; namespace Volo.Abp.AspNetCore.Security.Claims; +[Obsolete("Replace with AbpClaimsTransformation")] public class AbpClaimsMapMiddleware : AbpMiddlewareBase, ITransientDependency { public async override Task InvokeAsync(HttpContext context, RequestDelegate next) diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsTransformation.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsTransformation.cs new file mode 100644 index 0000000000..17e8f0d599 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsTransformation.cs @@ -0,0 +1,35 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Options; + +namespace Volo.Abp.AspNetCore.Security.Claims; + +public class AbpClaimsTransformation : IClaimsTransformation +{ + protected IOptions AbpClaimsMapOptions { get; } + + public AbpClaimsTransformation(IOptions abpClaimsMapOptions) + { + AbpClaimsMapOptions = abpClaimsMapOptions; + } + + public virtual Task TransformAsync(ClaimsPrincipal principal) + { + var mapClaims = principal.Claims.Where(claim => AbpClaimsMapOptions.Value.Maps.Keys.Contains(claim.Type)); + + principal.AddIdentity(new ClaimsIdentity(mapClaims.Select( + claim => new Claim( + AbpClaimsMapOptions.Value.Maps[claim.Type](), + claim.Value, + claim.ValueType, + claim.Issuer + ) + ) + ) + ); + + return Task.FromResult(principal); + } +} diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index 53216b03b9..2ab98adbb4 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs @@ -50,14 +50,18 @@ public static class AbpRegistrationBuilderExtensions ServiceDescriptor serviceDescriptor) where TActivatorData : ReflectionActivatorData { - registrationBuilder.OnActivated(context => + var actions = activatedActionList.GetActions(serviceDescriptor); + if (actions.Any()) { - var serviceActivatedContext = new OnServiceActivatedContext(context.Instance!); - foreach (var action in activatedActionList.GetActions(serviceDescriptor)) + registrationBuilder.OnActivated(context => { - action.Invoke(serviceActivatedContext); - } - }); + var serviceActivatedContext = new OnServiceActivatedContext(context.Instance!); + foreach (var action in actions) + { + action.Invoke(serviceActivatedContext); + } + }); + } return registrationBuilder; } diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs index 91bc360f72..0d70ae6aa9 100644 --- a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs +++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs @@ -22,8 +22,9 @@ public abstract class BackgroundWorkerBase : IBackgroundWorker protected ILogger Logger => LazyServiceProvider.LazyGetService(provider => LoggerFactory?.CreateLogger(GetType().FullName!) ?? NullLogger.Instance); - protected CancellationTokenSource StoppingTokenSource { get; } - protected CancellationToken StoppingToken { get; } + protected CancellationTokenSource StoppingTokenSource { get; set; } + + protected CancellationToken StoppingToken { get; set; } public BackgroundWorkerBase() { diff --git a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs index a719c62c2a..0e99a94460 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs @@ -480,7 +480,12 @@ public abstract class AbpCrudPageBase< await GetEntitiesAsync(); await InvokeAsync(CreateModal!.Hide); - await Notify.Success(L["SavedSuccessfully"]); + await Notify.Success(GetCreateMessage()); + } + + protected virtual string GetCreateMessage() + { + return UiLocalizer["CreatedSuccessfully"]; } protected virtual async Task UpdateEntityAsync() @@ -519,7 +524,12 @@ public abstract class AbpCrudPageBase< await GetEntitiesAsync(); await InvokeAsync(EditModal!.Hide); - await Notify.Success(L["SavedSuccessfully"]); + await Notify.Success(GetUpdateMessage()); + } + + protected virtual string GetUpdateMessage() + { + return UiLocalizer["SavedSuccessfully"]; } protected virtual async Task DeleteEntityAsync(TListViewModel entity) @@ -546,7 +556,12 @@ public abstract class AbpCrudPageBase< { await GetEntitiesAsync(); await InvokeAsync(StateHasChanged); - await Notify.Success(L["DeletedSuccessfully"]); + await Notify.Success(GetDeleteMessage()); + } + + protected virtual string GetDeleteMessage() + { + return UiLocalizer["DeletedSuccessfully"]; } protected virtual string GetDeleteConfirmationMessage(TListViewModel entity) diff --git a/framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiObjectExtensionPropertyInfoExtensions.cs b/framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiObjectExtensionPropertyInfoExtensions.cs index a3cd7372a6..2773b88bed 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiObjectExtensionPropertyInfoExtensions.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/BlazoriseUiObjectExtensionPropertyInfoExtensions.cs @@ -46,6 +46,12 @@ public static class BlazoriseUiObjectExtensionPropertyInfoExtensions public static string? GetDateEditInputFormatOrNull(this IBasicObjectExtensionPropertyInfo property) { + var dataFormatString = property.GetDataFormatStringOrNull(); + if (dataFormatString != null) + { + return dataFormatString; + } + if (property.IsDate()) { return "{0:yyyy-MM-dd}"; @@ -59,6 +65,14 @@ public static class BlazoriseUiObjectExtensionPropertyInfoExtensions return null; } + public static string? GetDataFormatStringOrNull(this IBasicObjectExtensionPropertyInfo property) + { + return property + .Attributes + .OfType() + .FirstOrDefault()?.DataFormatString; + } + public static string? GetTextInputValueOrNull(this IBasicObjectExtensionPropertyInfo property, object? value) { if (value == null) diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor index 96ee31a9f1..ae87961e21 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/AbpExtensibleDataGrid.razor @@ -45,9 +45,9 @@ @@ -55,10 +55,10 @@ else { @@ -76,7 +76,7 @@ { - @RenderCustomTableColumnComponent(column.Component, context) + @RenderCustomTableColumnComponent(column.Component, context!) } @@ -84,7 +84,7 @@ { - @RenderCustomTableColumnComponent(column.Component, context) + @RenderCustomTableColumnComponent(column.Component, context!) } diff --git a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor index 844dea4950..5e686afbda 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor +++ b/framework/src/Volo.Abp.BlazoriseUI/Components/EntityActions.razor @@ -2,7 +2,7 @@ @if ( Type == ActionType.Dropdown ) { - + @if ( HasPrimaryAction ) {