diff --git a/Directory.Packages.props b/Directory.Packages.props index 39c98ea5de..b1398663ee 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,8 +38,8 @@ - - + + @@ -174,4 +174,4 @@ - \ No newline at end of file + 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/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 4800ab9744..5bc1680708 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -621,6 +621,7 @@ "DeleteImageConfirmationMessage": "Are you sure you want to delete the image for \"{0}\"?", "DeleteImageSuccessMessage": "Image successfully deleted", "DeleteImage": "Delete Image", - "NetTerms": "Terms (Days)" + "NetTerms": "Terms (Days)", + "AbpStudioName": "Abp Studio name" } } \ 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 ef07928c83..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", @@ -266,4 +266,4 @@ "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/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/docs/en/CLI.md b/docs/en/CLI.md index e0eede9241..cb16fb2897 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -131,7 +131,7 @@ For more samples, go to [ABP CLI Create Solution Samples](CLI-New-Command-Sample * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: * `ef`: Entity Framework Core. * `mongodb`: MongoDB. - * `--theme`: Specifes the theme. Default theme is `leptonx-lite`. Available themes: + * `--theme`: Specifies the theme. Default theme is `leptonx-lite`. Available themes: * `leptonx-lite`: [LeptonX Lite Theme](Themes/LeptonXLite/AspNetCore.md). * `basic`: [Basic Theme](UI/AspNetCore/Basic-Theme.md). * **`module`**: [Module template](Startup-Templates/Module.md). Additional options: @@ -147,7 +147,7 @@ For more samples, go to [ABP CLI Create Solution Samples](CLI-New-Command-Sample * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: * `ef`: Entity Framework Core. * `mongodb`: MongoDB. - * `--theme`: Specifes the theme. Default theme is `leptonx-lite`. Available themes: + * `--theme`: Specifies the theme. Default theme is `leptonx-lite`. Available themes: * `leptonx-lite`: [LeptonX Lite Theme](Themes/LeptonXLite/AspNetCore.md). * `basic`: [Basic Theme](UI/AspNetCore/Basic-Theme.md). * **`maui`**: .NET MAUI. A minimalist .NET MAUI application will be created if you specify this option. diff --git a/docs/en/Data-Filtering.md b/docs/en/Data-Filtering.md index 830979e710..4706912fe7 100644 --- a/docs/en/Data-Filtering.md +++ b/docs/en/Data-Filtering.md @@ -217,17 +217,14 @@ protected override bool ShouldFilterEntity(IMutableEntityType entityTyp return base.ShouldFilterEntity(entityType); } -protected override Expression> CreateFilterExpression() +protected override Expression> CreateFilterExpression(ModelBuilder modelBuilder) { - var expression = base.CreateFilterExpression(); + var expression = base.CreateFilterExpression(modelBuilder); if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) { - Expression> isActiveFilter = - e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); - expression = expression == null - ? isActiveFilter - : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); + Expression> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); + expression = expression == null ? isActiveFilter : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); } return expression; @@ -251,6 +248,78 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } ```` +#### Using User-defined function mapping for global filters + +Using [User-defined function mapping](https://learn.microsoft.com/en-us/ef/core/querying/user-defined-function-mapping) for global filters will gain performance improvements. + +To use this feature, you need to change your DbContext like below: + +````csharp +protected bool IsActiveFilterEnabled => DataFilter?.IsEnabled() ?? false; + +protected override bool ShouldFilterEntity(IMutableEntityType entityType) +{ + if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) + { + return true; + } + + return base.ShouldFilterEntity(entityType); +} + +protected override Expression> CreateFilterExpression(ModelBuilder modelBuilder) +{ + var expression = base.CreateFilterExpression(modelBuilder); + + if (typeof(IIsActive).IsAssignableFrom(typeof(TEntity))) + { + Expression> isActiveFilter = e => !IsActiveFilterEnabled || EF.Property(e, "IsActive"); + + if (UseDbFunction()) + { + isActiveFilter = e => IsActiveFilter(((IIsActive)e).IsActive, true); + + var abpEfCoreCurrentDbContext = this.GetService(); + modelBuilder.HasDbFunction(typeof(MyProjectNameDbContext).GetMethod(nameof(IsActiveFilter))!) + .HasTranslation(args => + { + // (bool isActive, bool boolParam) + var isActive = args[0]; + var boolParam = args[1]; + + if (abpEfCoreCurrentDbContext.Context?.DataFilter.IsEnabled() == true) + { + // isActive == true + return new SqlBinaryExpression( + ExpressionType.Equal, + isActive, + new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping), + boolParam.Type, + boolParam.TypeMapping); + } + + // empty where sql + return new SqlConstantExpression(Expression.Constant(true), boolParam.TypeMapping); + }); + } + + expression = expression == null ? isActiveFilter : QueryFilterExpressionHelper.CombineExpressions(expression, isActiveFilter); + } + + return expression; +} + +public static bool IsActiveFilter(bool isActive, bool boolParam) +{ + throw new NotSupportedException(AbpEfCoreDataFilterDbFunctionMethods.NotSupportedExceptionMessage); +} + +public override string GetCompiledQueryCacheKey() +{ + return $"{base.GetCompiledQueryCacheKey()}:{IsActiveFilterEnabled}"; +} +```` + ### MongoDB ABP abstracts the `IMongoDbRepositoryFilterer` interface to implement data filtering for the [MongoDB Integration](MongoDB.md), it works only if you use the repositories properly. Otherwise, you should manually filter the data. diff --git a/docs/en/Local-Event-Bus.md b/docs/en/Local-Event-Bus.md index 81c0c5a171..5c5d967f5f 100644 --- a/docs/en/Local-Event-Bus.md +++ b/docs/en/Local-Event-Bus.md @@ -235,6 +235,13 @@ Pre-build events are published when you save changes to the database; * For EF Core, they are published on `DbContext.SaveChanges`. * For MongoDB, they are published when you call repository's `InsertAsync`, `UpdateAsync` or `DeleteAsync` methods (since MongoDB has not a change tracking system). +#### AbpEntityChangeOptions + +There is a `PublishEntityUpdatedEventWhenNavigationChanges` option in the `AbpEntityChangeOptions` class with a default value of `true`. +If you set it to `false`, the `EntityUpdatedEventData` will not be published when a navigation property changes. + +> This option is only used for the EF Core. + ## See Also * [Distributed Event Bus](Distributed-Event-Bus.md) diff --git a/docs/en/Modules/Cms-Kit/Comments.md b/docs/en/Modules/Cms-Kit/Comments.md index ddd39fc675..4eca4cb93d 100644 --- a/docs/en/Modules/Cms-Kit/Comments.md +++ b/docs/en/Modules/Cms-Kit/Comments.md @@ -80,6 +80,12 @@ You can also view and manage replies on this page. ![comments-detail](../../images/cmskit-module-comments-detail.png) +## Settings + +You can configure the approval status of comments using the "Comment" tab under the "Cms" section on the Settings page. When this feature is enabled, you can approve and reject comments. In this way, users can only see the comments that you approve. By default, this feature is set to "false." + +![comments-settings](../../images/cmskit-module-comments-settings.png) + ## Internals ### Domain Layer diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md index 922976767d..123eb45165 100644 --- a/docs/en/Multi-Tenancy.md +++ b/docs/en/Multi-Tenancy.md @@ -1,6 +1,6 @@ # Multi-Tenancy -Multi-Tenancy is a widely used architecture to create **SaaS applications** where the hardware and software **resources are shared by the customers** (tenants). ABP Framework provides all the base functionalities to create **multi tenant applications**. +Multi-Tenancy is a widely used architecture to create **SaaS applications** where the hardware and software **resources are shared by the customers** (tenants). ABP Framework provides all the base functionalities to create **multi tenant applications**. Wikipedia [defines](https://en.wikipedia.org/wiki/Multitenancy) the multi-tenancy as like that: @@ -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 Framework 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. @@ -48,11 +48,11 @@ Multi-tenancy system is designed to **work seamlessly** and make your applicatio ### IMultiTenant -You should implement the `IMultiTenant` interface for your [entities](Entities.md) to make them **multi-tenancy ready**. +You should implement the `IMultiTenant` interface for your [entities](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 Framework **automatically** [filters](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 Framework automatically filters the resources (database, cache...) based on **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 Framework handles data isolation between tenants using **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](Data-Filtering.md) for more. @@ -201,15 +201,15 @@ ABP Framework provides an extensible **Tenant Resolving** system for that purpos 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,30 +279,30 @@ 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](Module-Development-Basics.md). -* This should be done in the *Web/API Layer* since the URL is a web related stuff. +- `{0}` is the placeholder to determine the current tenant's unique name. +- Add this code to the `ConfigureServices` method of your [module](Module-Development-Basics.md). +- This should be done in the _Web/API Layer_ since the URL is a web related stuff. Openiddict is the default Auth Server in ABP (since v6.0). When you use OpenIddict, you must add this code to the `PreConfigure` method as well. ```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,10 +375,10 @@ 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](Dependency-Injection.md) system. +- 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](Dependency-Injection.md) system. #### Multi-Tenancy Middleware @@ -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) is **included in the s **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) is **included in the s } } ] -```` +``` > 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. @@ -440,4 +442,4 @@ The [Tenant Management module](Modules/Tenant-Management.md) provides a basic UI ## See Also -* [Features](Features.md) +- [Features](Features.md) diff --git a/docs/en/Themes/LeptonXLite/Angular.md b/docs/en/Themes/LeptonXLite/Angular.md index e7c8142bda..8e46468bd2 100644 --- a/docs/en/Themes/LeptonXLite/Angular.md +++ b/docs/en/Themes/LeptonXLite/Angular.md @@ -31,20 +31,21 @@ yarn add bootstrap-icons Note: You should remove the old theme styles from "angular.json" if you are switching from "ThemeBasic" or "Lepton." Look at the [Theme Configurations](../../UI/Angular/Theme-Configurations) list of styles. Depending on your theme, you can alter your styles in angular.json. -- Finally, remove `ThemeBasicModule` from `app.module.ts`, and import the related modules in `app.module.ts` +- Finally, remove `ThemeBasicModule`, `provideThemeBasicConfig` from `app.module.ts`, and import the related modules in `app.module.ts` ```js import { ThemeLeptonXModule } from "@abp/ng.theme.lepton-x"; -import { SideMenuLayoutModule } from "@abp/ng.theme.lepton-x/layouts"; @NgModule({ imports: [ // ... - // do not forget to remove ThemeBasicModule or other old theme module - // ThemeBasicModule.forRoot(), - ThemeLeptonXModule.forRoot(), - SideMenuLayoutModule.forRoot(), + // ThemeBasicModule + ThemeLeptonXModule.forRoot() + ], + providers: [ + // do not forget to remove provideThemeBasicConfig or other old theme providers + // provideThemeBasicConfig ], // ... }) diff --git a/docs/en/UI/Angular/Account-Module.md b/docs/en/UI/Angular/Account-Module.md index 28852f6b4d..b5fedb8afe 100644 --- a/docs/en/UI/Angular/Account-Module.md +++ b/docs/en/UI/Angular/Account-Module.md @@ -7,7 +7,6 @@ If you add the account module to your project; - "My account" link in the current user dropdown on the top bar will redirect the user to a page in the account module. - You can switch the authentication flow to the resource owner password flow. - ### Account Module Implementation Install the `@abp/ng.account` NPM package by running the below command: @@ -18,18 +17,18 @@ npm install @abp/ng.account > Make sure v4.3 or higher version is installed. -Open the `app.module.ts` and add `AccountConfigModule.forRoot()` to the imports array as shown below: +Open the `app.module.ts` and add `provideAccountConfig()` to the providers array as shown below: ```js // app.module.ts -import { AccountConfigModule } from '@abp/ng.account/config'; +import { provideAccountConfig } from "@abp/ng.account/config"; //... @NgModule({ - imports: [ + providers: [ //... - AccountConfigModule.forRoot() + provideAccountConfig(), ], //... }) @@ -57,19 +56,19 @@ The pro startup template comes with `@volo/abp.ng.account` package. You should u ```bash npm install @volo/abp.ng.account ``` + > Make sure v4.3 or higher version is installed. Open the `app.module.ts` and add `AccountPublicConfigModule.forRoot()` to the imports array as shown below: > Ensure that the `Account Layout Module` has been added if you are using the Lepton X theme. If you miss the step, you will get an error message that says `Account layout not found. Please check your configuration. If you are using LeptonX, please make sure you have added "AccountLayoutModule.forRoot()" to your app.module configuration.` when you try to access the account pages. Otherwise, you can skip adding the `AccountLayoutModule` step. - ```js // app.module.ts -import { AccountPublicConfigModule } from '@volo/abp.ng.account/public/config'; +import { AccountPublicConfigModule } from "@volo/abp.ng.account/public/config"; // if you are using or want to use Lepton X, you should add AccountLayoutModule -// import { AccountLayoutModule } from '@volosoft/abp.ng.theme.lepton-x/account' +// import { AccountLayoutModule } from '@volosoft/abp.ng.theme.lepton-x/account' //... @@ -104,9 +103,9 @@ Before v4.3, the "My account" link in the current user dropdown on the top bar r ### Personal Info Page Confirm Message -When the user changes their own data on the personal settings tab in My Account, The data can not update the CurrentUser key of Application-Configuration. The information of the user is stored in claims. The only way to apply this information to the CurrentUser of Application-Configuration is user should log out and log in. When the Refresh-Token feature is implemented, it will be fixed. So We've added a confirmation alert. +When the user changes their own data on the personal settings tab in My Account, The data can not update the CurrentUser key of Application-Configuration. The information of the user is stored in claims. The only way to apply this information to the CurrentUser of Application-Configuration is user should log out and log in. When the Refresh-Token feature is implemented, it will be fixed. So We've added a confirmation alert. -If you want to disable these warning, You should set `isPersonalSettingsChangedConfirmationActive` false +If you want to disable these warning, You should set `isPersonalSettingsChangedConfirmationActive` false ```js // app-routing.module.ts diff --git a/docs/en/UI/Angular/Basic-Theme.md b/docs/en/UI/Angular/Basic-Theme.md index 651be9b637..3b11e81f14 100644 --- a/docs/en/UI/Angular/Basic-Theme.md +++ b/docs/en/UI/Angular/Basic-Theme.md @@ -11,7 +11,7 @@ The Basic Theme is a theme implementation for the Angular UI. It is a minimalist If you need to manually this theme, follow the steps below: * Install the [@abp/ng.theme.basic](https://www.npmjs.com/package/@abp/ng.theme.basic) NPM package to your Angular project. -* Open the `src/app/app.module.ts` file, import `ThemeBasicModule` (it can be imported from `@abp/ng.theme.basic` package), and add `ThemeBasicModule.forRoot()` to the `imports` array. +* Open the `src/app/app.module.ts` file, import `ThemeBasicModule`,`provideThemeBasicConfig` (it can be imported from `@abp/ng.theme.basic` package), and add `ThemeBasicModule` to the `imports` array and provide `provideThemeBasicConfig()` to the providers array. * Open the `src/app/shared/shared.module` file, import `ThemeBasicModule` (it can be imported from `@abp/ng.theme.basic` package), and add `ThemeBasicModule` to the `imports` and `exports` array. The `ThemeBasicModule` is registered own layouts (`ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`) to a service which is exposed by `@abp/ng.core` package on application initialization. diff --git a/docs/en/UI/Angular/Component-Replacement.md b/docs/en/UI/Angular/Component-Replacement.md index d21489d232..73c7a2ed15 100644 --- a/docs/en/UI/Angular/Component-Replacement.md +++ b/docs/en/UI/Angular/Component-Replacement.md @@ -30,7 +30,6 @@ export class AppComponent { ![Example Usage](./images/component-replacement.gif) - ## How to Replace a Layout Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced the same way. @@ -88,11 +87,13 @@ This component should have a 'router-outlet' for dynamic content loading. You ca ```bash ng generate component new-layout ``` + This command will create a new component named `new-layout`. Now, open the new-layout.component.html file and add a `router-outlet` to it: ```html - + ``` + This 'router-outlet' will act as a placeholder that Angular dynamically fills based on the current router state. note: (don't forget: you should add the app in the app.module.ts file) @@ -103,10 +104,11 @@ Although this step is optional, it can be useful if you're going to use the layo ```javascript export const eCustomLayout = { - key: 'CustomLayout', - component: 'CustomLayoutComponent', + key: "CustomLayout", + component: "CustomLayoutComponent", }; ``` + In this variable, `key` is a unique identifier for the layout component, and `component` is the name of the layout component. You can use this variable when you need to refer to the layout component. @@ -120,19 +122,24 @@ Here's how you can do it: ```javascript export const CUSTOM_LAYOUT_PROVIDERS = [ - { provide: APP_INITIALIZER, useFactory: configureLayoutFn, deps: [ReplaceableComponentsService], multi: true }, - + { + provide: APP_INITIALIZER, + useFactory: configureLayoutFn, + deps: [ReplaceableComponentsService], + multi: true, + }, ]; function configureLayoutFn() { - const service= inject( ReplaceableComponentsService) - return () =>{ - service.add({ - key: eCustomLayout.component, - component: CustomLayoutComponent, - }) - } + const service = inject(ReplaceableComponentsService); + return () => { + service.add({ + key: eCustomLayout.component, + component: CustomLayoutComponent, + }); + }; } ``` + In this code, `configureLayoutFn` is a factory function that adds the new layout component to the `ReplaceableComponentsService`. The `APP_INITIALIZER` provider runs this function when the application starts. note: (don't forget: you should add the CUSTOM_LAYOUT_PROVIDERS in the app.module.ts file) @@ -149,34 +156,31 @@ export const myDynamicLayouts = new Map([...DEFAULT_DYNAMIC_LAYO #### Step 5: Pass the Dynamic Layouts to the CoreModule -The final step is to pass the dynamic layouts to the `CoreModule` using the `forRoot` method. This method allows you to configure the module with a static method. +The final step is to pass the dynamic layouts to the `provideAbpCore` using the `withOptions` method. This method allows you to configure the module with a static method. Here's how you can do it: -```javascript +```ts @NgModule({ - declarations: [AppComponent], - imports: [ - // other imports... - CoreModule.forRoot({ - dynamicLayouts: myDynamicLayouts, - environment, - registerLocaleFn: registerLocale(), - }), - // other imports... - NewLayoutComponent + providers: [ + // ... + provideAbpCore( + withOptions({ + dynamicLayouts: myDynamicLayouts, + environment, + registerLocaleFn: registerLocale(), + }), + ), ], - providers: [APP_ROUTE_PROVIDER, CUSTOM_LAYOUT_PROVIDERS], - bootstrap: [AppComponent], }) export class AppModule {} ``` -In this code, `myDynamicLayouts` is the map of dynamic layouts you defined earlier. We pass this map to the `CoreModule` using the `forRoot` method. +In this code, `myDynamicLayouts` is the map of dynamic layouts you defined earlier. We pass this map to the `provideAbpCore` using the `withOptions` method. Now that you have defined the new layout, you can use it in the router definition. You do this by adding a new route that uses the new layout. -Here's how you can do it: +Here's how you can do it: ```javascript // route.provider.ts @@ -221,14 +225,13 @@ Run the following command in `angular` folder to create a new component called ` yarn ng generate component logo --inlineTemplate --inlineStyle ``` - Open the generated `logo.component.ts` in `src/app/logo` folder and replace its content with the following: ```js -import { Component } from '@angular/core'; +import { Component } from "@angular/core"; @Component({ - selector: 'app-logo', + selector: "app-logo", template: ` @@ -284,14 +287,14 @@ yarn ng generate component routes Open the generated `routes.component.ts` in `src/app/routes` folder and replace its content with the following: ```js -import { Component, HostBinding } from '@angular/core'; +import { Component, HostBinding } from "@angular/core"; @Component({ - selector: 'app-routes', - templateUrl: 'routes.component.html', + selector: "app-routes", + templateUrl: "routes.component.html", }) export class RoutesComponent { - @HostBinding('class.mx-auto') + @HostBinding("class.mx-auto") marginAuto = true; get smallScreen() { @@ -321,11 +324,14 @@ Open the generated `routes.component.html` in `src/app/routes` folder and replac