diff --git a/.gitignore b/.gitignore index 3c5c4f89da..2f820efc4d 100644 --- a/.gitignore +++ b/.gitignore @@ -310,4 +310,7 @@ abp-build-config.json /templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Pages/Test/ # Identity Server temp signature file -tempkey.jwk \ No newline at end of file +tempkey.jwk + +# ABP Analyzing files +*.abppkg.analyze.json \ No newline at end of file diff --git a/docs/en/Blog-Posts/2021-04-05 CmsKit/POST.md b/docs/en/Blog-Posts/2021-04-05 CmsKit/POST.md index ad8b89faa0..740adb96c7 100644 --- a/docs/en/Blog-Posts/2021-04-05 CmsKit/POST.md +++ b/docs/en/Blog-Posts/2021-04-05 CmsKit/POST.md @@ -1,3 +1,85 @@ -# Introducing the CMS Kit Module for the ABP Framework +# Introducing The CMS-Kit Module + +One of the most exciting announcements of the ABP Framework 4.3 release was releasing the initial version of the CMS Kit module. The team had been working hard to release the initial version for months. For those who didn't read the [ABP Framework 4.3 release post](https://blog.abp.io/abp/ABP-Framework-4.3-RC-Has-Been-Published), the CMS Kit module adds CMS(Content Management System) capabilities to your application. Some of the CMS Kit features are open source and free, while others are only included in the Pro version included in the ABP Commercial. + +List of free and pro features in the CMS Kit module; + +- Free + - Page System: Provides a **page** management system to manage dynamic pages. + - Blog System: Provides a **blog** system to create blogs and publish posts. + - Tag System: Provides a **tag** system to tag any kind of resources, such as blog posts. + - Comment System: Provides a **comment** system to add comments feature to any type of resource, such as blog posts, products, etc. + - Reaction System: Provides a **reaction** system to add reactions to any type of resource, such as blog posts or comments, etc. + - Rating System: Provides a **rating** system to add ratings feature to any type of resource, such as comments, products, etc. +- Pro + - Newsletter System: Provides a **newsletter** system to allow users to subscribe to newsletters. + - Contact Form System: Provides a **contact form** system to allow users to write a message to you on public websites. + +## Installation + +CMS Kit module comes installed with commercial templates when you create the solution with the public website option. You can run the following command and create a solution with a public website. + +```bash +abp new Acme --with-public-website +``` + +If you're using the open-source version or adding the module to an existing project, the ABP CLI allows adding a module to a solution using `add-module` command. + +You can run the following command to add the module to an open-source solution. + +```bash +abp add-module Volo.CmsKit +``` + +If you're a commercial user, run the following command to install the pro version of the module. + +```bash +abp add-module Volo.CmsKit.Pro +``` + +After adding the module to the solution, you need to configure features. The CMS Kit module uses the [global feature](https://docs.abp.io/en/abp/latest/Global-Features) system for all features. To enable the features in the CMS Kit module, open the `GlobalFeatureConfigurator` class in the `Domain.Shared` project and place the following code to the `Configure` method to enable all features in the CMS kit module. + + ```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.EnableAll(); +}); + ``` + +If you're a commercial user, you need to enable CMS Kit Pro features as well. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKitPro(cmsKitPro => +{ + cmsKitPro.EnableAll(); +}); +``` + +> If you are using Entity Framework Core, do not forget to add a new migration and update your database after configuring the features. + +We've completed the installation step. Run the project, and you will see the CMS menu item in the menu. + +![cms-kit-menu](cms-kit-menu.png) + +> CMS Kit's initial release only supports the MVC UI. We're planning to add Angular and Blazor support in upcoming releases. + +## Package Structures + +CMS Kit is designed for various usage scenarios. When you visit [nuget.org](https://www.nuget.org/packages?q=Volo.CmsKit) or [abp.io](https://abp.io/packages?moduleName=Volo.CmsKit.Pro) to see the available CMS Kit packages, you will find out that packages have admin, public and unified versions. + +For example, + + - `Volo.CmsKit.Admin.Application`: Contains functionality required by admin websites. + - `Volo.CmsKit.Public.Application`: Contains functionality required by public websites. + - `Volo.CmsKit.Application` : Unified package dependent on both public and admin packages. + + If you want to separate admin and public website codes, you can use the admin and public packages. However, if you want to keep admin and public website codes in a shared project, you can use the unified packages to include admin and public packages. + +> It is recommended to use the unified packages instead of adding both admin and public packages. + + + +We've covered the initial features, installation and configuration steps in this post. You can read the [open-source](https://docs.abp.io/en/abp/latest/Modules/Cms-Kit) and [commercial](https://docs.abp.io/en/commercial/latest/modules/CMS-Kit) documentation to get further information about features and the CMS Kit module. CMS Kit's initial version contains lots of features that you can easily integrate and use in your applications. We're planning to improve the existing features, fixing bugs and adding new features in upcoming releases. If you want to give some feedback or have a feature request, please reach out to us from [GitHub](https://github.com/abpframework/abp) or [support.abp.io](https://support.abp.io). We will be happy to plan the CMS Kit module's future together. + +Thank you! -TODO... \ No newline at end of file diff --git a/docs/en/Blog-Posts/2021-04-05 CmsKit/cms-kit-menu.png b/docs/en/Blog-Posts/2021-04-05 CmsKit/cms-kit-menu.png new file mode 100644 index 0000000000..3f187c9a9d Binary files /dev/null and b/docs/en/Blog-Posts/2021-04-05 CmsKit/cms-kit-menu.png differ diff --git a/docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/POST.md b/docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/POST.md index 7a88adde44..8aeff16612 100644 --- a/docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/POST.md +++ b/docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/POST.md @@ -38,8 +38,19 @@ Open `YourProjectWebModule.cs` class and add the following line to the `PreConfi #### 2- Add Client Scripts -2- In the `ConfigureServices` method of your web module add the following code to add the `signalr.js` and `notification-hub.js`. We'll add these packages in the next steps. +2- In the `ConfigureServices` method of your web module add the following code to add the `signalr.js` and `notification-hub.js`. We'll add these packages in the next steps. If you are not using Lepton Theme you can add the files to `StandardBundles.Scripts.Global`. +```csharp + Configure(options => + { + options + .ScriptBundles + .Get(LeptonThemeBundles.Scripts.Global) //or -> Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling.StandardBundles.Scripts.Global + .AddFiles("/libs/signalr/signalr.js") + .AddFiles("/Pages/notification-hub.js"); + }); + ``` + ![Script Bundles](add-script-bundles.jpg) #### 3- Add Hub Endpoint @@ -71,6 +82,11 @@ Add the following JavaScript class into your `Pages` folder in your Web project. Add [Microsoft.SignalR](https://www.npmjs.com/package/@microsoft/signalr) JavaScript package to the `package.json` which is located in your root folder of the Web project. After you add it, run `yarn` command in your Web directory to be able to install this package. +You can install the latest version (3.1.13 will be old) +``` + "@microsoft/signalr": "^3.1.13" +```` + ![Add SignalR package](signalr-package.jpg) #### 3- Add resource Mapping @@ -94,6 +110,8 @@ To do this replace the `Index.cshtml` and `Index.cshtml.cs` with the followings: #### 5- See it in action -Run your web project and in the Index page you'll see a button named as "Get Notification". Click the button and see the notification that comes from SignalR. This is a basic usage of SignalR notification system. You can implement it according to your own requirements. +Run your web project and in the Index page you'll Server Time will be updated every second. The time information comes from backend via SignalR. +This is a very basic usage of SignalR notification system. +You can implement your own SignalR notification system based on your requirements. ![Result](result.jpg) diff --git a/docs/en/Entity-Framework-Core-Migrations.md b/docs/en/Entity-Framework-Core-Migrations.md index be5968a9af..4d42d8bb97 100644 --- a/docs/en/Entity-Framework-Core-Migrations.md +++ b/docs/en/Entity-Framework-Core-Migrations.md @@ -881,6 +881,67 @@ We had a reference to the `Acme.BookStore.EntityFrameworkCore.DbMigrationsForSec You can run the `.DbMigrator` application to migrate & seed the databases. To test, you can delete both databases and run the `.DbMigrator` application again to see if it creates both of the databases. +## Separating Host & Tenant Database Schemas + +In a multi-tenant solution, you may want to separate your database schemas, so host-related tables don't locate in the tenant databases when tenants have separate databases. + +Some pre-built ABP modules are related only with the host side, like the [Tenant Management](Modules/Tenant-Management.md) module. So, in the tenant `DbContext` class you don't call `modelBuilder.ConfigureTenantManagement()` and that's all. + +However, some modules, like the [Identity](Modules/Identity.md) module, is both used in host and tenant sides. It stores tenant users in the tenant database and host users in the host database. However, it stores some entities, like `IdentityClaimType`, only in the host side. In this case, you don't want to add these tables in the tenant database, even if they are not used and will always be empty. + +ABP provides a simple way to set the multi-tenancy side for a `DbContext`, so the modules can check it and decide to map tables to the database, or not. + +````csharp +public class MyTenantDbContext : AbpDbContext +{ + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.SetMultiTenancySide(MultiTenancySides.Tenant); + + base.OnModelCreating(modelBuilder); + modelBuilder.ConfigureIdentity(); + modelBuilder.ConfigureFeatureManagement(); + modelBuilder.ConfigureAuditLogging(); + } +} +```` + +The first line in the `OnModelCreating` sets multi-tenancy side to `Tenant`. For this example, Feature management tables are not created (because all the tables are host-specific), so calling `modelBuilder.ConfigureFeatureManagement()` has no effect. Also, `ConfigureIdentity()` call respects to the multi-tenancy side and doesn't create host-specific tables for this database. + +`SetMultiTenancySide` can get the following values: + +* `MultiTenancySides.Both` (**default value**): This `DbContext` (and the related database) is shared by host and tenant. +* `MultiTenancySides.Host`: This `DbContext` (and the related database) is used only by the host side. +* `MultiTenancySides.Tenant`: This `DbContext` (and the related database) is only for tenants. + +If you create a re-usable application module or want to check that value in your application code, you can use `modelBuilder.GetMultiTenancySide()` to check the current side. + +````csharp +var side = modelBuilder.GetMultiTenancySide(); +if (!side.HasFlag(MultiTenancySides.Host)) +{ + ... +} +```` + +Or practically you can use one of the shortcut extension methods: + +````csharp +if (modelBuilder.IsTenantOnlyDatabase()) +{ + ... +} +```` + +There are four methods to check the current side: + +* `IsHostDatabase()`: Returns `true` if you should create host-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide().HasFlag(MultiTenancySides.Host)`. +* `IsHostOnlyDatabase()`: Returns `true` if you should only create host-related tables, but should not create tenant-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide() == MultiTenancySides.Host`. +* `IsTenantDatabase()`: Returns `true` if you should create tenant-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide().HasFlag(MultiTenancySides.Tenant)`. +* `IsTenantOnlyDatabase()`: Returns `true` if you should only create tenant-related tables, but should not create host-related tables. It is equivalent of checking `modelBuilder.GetMultiTenancySide() == MultiTenancySides.Tenant`. + +All pre-built ABP [modules](Modules/Index.md) checks this value in their `modelBuilder.ConfigureXXX()` methods. + ## Conclusion This document explains how to split your databases and manage your database migrations of your solution for Entity Framework Core. In brief, you need to have a separate migration project per different databases. diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index a2c0e79996..241a08790f 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -626,6 +626,24 @@ See the "*ConfigureByConvention Method*" section above for more information. ## Advanced Topics +### Controlling the Multi-Tenancy + +If your solution is [multi-tenant](Multi-Tenancy.md), tenants may have **separate databases**, you have **multiple** `DbContext` classes in your solution and some of your `DbContext` classes should be usable **only from the host side**, it is suggested to add `[IgnoreMultiTenancy]` attribute on your `DbContext` class. In this case, ABP guarantees that the related `DbContext` always uses the host [connection string](Connection-Strings.md), even if you are in a tenant context. + +**Example:** + +````csharp +[IgnoreMultiTenancy] +public class MyDbContext : AbpDbContext +{ + ... +} +```` + +Do not use the `[IgnoreMultiTenancy]` attribute if any one of your entities in your `DbContext` can be persisted in a tenant database. + +> When you use repositories, ABP already uses the host database for the entities don't implement the `IMultiTenant` interface. So, most of time you don't need to `[IgnoreMultiTenancy]` attribute if you are using the repositories to work with the database. + ### Set Default Repository Classes Default generic repositories are implemented by `EfCoreRepository` class by default. You can create your own implementation and use it for all the default repository implementations. @@ -782,4 +800,4 @@ public class MyCustomEfCoreBulkOperationProvider ## See Also * [Entities](Entities.md) -* [Repositories](Repositories.md) +* [Repositories](Repositories.md) \ No newline at end of file diff --git a/docs/en/Features.md b/docs/en/Features.md index b185eb2d74..3b1869a9a5 100644 --- a/docs/en/Features.md +++ b/docs/en/Features.md @@ -146,9 +146,9 @@ A feature should be defined to be able to check it. ### FeatureDefinitionProvider -Create a class inheriting the `FeatureDefinitionProvider` to define permissions. +Create a class inheriting the `FeatureDefinitionProvider` to define features. -**Example: Defining permissions** +**Example: Defining features** ```csharp using Volo.Abp.Features; diff --git a/docs/en/Modules/Cms-Kit.md b/docs/en/Modules/Cms-Kit.md index e13f1e585b..db35912ae6 100644 --- a/docs/en/Modules/Cms-Kit.md +++ b/docs/en/Modules/Cms-Kit.md @@ -1,3 +1,159 @@ # CMS Kit Module -TODO \ No newline at end of file +This module provides CMS(Content Management System) capabilities for your application. + +* Provides a **page** management system to manage dynamic pages. +* Provides a **blog** system to create blogs and publish posts. +* Provides a [**tag**](cms-kit/Tag-Management.md) system to tag any kind of resources, like blog posts. +* Provides a [**comment**](cms-kit/Comment-System.md) system to add comments feature to any kind of resource, like blog posts, products, etc. +* Provides a [**reaction**](cms-kit/Reaction-System.md) system to add reactions feature to any kind of resource, like blog posts or comments. +* Provides a [**rating**](cms-kit/Rating-System.md) system to add rating feature to any kind of resource. + +## How to install + +The ABP CLI allows adding a module to a solution using `add-module` command. CMS kit module can be added using the command below; + +```bash +abp add-module Volo.CmsKit +``` +Open the `GlobalFeatureConfigurator` class in the `Domain.Shared` project and place the following code to the `Configure` method to enable all features in the CMS kit module. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.EnableAll(); +}); + +``` + +> If you are using EntityFrameworkCore, do not forget to add a new migration and update your database. + +## Packages + +This module follows the [module development best practices guide](https://docs.abp.io/en/abp/latest/Best-Practices/Index) and consists of several NuGet and NPM packages. See the guide if you want to understand the packages and relations between them. + +CMS kit packages are designed for various usage scenarios. When you visit the [CMS kit package list page](https://www.nuget.org/packages?q=Volo.CmsKit), you will see that packages have admin, public and unified versions. +For example, + - `Volo.CmsKit.Admin.Application`: Contains functionality required by admin websites. + - `Volo.CmsKit.Public.Application`: Contains functionality required by public websites. + - `Volo.CmsKit.Application` : Unified package dependent on both public and admin packages. + +If you want to separate admin and public website codes, you can use the admin and public packages. However, if you want to keep admin and public website codes in a shared project, you can use the unified packages to include admin and public packages. + +It is recommended to use the unified packages instead of adding both admin and public packages. + +## Feature System + +CMS kit uses the [global feature](https://docs.abp.io/en/abp/latest/Global-Features) system for all implemented features. You can use the global feature system to enable all features or only enabled specific features easily. + +To enable all features in the CMS kit, open the `GlobalFeatureConfigurator` class in the `Domain.Shared` project. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.EnableAll(); +}); +``` + +You can only enable specific features such as pages, like below. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.Pages.Enable(); +}); +``` + +## User interface + +### Menu items + +CMS kit module adds the following items to the "Main" menu, under the "CMS" menu item: + +* **Pages**: Page management page. +* **Blogs**: Blog management page. +* **Blog Posts**: Blog post management page. +* **Tags**: Tag management page. +* **Comments**: Comment management page. + +`CmsKitAdminMenus` class has the constants for the menu item names. + +### Pages + +### Page Management + +Pages page is used to manage dynamic pages in the system. + +![pages-page](../images/cmskit-module-pages-page.png) + +You can create or edit an existing page on this page. + +![pages-edit](../images/cmskit-module-pages-edit.png) + +* When you create a page, you can access the created page via `pages/{slug}` URL. + +### Blog Management + +Blogs page is used to create and manage blogs in your system. + +![blogs-page](../images/cmskit-module-blogs-page.png) + +A screenshot from the new blog creation modal: + +![blogs-edit](../images/cmskit-module-blogs-edit.png) + +#### Blog Features + +You can enable or disable a specific feature for blogs by clicking the features action. + +![blogs-feature-action](../images/cmskit-module-blogs-feature-action.png) + +You can select/deselect the desired features for blog posts. + +![features-dialog](../images/cmskit-module-features-dialog.png) + +### Blog Post Management + +When you create blogs, you can manage blog posts on this page. + +![blog-posts-page](../images/cmskit-module-blog-posts-page.png) + +You can create and edit an existing blog post on this page. If you enable specific features such as tags, you can set tags for the blog post on this page. + +![blog-post-edit](../images/cmskit-module-blog-post-edit.png) + +### Tag Management + +CMS Kit provides an extensible tagging mechanism to add tagging capabilities to various places. + +![tags-page](../images/cmskit-module-tags-page.png) + +You can create or edit an existing tag on this page. + +![tag-edit](../images/cmskit-module-tag-edit.png) + +### Comment Management + +CMS Kit provides an extensible commenting mechanism to add comments to various places. You can add comment control to anywhere you want and manage the comments. + +You can view and manage comments on this page. + +![comment-page](../images/cmskit-module-comment-page.png) + +You can manage and view replies on this page. + +![comments-detail](../images/cmskit-module-comments-detail.png) + +### Reactions + +CMS Kit provides an extensible reaction component system to allow users to send reactions to your content. Here how the reactions component looks on a sample page. + +![reactions](../images/cmskit-module-reactions.png) + +You can also customize the reaction icons shown in the reaction component. + +### Ratings + +You can use the rating component to add rating a mechanism to your content. Here how the rating component looks on a sample page. + +![ratings](../images/cmskit-module-ratings.png) diff --git a/docs/en/Modules/cms-kit/Comment-System.md b/docs/en/Modules/cms-kit/Comment-System.md new file mode 100644 index 0000000000..40b9d91a96 --- /dev/null +++ b/docs/en/Modules/cms-kit/Comment-System.md @@ -0,0 +1,117 @@ +# Comment System + +CMS kit provides a **rating** system to add comments feature to any kind of resource, like blog posts, products, etc. This section describes the details of the comments system. + +## Feature + +CMS kit uses the [global feature](https://docs.abp.io/en/abp/latest/Global-Features) system for all implemented features. Commercial startup templates come with all the CMS kit related features are enabled by default, if you create the solution with public website option. If you're installing the CMS kit manually or want to enable the comment feature individually, open the `GlobalFeatureConfigurator` class in the `Domain.Shared` project and place the following code to the `Configure ` method. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.Comments.Enable(); +}); +``` + +# Options + +## CmsKitCommentOptions + +You can use the comment system to add comments to any kind of resources, like blog posts, products etc. Comment system provides a mechanism to group comment definitions by entity types. For example, if you want to use comment system for blog posts and products, you need to define two entity types named `BlogPosts` and `Product`, and add comments under these entity types. + +`CmsKitCommentOptions` can be configured in the domain layer, 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 CommentEntityTypeDefinition("Product")); +}); +``` + +> If you're using the blog feature, the ABP framework defines an entity type for the blog feature automatically. You can easily override or remove the predefined entity types in `Configure` method like shown above. + +`CmsKitCommentOptions` properties: + +- `EntityTypes`: List of defined entity types(`CmsKitCommentOptions`) in the comment system. + +`CommentEntityTypeDefinition` properties: + +- `EntityType`: Name of the entity type. + +# Internals + +## Domain Layer + +#### Aggregates + +This module follows the [Entity Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Entities) guide. + +##### Comment + +A comment represents a written comment from a user. + +- `Comment` (aggregate root): Represents a written comment 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: + +- `ICommentRepository` + +#### Domain services + +This module follows the [Domain Services Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services) guide. + +##### Comment Manager + +`CommentManager` is used to perform some operations for the `Comment` aggregate root. + +### Application layer + +#### Application services + +- `CommentAdminAppService` (implements `ICommentAdminAppService`): Implements the use cases of comment management system, like listing or removing comments etc. +- `CommentPublicAppService` (implements `ICommentPublicAppService`): Implements the use cases of comment management on the public websites, like listing comments, adding comments etc. + +### 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 + +- CmsComments + +#### MongoDB + +##### Collections + +- **CmsComments** + +### MVC UI + +The comment system provides a commenting widget to allow users to send comments to resources on public websites. You can simply place the widget on a page like below. + +```csharp +@await Component.InvokeAsync(typeof(CommentingViewComponent), new +{ + entityType = "entityType", + entityId = "entityId" +}) +``` +You need to specify entity type and entity ID parameters. Entity type represents the group name you specified while defining the comments in the domain module. Entity ID represents the unique identifier for the resource that users send comments to, such as blog post ID or product ID. + +For more information about widgets see [widgets](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Widgets) documentation. \ No newline at end of file diff --git a/docs/en/Modules/cms-kit/Rating-System.md b/docs/en/Modules/cms-kit/Rating-System.md new file mode 100644 index 0000000000..76aaaaa249 --- /dev/null +++ b/docs/en/Modules/cms-kit/Rating-System.md @@ -0,0 +1,116 @@ +# Rating System + +CMS kit provides a **rating** system to to add ratings feature to any kind of resource like blog posts, comments, etc. This section describes the details of the rating system. + +## Feature + +CMS kit uses the [global feature](https://docs.abp.io/en/abp/latest/Global-Features) system for all implemented features. Commercial startup templates come with all the CMS kit related features are enabled by default, if you create the solution with the public website option. If you're installing the CMS kit manually or want to enable the rating feature individually, open the `GlobalFeatureConfigurator` class in the `Domain.Shared` project and place the following code to the `Configure ` method. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.Ratings.Enable(); +}); +``` + +# Options + +## CmsKitRatingOptions + +You can use the rating system to to add ratings to any kind of resource, like blog posts, comments, etc. The rating system provides a mechanism to group ratings by entity types. For example, if you want to use the rating system for products, you need to define an entity type named `Product` and then add ratings under the defined entity type. + +`CmsKitRatingOptions` can be configured in the domain layer, 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 RatingEntityTypeDefinition("Product")); +}); +``` + +> If you're using the blog feature, the ABP framework defines an entity type for the blog feature automatically. You can easily override or remove the predefined entity types in `Configure` method like shown above. + +`CmsKitRatingOptions` properties: + +- `EntityTypes`: List of defined entity types(`RatingEntityTypeDefinition`) in the rating system. + +`RatingEntityTypeDefinition` properties: + +- `EntityType`: Name of the entity type. + +# Internals + +## Domain Layer + +#### Aggregates + +This module follows the [Entity Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Entities) guide. + +##### Rating + +A rating represents a given rating from a user. + +- `Rating` (aggregate root): Represents a given rating 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: + +- `IRatingRepository` + +#### Domain services + +This module follows the [Domain Services Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services) guide. + +##### Reaction Manager + +`RatingManager` is used to perform some operations for the `Rating` aggregate root. + +### Application layer + +#### Application services + +- `RatingPublicAppService` (implements `IRatingPublicAppService`): Implements the use cases of rating 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 + +- CmsRatings + +#### MongoDB + +##### Collections + +- **CmsRatings** + +### MVC UI + +The ratings system provides a rating widget to allow users send ratings to resources in public websites. You can simply place the widget on a page like below. + +```csharp +@await Component.InvokeAsync(typeof(RatingViewComponent), new +{ + entityType = "entityType", + entityId = "entityId" +}) +``` +You need to specify entity type and entity ID parameters. Entity type represents the group name you specified while defining the reactions in the domain module. Entity ID represents the unique identifier for the resource that users give ratings to, such as blog post ID or product ID. + +For more information about widgets see [widgets](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Widgets) documentation. \ No newline at end of file diff --git a/docs/en/Modules/cms-kit/Reaction-System.md b/docs/en/Modules/cms-kit/Reaction-System.md new file mode 100644 index 0000000000..e1219e24c3 --- /dev/null +++ b/docs/en/Modules/cms-kit/Reaction-System.md @@ -0,0 +1,128 @@ +# Reaction System + +CMS kit provides a **reaction** system to add reactions feature to any kind of resource, like blog posts or comments. This section describes the details of the reaction system. + +## Feature + +CMS kit uses the [global feature](https://docs.abp.io/en/abp/latest/Global-Features) system for all implemented features. Commercial startup templates come with all the CMS kit related features are enabled by default, if you create the solution with the public website option. If you're installing the CMS kit manually or want to enable reaction feature individually, open the `GlobalFeatureConfigurator` class in the `Domain.Shared` project and place the following code to the `Configure ` method. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.Reactions.Enable(); +}); +``` + +# Options + +## CmsKitReactionOptions + +You can use the reaction system to to add reactions feature to any kind of resource, like blog posts or comments, etc. The reaction system provides a mechanism to group reactions by entity types. For example, if you want to use the reaction system for products, you need to define an entity type named `Product`, and then add reactions under the defined entity type. + +`CmsKitReactionOptions` can be configured in the domain layer, 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 ReactionEntityTypeDefinition( + "Product", + reactions: new[] + { + new ReactionDefinition(StandardReactions.Smile), + new ReactionDefinition(StandardReactions.ThumbsUp), + new ReactionDefinition(StandardReactions.ThumbsDown), + new ReactionDefinition(StandardReactions.Confused), + new ReactionDefinition(StandardReactions.Eyes), + new ReactionDefinition(StandardReactions.Heart) + })); +}); +``` + +> If you're using the comment or blog features, the ABP framework defines predefined reactions for these features automatically. You can easily override or remove the predefined reactions in `Configure` method like shown above. + +`CmsKitReactionOptions` properties: + +- `EntityTypes`: List of defined entity types(`CmsKitReactionOptions`) in the reaction system. + +`ReactionEntityTypeDefinition` properties:asdas + +- `EntityType`: Name of the entity type. +- `Reactions`: List of defined reactions(`ReactionDefinition`) in the entity type. + +# Internals + +## Domain Layer + +#### Aggregates + +This module follows the [Entity Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Entities) guide. + +##### UserReaction + +A user reaction represents a given reaction from a user. + +- `UserReaction` (aggregate root): Represents a given reaction 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: + +- `IUserReactionRepository` + +#### Domain services + +This module follows the [Domain Services Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services) guide. + +##### Reaction Manager + +`ReactionManager` is used to perform some operations for the `UserReaction` aggregate root. + +### Application layer + +#### Application services + +- `ReactionPublicAppService` (implements `IReactionPublicAppService`): Implements the use cases of reaction 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 + +- CmsUserReactions + +#### MongoDB + +##### Collections + +- **CmsUserReactions** + +### MVC UI + +The reaction system provides a reaction widget to allow users to send reactions to resources on public websites. You can place the widget on a page like below. + +```csharp +@await Component.InvokeAsync(typeof(ReactionSelectionViewComponent), new +{ + entityType = "entityType", + entityId = "entityId" +}) +``` +You need to specify entity type and entity ID parameters. Entity type represents the group name you specified while defining the reactions in the domain module. Entity ID represents the unique identifier for the resource that users give reactions to, such as blog post ID or product ID. + +For more information about widgets see [widgets](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Widgets) documentation. \ No newline at end of file diff --git a/docs/en/Modules/cms-kit/Tag-Management.md b/docs/en/Modules/cms-kit/Tag-Management.md new file mode 100644 index 0000000000..aa4b675dac --- /dev/null +++ b/docs/en/Modules/cms-kit/Tag-Management.md @@ -0,0 +1,135 @@ +# Tag Management + +CMS kit provides a **tag** system to tag any kind of resources, like blog posts. This section describes the details of the tag system. + +## Feature + +CMS kit uses the [global feature](https://docs.abp.io/en/abp/latest/Global-Features) system for all implemented features. Commercial startup templates come with all the CMS kit related features are enabled by default, if you create the solution with public website option. If you're installing the CMS kit manually or want to enable the tag management feature individually, open the `GlobalFeatureConfigurator` class in the `Domain.Shared` project and place the following code to the `Configure ` method. + +```csharp +GlobalFeatureManager.Instance.Modules.CmsKit(cmsKit => +{ + cmsKit.Tags.Enable(); +}); +``` + +# Options + +## CmsKitTagOptions + +You can use the tag system to tag any kind of resources, like blog posts, products, etc. The tag system provides a mechanism to group tags by entity types. For example, if you want to use the tag system for blog posts and products, you need to define two entity types named `BlogPosts` and `Product` and add tags under these entity types. + +`CmsKitTagOptions` can be configured in the domain layer, 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 TagEntityTypeDefiniton("Product")); +}); +``` + +> If you're using the blog feature, the ABP framework defines an entity type for the blog feature automatically. You can easily override or remove the predefined entity types in `Configure` method like shown above. + +`CmsKitTagOptions` properties: + +- `EntityTypes`: List of defined entity types(`TagEntityTypeDefiniton`) in the tag system. + +`TagEntityTypeDefiniton` properties: + +- `EntityType`: Name of the entity type. +- `DisplayName`: Display name of the entity type. You can use a user friendly display name to show entity type definition on the admin website. +- `CreatePolicies`: List of policy names allowing users to create tags under the entity type. +- `UpdatePolicies`: List of policy names allowing users to update tags under the entity type. +- `DeletePolicies`: List of policy names allowing users to delete tags under the entity type. + +# Internals + +## Domain Layer + +#### Aggregates + +This module follows the [Entity Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Entities) guide. + +##### Tag + +A tag represents a tag under the entity type. + +- `Tag` (aggregate root): Represents a tag in the system. + +##### EntityTag + +An entity tag represents a connection between the tag and the tagged entity. + +- `EntityTag`(entity): Represents a connection between the tag and the tagged entity. + +#### 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: + +- `ITagRepository` +- `IEntityTagRepository` + +#### Domain services + +This module follows the [Domain Services Best Practices & Conventions](https://docs.abp.io/en/abp/latest/Best-Practices/Domain-Services) guide. + +##### Tag Manager + +`TagManager` is used to perform some operations for the `Tag` aggregate root. + +##### Entity Tag Manager + +`EntityTagManager` is used to perform some operations for the `EntityTag` entity. + +### Application layer + +#### Application services + +- `TagAdminAppService` (implements `ITagAdminAppService`): Implements the use cases of tag management. +- `EntityTagAdminAppService` (implements `IEntityTagAdminAppService`): Implements the use cases of entity tag relationship. +- `TagAppService` (implements `ITagAppService`): Implements the use cases of entity tag relationship for public websites. + +### 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 + +- CmsTags +- CmsEntityTags + - CmsTags + +#### MongoDB + +##### Collections + +- **CmsTags** +- **CmsEntityTags** + +### MVC UI + +The tag system provides a tag widget to display associated tags of the resource on public websites. You can simply place the widget on a page like below. + +```csharp +@await Component.InvokeAsync(typeof(TagViewComponent), new +{ + entityType = "entityType", + entityId = "entityId" +}) +``` + +For more information about widgets see [widgets](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Widgets) documentation. \ No newline at end of file diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index a12d216b3d..5bcdfe7730 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -292,6 +292,24 @@ Configure(options => ### Advanced Topics +### Controlling the Multi-Tenancy + +If your solution is [multi-tenant](Multi-Tenancy.md), tenants may have **separate databases**, you have **multiple** `DbContext` classes in your solution and some of your `DbContext` classes should be usable **only from the host side**, it is suggested to add `[IgnoreMultiTenancy]` attribute on your `DbContext` class. In this case, ABP guarantees that the related `DbContext` always uses the host [connection string](Connection-Strings.md), even if you are in a tenant context. + +**Example:** + +````csharp +[IgnoreMultiTenancy] +public class MyDbContext : AbpMongoDbContext +{ + ... +} +```` + +Do not use the `[IgnoreMultiTenancy]` attribute if any one of your entities in your `DbContext` can be persisted in a tenant database. + +> When you use repositories, ABP already uses the host database for the entities don't implement the `IMultiTenant` interface. So, most of time you don't need to `[IgnoreMultiTenancy]` attribute if you are using the repositories to work with the database. + #### Set Default Repository Classes Default generic repositories are implemented by `MongoDbRepository` class by default. You can create your own implementation and use it for default repository implementation. diff --git a/docs/en/UI/Angular/Page-Component.md b/docs/en/UI/Angular/Page-Component.md index a9e9857142..912b516baf 100644 --- a/docs/en/UI/Angular/Page-Component.md +++ b/docs/en/UI/Angular/Page-Component.md @@ -1,4 +1,4 @@ -# Page Component +# Page Component ABP provides a component that wraps your content with some built-in components to reduce the amount of code you need to write. @@ -11,7 +11,7 @@ Let's look at the following example without `abp-page` component. ```html
-

{{ '::Dashboard' | abpLocalization }}

+

{%{{{ '::Dashboard' | abpLocalization }}}%}

``` -## Page Parts +## Page Parts PageComponent divides the template shown above into three parts, `title`, `breadcrumb`, `toolbar`. Each can be configured separately. There, also, is an enum exported from the package that describes each part. @@ -67,41 +67,35 @@ And change the template of `dashboard.component.ts` to the following: ``` -## Inputs +## Inputs * title: `string`: Will be be rendered within `h1.content-header-title`. If not provided, the parent `div` will not be rendered * breadcrumb: `boolean`: Determines whether to render `abp-breadcrumb`. Default is `true`. * toolbar: `any`: Will be passed into `abp-page-toolbar` component through `record` input. If your page does not contain `abp-page-toolbar`, you can simply omit this field. -## Overriding template +## Overriding template If you need to replace the template of any part, you can use the following sub-components. ```html - -
-

Custom Title

-
+ +

Custom Title

- -
- -
+ + - -
- -
+ +
``` You do not have to provide them all. You can just use which one you need to replace. These components have priority over the inputs declared above. If you use these components, you can omit the inputs. -## PagePartDirective +## PagePartDirective `PageModule` provides a structural directive that is used internally within `PageComponent` and can also be used externally. diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 0a7722de78..8aae7ac96b 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -39,31 +39,6 @@ } ] }, - { - "text": "Startup Templates", - "items": [ - { - "text": "Overall", - "path": "Startup-Templates/Index.md" - }, - { - "text": "Application", - "path": "Startup-Templates/Application.md" - }, - { - "text": "Module", - "path": "Startup-Templates/Module.md" - }, - { - "text": "Console", - "path": "Startup-Templates/Console.md" - }, - { - "text": "WPF", - "path": "Startup-Templates/WPF.md" - } - ] - }, { "text": "Tutorials", "items": [ @@ -122,6 +97,45 @@ } ] }, + { + "text": "CLI", + "path": "CLI.md", + "items": [ + { + "text": "Build Command", + "path": "CLI-BuildCommand.md" + }, + { + "text": "Create Solution Sample Commands", + "path": "CLI-New-Command-Samples.md" + } + ] + }, + { + "text": "Startup Templates", + "items": [ + { + "text": "Overall", + "path": "Startup-Templates/Index.md" + }, + { + "text": "Application", + "path": "Startup-Templates/Application.md" + }, + { + "text": "Module", + "path": "Startup-Templates/Module.md" + }, + { + "text": "Console", + "path": "Startup-Templates/Console.md" + }, + { + "text": "WPF", + "path": "Startup-Templates/WPF.md" + } + ] + }, { "text": "Fundamentals", "items": [ @@ -1292,25 +1306,6 @@ } ] }, - { - "text": "Reference", - "items": [ - { - "text": "CLI", - "path": "CLI.md", - "items": [ - { - "text": "Build Command", - "path": "CLI-BuildCommand.md" - }, - { - "text": "Create Solution Sample Commands", - "path": "CLI-New-Command-Samples.md" - } - ] - } - ] - }, { "text": "Contribution Guide", "path": "Contribution/Index.md" diff --git a/docs/en/images/cmskit-module-blog-post-edit.png b/docs/en/images/cmskit-module-blog-post-edit.png new file mode 100644 index 0000000000..528bc6b499 Binary files /dev/null and b/docs/en/images/cmskit-module-blog-post-edit.png differ diff --git a/docs/en/images/cmskit-module-blog-posts-page.png b/docs/en/images/cmskit-module-blog-posts-page.png new file mode 100644 index 0000000000..64d5d0e934 Binary files /dev/null and b/docs/en/images/cmskit-module-blog-posts-page.png differ diff --git a/docs/en/images/cmskit-module-blogs-edit.png b/docs/en/images/cmskit-module-blogs-edit.png new file mode 100644 index 0000000000..b2da4e5b5a Binary files /dev/null and b/docs/en/images/cmskit-module-blogs-edit.png differ diff --git a/docs/en/images/cmskit-module-blogs-feature-action.png b/docs/en/images/cmskit-module-blogs-feature-action.png new file mode 100644 index 0000000000..c0f0b1d3f4 Binary files /dev/null and b/docs/en/images/cmskit-module-blogs-feature-action.png differ diff --git a/docs/en/images/cmskit-module-blogs-page.png b/docs/en/images/cmskit-module-blogs-page.png new file mode 100644 index 0000000000..6c150429b7 Binary files /dev/null and b/docs/en/images/cmskit-module-blogs-page.png differ diff --git a/docs/en/images/cmskit-module-comment-page.png b/docs/en/images/cmskit-module-comment-page.png new file mode 100644 index 0000000000..6647a6dd09 Binary files /dev/null and b/docs/en/images/cmskit-module-comment-page.png differ diff --git a/docs/en/images/cmskit-module-comments-detail.png b/docs/en/images/cmskit-module-comments-detail.png new file mode 100644 index 0000000000..686a70e1f8 Binary files /dev/null and b/docs/en/images/cmskit-module-comments-detail.png differ diff --git a/docs/en/images/cmskit-module-features-dialog.png b/docs/en/images/cmskit-module-features-dialog.png new file mode 100644 index 0000000000..23aa2aa17a Binary files /dev/null and b/docs/en/images/cmskit-module-features-dialog.png differ diff --git a/docs/en/images/cmskit-module-pages-edit.png b/docs/en/images/cmskit-module-pages-edit.png new file mode 100644 index 0000000000..cca074fdac Binary files /dev/null and b/docs/en/images/cmskit-module-pages-edit.png differ diff --git a/docs/en/images/cmskit-module-pages-page.png b/docs/en/images/cmskit-module-pages-page.png new file mode 100644 index 0000000000..b64915493c Binary files /dev/null and b/docs/en/images/cmskit-module-pages-page.png differ diff --git a/docs/en/images/cmskit-module-ratings.png b/docs/en/images/cmskit-module-ratings.png new file mode 100644 index 0000000000..f97c934f15 Binary files /dev/null and b/docs/en/images/cmskit-module-ratings.png differ diff --git a/docs/en/images/cmskit-module-reactions.png b/docs/en/images/cmskit-module-reactions.png new file mode 100644 index 0000000000..b915b85f91 Binary files /dev/null and b/docs/en/images/cmskit-module-reactions.png differ diff --git a/docs/en/images/cmskit-module-tag-edit.png b/docs/en/images/cmskit-module-tag-edit.png new file mode 100644 index 0000000000..eb1ebb3a52 Binary files /dev/null and b/docs/en/images/cmskit-module-tag-edit.png differ diff --git a/docs/en/images/cmskit-module-tags-page.png b/docs/en/images/cmskit-module-tags-page.png new file mode 100644 index 0000000000..f41b6ecbd0 Binary files /dev/null and b/docs/en/images/cmskit-module-tags-page.png differ diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/AppWithoutAuth.razor b/framework/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/AppWithoutAuth.razor new file mode 100644 index 0000000000..e9bf522595 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/AppWithoutAuth.razor @@ -0,0 +1,14 @@ +@using Microsoft.Extensions.Options +@using Volo.Abp.AspNetCore.Components.Web.Theming.Routing +@inject IOptions RouterOptions + + + + + + +

Sorry, there's nothing at this address.

+
+
+
\ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs index 281b2f7ded..787bf5cb6e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs @@ -1,5 +1,6 @@ using System.Security.Claims; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; @@ -14,21 +15,29 @@ namespace Volo.Abp.AspNetCore.Components.Web.Security { public ClaimsPrincipal Principal { get; private set; } + [CanBeNull] private readonly AuthenticationStateProvider _authenticationStateProvider; - public AbpComponentsClaimsCache(IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) + public AbpComponentsClaimsCache( + IClientScopeServiceProviderAccessor serviceProviderAccessor) { - _authenticationStateProvider = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService(); - _authenticationStateProvider.AuthenticationStateChanged += async (task) => + _authenticationStateProvider = serviceProviderAccessor.ServiceProvider.GetService(); + if (_authenticationStateProvider != null) { - Principal = (await task).User; - }; + _authenticationStateProvider.AuthenticationStateChanged += async (task) => + { + Principal = (await task).User; + }; + } } public virtual async Task InitializeAsync() { - var authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync(); - Principal = authenticationState.User; + if (_authenticationStateProvider != null) + { + var authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync(); + Principal = authenticationState.User; + } } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BasicThemeToolbarContributor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BasicThemeToolbarContributor.cs index ab2a73a8dd..b853670d39 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BasicThemeToolbarContributor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BasicThemeToolbarContributor.cs @@ -1,7 +1,10 @@ using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Components.Web.BasicTheme.Themes.Basic; using Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; using Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic; +using Volo.Abp.DependencyInjection; namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme { @@ -12,7 +15,15 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme if (context.Toolbar.Name == StandardToolbars.Main) { context.Toolbar.Items.Add(new ToolbarItem(typeof(LanguageSwitch))); - context.Toolbar.Items.Add(new ToolbarItem(typeof(LoginDisplay))); + + //TODO: Can we find a different way to understand if authentication was configured or not? + var authenticationStateProvider = context.ServiceProvider + .GetService(); + + if (authenticationStateProvider != null) + { + context.Toolbar.Items.Add(new ToolbarItem(typeof(LoginDisplay))); + } } return Task.CompletedTask; diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor index eebfb501a5..1b084a60ce 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor @@ -1,13 +1,12 @@ -@using Microsoft.AspNetCore.Components.WebAssembly.Authentication -@using Microsoft.Extensions.Localization +@using Microsoft.Extensions.Localization @using Volo.Abp.Users @using Volo.Abp.MultiTenancy @using global::Localization.Resources.AbpUi +@inherits AbpComponentBase @inject ICurrentUser CurrentUser @inject ICurrentTenant CurrentTenant @inject IJSRuntime JsRuntime @inject NavigationManager Navigation -@inject SignOutSessionStateManager SignOutManager @inject IStringLocalizer UiLocalizer @@ -38,24 +37,4 @@ @UiLocalizer["Login"] - -@code{ - - private async Task NavigateToAsync(string uri, string target = null) - { - if (target == "_blank") - { - await JsRuntime.InvokeVoidAsync("open", uri, target); - } - else - { - Navigation.NavigateTo(uri); - } - } - - private async Task BeginSignOut() - { - await SignOutManager.SetSignOutState(); - await NavigateToAsync("authentication/logout"); - } -} + \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor.cs b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor.cs index addef3dc80..0ec9caa23c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor.cs @@ -1,8 +1,11 @@ using System; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Routing; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using Microsoft.JSInterop; using Volo.Abp.UI.Navigation; namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic @@ -11,23 +14,32 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic { [Inject] protected IMenuManager MenuManager { get; set; } - - [Inject] - protected AuthenticationStateProvider AuthenticationStateProvider { get; set; } - protected ApplicationMenu Menu { get; set; } + [CanBeNull] + protected AuthenticationStateProvider AuthenticationStateProvider; + + [CanBeNull] + protected SignOutSessionStateManager SignOutManager; + protected ApplicationMenu Menu { get; set; } + protected override async Task OnInitializedAsync() { Menu = await MenuManager.GetAsync(StandardMenus.User); Navigation.LocationChanged += OnLocationChanged; - AuthenticationStateProvider.AuthenticationStateChanged += async (task) => + LazyGetService(ref AuthenticationStateProvider); + LazyGetService(ref SignOutManager); + + if (AuthenticationStateProvider != null) { - Menu = await MenuManager.GetAsync(StandardMenus.User); - await InvokeAsync(StateHasChanged); - }; + AuthenticationStateProvider.AuthenticationStateChanged += async (task) => + { + Menu = await MenuManager.GetAsync(StandardMenus.User); + await InvokeAsync(StateHasChanged); + }; + } } protected virtual void OnLocationChanged(object sender, LocationChangedEventArgs e) @@ -39,5 +51,26 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic { Navigation.LocationChanged -= OnLocationChanged; } + + private async Task NavigateToAsync(string uri, string target = null) + { + if (target == "_blank") + { + await JsRuntime.InvokeVoidAsync("open", uri, target); + } + else + { + Navigation.NavigateTo(uri); + } + } + + private async Task BeginSignOut() + { + if (SignOutManager != null) + { + await SignOutManager.SetSignOutState(); + await NavigateToAsync("authentication/logout"); + } + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs index bc4db53002..163b2217fa 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs @@ -108,6 +108,18 @@ namespace Volo.Abp.AspNetCore.Components return reference; } + + protected TService LazyGetService(ref TService reference) => LazyGetService(typeof(TService), ref reference); + + protected TRef LazyGetService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + reference = (TRef)ScopedServices.GetService(serviceType); + } + + return reference; + } protected TService LazyGetNonScopedRequiredService(ref TService reference) => LazyGetNonScopedRequiredService(typeof(TService), ref reference); @@ -120,6 +132,18 @@ namespace Volo.Abp.AspNetCore.Components return reference; } + + protected TService LazyGetNonScopedService(ref TService reference) => LazyGetNonScopedService(typeof(TService), ref reference); + + protected TRef LazyGetNonScopedService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + reference = (TRef)NonScopedServices.GetService(serviceType); + } + + return reference; + } [Inject] protected IServiceProvider NonScopedServices { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs index d4f0ff2187..f47edab789 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs @@ -48,7 +48,12 @@ namespace Pages.Abp.MultiTenancy else { var tenant = await TenantStore.FindAsync(Input.Name); - if (tenant == null || !tenant.IsActive) + if (tenant == null) + { + throw new UserFriendlyException(L["GivenTenantIsNotExist", Input.Name]); + } + + if (!tenant.IsActive) { throw new UserFriendlyException(L["GivenTenantIsNotAvailable", Input.Name]); } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json index 7e5f26edc6..fafc04a6ee 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json @@ -1,6 +1,7 @@ { "culture": "ar", "texts": { + "GivenTenantIsNotExist": "المستأجر المحدد غير موجود: {0}", "GivenTenantIsNotAvailable": "الجهة المحددة غير متاحة: {0}", "Tenant": "الجهة", "Switch": "تغيير", @@ -9,4 +10,4 @@ "SwitchTenant": "تغيير الجهة", "NotSelected": "غير محدد" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json index 05457dfa16..0f63728c77 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json @@ -1,6 +1,7 @@ { "culture": "cs", "texts": { + "GivenTenantIsNotExist": "Vybraný tenant neexistuje: {0}", "GivenTenantIsNotAvailable": "Zvolený tenant není dostupný: {0}", "Tenant": "Tenant", "Switch": "změnit", diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de-DE.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de-DE.json index 3b1f12c29d..a0bb43999a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de-DE.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de-DE.json @@ -1,6 +1,7 @@ { "culture": "de-DE", "texts": { + "GivenTenantIsNotExist": "Der angegebene Client ist nicht vorhanden: {0}", "GivenTenantIsNotAvailable": "Der angegebene Mandant ist nicht verfügbar: {0}", "Tenant": "Mandant", "Switch": "wechseln", @@ -9,4 +10,4 @@ "SwitchTenant": "Mandant wechseln", "NotSelected": "Nicht ausgewählt" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de.json index 98909d9847..029e7ed76a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de.json @@ -1,6 +1,7 @@ { "culture": "de", "texts": { + "GivenTenantIsNotExist": "Der angegebene Client ist nicht vorhanden: {0}", "GivenTenantIsNotAvailable": "Der angegebene Mandant ist nicht verfügbar: {0}", "Tenant": "Mandant", "Switch": "wechseln", @@ -9,4 +10,4 @@ "SwitchTenant": "Mandant wechseln", "NotSelected": "Nicht ausgewählt" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en-GB.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en-GB.json index be5593fbd3..e93e7ef25e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en-GB.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en-GB.json @@ -1,6 +1,7 @@ { "culture": "en-GB", "texts": { + "GivenTenantIsNotExist": "Given tenant is not exist: {0}", "GivenTenantIsNotAvailable": "Given tenant is not available: {0}", "Tenant": "Tenant", "Switch": "switch", @@ -9,4 +10,4 @@ "SwitchTenant": "Switch tenant", "NotSelected": "Not selected" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json index d1a9f1f09e..25516ba765 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json @@ -1,6 +1,7 @@ { "culture": "en", "texts": { + "GivenTenantIsNotExist": "Given tenant is not exist: {0}", "GivenTenantIsNotAvailable": "Given tenant is not available: {0}", "Tenant": "Tenant", "Switch": "switch", @@ -9,4 +10,4 @@ "SwitchTenant": "Switch tenant", "NotSelected": "Not selected" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es-mx.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es-mx.json index 746f1dec0f..5cd08ab345 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es-mx.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es-mx.json @@ -1,6 +1,7 @@ { "culture": "es-MX", "texts": { + "GivenTenantIsNotExist": "El inquilino {0} no existe", "GivenTenantIsNotAvailable": "El inquilino {0} no está disponible", "Tenant": "Inquilino", "Switch": "cambiar", diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es.json index d6fd84dcbe..bd2d82a2d0 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es.json @@ -1,6 +1,7 @@ { "culture": "es", "texts": { + "GivenTenantIsNotExist": "El inquilino {0} no existe", "GivenTenantIsNotAvailable": "El inquilino {0} no está disponible", "Tenant": "Inquilino", "Switch": "cambiar", @@ -9,4 +10,4 @@ "SwitchTenantHint": "Deje en blanco el campo nombre para cambiar el lado del host.", "NotSelected": "No seleccionado" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fi.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fi.json index 7cbeabe8cd..d6fb04eb4c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fi.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fi.json @@ -1,6 +1,7 @@ { "culture": "fi", "texts": { + "GivenTenantIsNotExist": "Annettua vuokralaista ei ole olemassa: {0}", "GivenTenantIsNotAvailable": "Annettua vuokralaista ei ole saatavilla: {0}", "Tenant": "Vuokralainen", "Switch": "vaihtaa", @@ -9,4 +10,4 @@ "SwitchTenant": "Vaihda vuokralainen", "NotSelected": "Ei valittu" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fr.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fr.json index dbd17957fc..6fe00242b5 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fr.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fr.json @@ -1,6 +1,7 @@ { "culture": "fr", "texts": { + "GivenTenantIsNotExist": "Adott bérlő nem létezik: {0}", "GivenTenantIsNotAvailable": "Le Tenant donné n'est pas disponible: {0}", "Tenant": "Tenant", "Switch": "Changer", @@ -9,4 +10,4 @@ "SwitchTenant": "Changer le Tenant", "NotSelected": "Non séléctionné" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json index 057e9d2968..108b3520d7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json @@ -1,6 +1,7 @@ { "culture": "hu", "texts": { + "GivenTenantIsNotExist": "Adott bérlő nem létezik: {0}", "GivenTenantIsNotAvailable": "A megadott előfizető nem érhető el: {0}", "Tenant": "Előfizető", "Switch": "váltás", @@ -9,4 +10,4 @@ "SwitchTenant": "Előfizető váltás", "NotSelected": "Nincs kiválasztva" } - } \ No newline at end of file + } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/nl.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/nl.json index ea15166b9e..3cbb689f03 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/nl.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/nl.json @@ -1,6 +1,7 @@ { "culture": "nl", "texts": { + "GivenTenantIsNotExist": "Opgegeven huurder bestaat niet: {0}", "GivenTenantIsNotAvailable": "Gegeven klant is niet beschikbaar: {0}", "Tenant": "Klant", "Switch": "Schakel over", @@ -9,4 +10,4 @@ "SwitchTenant": "Klant wisselen", "NotSelected": "Niet geselecteerd" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json index 438d770a7c..79d3e9640d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json @@ -1,6 +1,7 @@ { "culture": "pl-PL", "texts": { + "GivenTenantIsNotExist": "Podany najemca nie istnieje: {0}", "GivenTenantIsNotAvailable": "Podany tenant jest niedostępny: {0}", "Tenant": "Tenant", "Switch": "zmień", @@ -8,4 +9,4 @@ "SwitchTenantHint": "Pozostaw pole nazwy puste, aby przełączyć się na stronę hosta.", "NotSelected": "Nie wybrano" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json index 8ac517d594..95396abca1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json @@ -1,6 +1,7 @@ { "culture": "pt-BR", "texts": { + "GivenTenantIsNotExist": "O inquilino dado não existe: {0}", "GivenTenantIsNotAvailable": "Inquilino não está disponível: {0}", "Tenant": "Inquilino", "Switch": "trocar", @@ -9,4 +10,4 @@ "SwitchTenant": "Alterar Inquilino", "NotSelected": "Não selecionado" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ru.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ru.json index 04b2e5cf85..5492a7d7bb 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ru.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ru.json @@ -1,6 +1,7 @@ { "culture": "ru", "texts": { + "GivenTenantIsNotExist": "Данный арендатор не существует: {0}", "GivenTenantIsNotAvailable": "Данный арендатор недоступен: {0}", "Tenant": "Арендатор", "Switch": "переключиться", diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/sl.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/sl.json index 51938a569e..b1a07d3ee9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/sl.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/sl.json @@ -1,6 +1,7 @@ { "culture": "sl", "texts": { + "GivenTenantIsNotExist": "Dani najemnik ne obstaja: {0}", "GivenTenantIsNotAvailable": "Najemnik ni na voljo: {0}", "Tenant": "Najemnik", "Switch": "Zamenjaj", diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/tr.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/tr.json index 994579e934..b5b6c374c1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/tr.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/tr.json @@ -1,6 +1,7 @@ { "culture": "tr", "texts": { + "GivenTenantIsNotExist": "İstenilen müşteri bulunamadı: {0}", "GivenTenantIsNotAvailable": "İstenilen müşteri bulunamadı: {0}", "Tenant": "Müşteri", "Switch": "değiştir", @@ -9,4 +10,4 @@ "SwitchTenant": "Müşteri değiştir", "NotSelected": "Seçili değil" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/vi.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/vi.json index 975e1b4590..b8ad08bb82 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/vi.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/vi.json @@ -1,6 +1,7 @@ { "culture": "vi", "texts": { + "GivenTenantIsNotExist": "Người thuê đã cho không tồn tại: {0}", "GivenTenantIsNotAvailable": "Người thuê không có sẵn: {0}", "Tenant": "Người thuê", "Switch": "Chuyển đổi", diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hans.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hans.json index 31588cbebd..0e86d66293 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hans.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hans.json @@ -1,6 +1,7 @@ { "culture": "zh-Hans", "texts": { + "GivenTenantIsNotExist": "给定的租户不存在: {0}", "GivenTenantIsNotAvailable": "给定的租户不可用: {0}", "Tenant": "租户", "Switch": "切换", @@ -9,4 +10,4 @@ "SwitchTenant": "切换租户", "NotSelected": "未选中" } - } \ No newline at end of file + } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hant.json b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hant.json index 12eea67007..10a0da1023 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hant.json +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hant.json @@ -1,6 +1,7 @@ { "culture": "zh-Hant", "texts": { + "GivenTenantIsNotExist": "指定的租戶不存在: {0}", "GivenTenantIsNotAvailable": "指定的租戶不可用: {0}", "SwitchTenant": "切換租戶", "Name": "名稱", @@ -9,4 +10,4 @@ "Switch": "切換", "NotSelected": "尚未選定" } -} \ No newline at end of file +} diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs index 5cce51ec51..f02ce38c74 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -22,7 +22,16 @@ namespace Volo.Abp.Authorization.Permissions { Check.NotNullOrEmpty(models, nameof(models)); - _models.AddRange(models); + foreach (var model in models) + { + if (!_models.Any(x => x.State.GetType() == model.State.GetType() && + x.RequiresAll == model.RequiresAll && + x.Permissions.SequenceEqual(model.Permissions))) + { + _models.Add(model); + } + } + return this; } diff --git a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs index 96caf956b1..776a8b9a7a 100644 --- a/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs @@ -35,7 +35,11 @@ namespace Autofac.Builder return registrationBuilder; } - private static IRegistrationBuilder InvokeRegistrationActions(this IRegistrationBuilder registrationBuilder, ServiceRegistrationActionList registrationActionList, Type serviceType, Type implementationType) + private static IRegistrationBuilder InvokeRegistrationActions( + this IRegistrationBuilder registrationBuilder, + ServiceRegistrationActionList registrationActionList, + Type serviceType, + Type implementationType) where TActivatorData : ReflectionActivatorData { var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType); @@ -48,6 +52,7 @@ namespace Autofac.Builder if (serviceRegistredArgs.Interceptors.Any()) { registrationBuilder = registrationBuilder.AddInterceptors( + registrationActionList, serviceType, serviceRegistredArgs.Interceptors ); @@ -71,10 +76,12 @@ namespace Autofac.Builder return registrationBuilder; } - private static IRegistrationBuilder AddInterceptors( - this IRegistrationBuilder registrationBuilder, - Type serviceType, - IEnumerable interceptors) + private static IRegistrationBuilder + AddInterceptors( + this IRegistrationBuilder registrationBuilder, + ServiceRegistrationActionList serviceRegistrationActionList, + Type serviceType, + IEnumerable interceptors) where TActivatorData : ReflectionActivatorData { if (serviceType.IsInterface) @@ -83,6 +90,11 @@ namespace Autofac.Builder } else { + if (serviceRegistrationActionList.IsClassInterceptorsDisabled) + { + return registrationBuilder; + } + (registrationBuilder as IRegistrationBuilder)?.EnableClassInterceptors(); } diff --git a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs index 6ce4a10a53..19ace62aca 100644 --- a/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs +++ b/framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs @@ -485,16 +485,26 @@ namespace Volo.Abp.BlazoriseUI try { await CheckDeletePolicyAsync(); - + await OnDeletingEntityAsync(); await AppService.DeleteAsync(entity.Id); - await GetEntitiesAsync(); - await InvokeAsync(StateHasChanged); + await OnDeletedEntityAsync(); } catch (Exception ex) { await HandleErrorAsync(ex); } } + + protected virtual Task OnDeletingEntityAsync() + { + return Task.CompletedTask; + } + + protected virtual async Task OnDeletedEntityAsync() + { + await GetEntitiesAsync(); + await InvokeAsync(StateHasChanged); + } protected virtual string GetDeleteConfirmationMessage(TListViewModel entity) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs index 1dd29e5df7..b7cd95a3e0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using Volo.Abp.DependencyInjection; namespace Volo.Abp.Cli.Args @@ -71,6 +72,11 @@ namespace Volo.Abp.Cli.Args return commandLineArgs; } + public CommandLineArgs Parse(string lineText) + { + return Parse(GetArgsArrayFromLine(lineText)); + } + private static bool IsOptionName(string argument) { return argument.StartsWith("-") || argument.StartsWith("--"); @@ -100,5 +106,46 @@ namespace Volo.Abp.Cli.Args throw new ArgumentException("Option names should start with '-' or '--'."); } + + private static string[] GetArgsArrayFromLine(string lineText) + { + var args = new List(); + var currentArgBuilder = new StringBuilder(); + string currentArg = null; + bool isInQuotes = false; + for (int i = 0; i < lineText.Length; i++) + { + var c = lineText[i]; + if (c == ' ' && !isInQuotes) + { + currentArg = currentArgBuilder.ToString(); + if (!currentArg.IsNullOrWhiteSpace()) + { + args.Add(currentArg); + } + + currentArgBuilder = new StringBuilder(); + } + else + { + if (c == '\"') + { + isInQuotes = !isInQuotes; + } + else + { + currentArgBuilder.Append(c); + } + } + } + + currentArg = currentArgBuilder.ToString(); + if (!currentArg.IsNullOrWhiteSpace()) + { + args.Add(currentArg); + } + + return args.ToArray(); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs index c467541210..4e8ee7cd77 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs @@ -3,5 +3,7 @@ public interface ICommandLineArgumentParser { CommandLineArgs Parse(string[] args); + + CommandLineArgs Parse(string lineText); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs index b2588f4502..785afbca37 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs @@ -3,9 +3,11 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using NuGet.Versioning; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Text; using System.Threading.Tasks; using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Commands; @@ -141,11 +143,11 @@ namespace Volo.Abp.Cli lineText = lineText.Substring(0, lineText.IndexOf('#')); } - var args = CommandLineArgumentParser.Parse(lineText.Split(' ')); + var args = CommandLineArgumentParser.Parse(lineText); await RunInternalAsync(args); } } - + private async Task RunInternalAsync(CommandLineArgs commandLineArgs) { var commandType = CommandSelector.Select(commandLineArgs); diff --git a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs index 59cbdec860..41676f00d5 100644 --- a/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs +++ b/framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs @@ -28,6 +28,16 @@ namespace Microsoft.Extensions.DependencyInjection return actionList; } + + public static void DisableAbpClassInterceptors(this IServiceCollection services) + { + GetOrCreateRegistrationActionList(services).IsClassInterceptorsDisabled = true; + } + + public static bool IsAbpClassInterceptorsDisabled(this IServiceCollection services) + { + return GetOrCreateRegistrationActionList(services).IsClassInterceptorsDisabled; + } // OnExposing diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceRegistrationActionList.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceRegistrationActionList.cs index bb72d28929..19ab6cdba2 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceRegistrationActionList.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceRegistrationActionList.cs @@ -5,6 +5,6 @@ namespace Volo.Abp.DependencyInjection { public class ServiceRegistrationActionList : List> { - + public bool IsClassInterceptorsDisabled { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs index 92c6935900..75dd6a5dc4 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs @@ -39,6 +39,11 @@ namespace Volo.Abp.EntityFrameworkCore DefaultConfigureAction = action; } + public bool IsConfiguredDefault() + { + return DefaultConfigureAction != null; + } + public void PreConfigure([NotNull] Action> action) where TDbContext : AbpDbContext { @@ -60,6 +65,16 @@ namespace Volo.Abp.EntityFrameworkCore ConfigureActions[typeof(TDbContext)] = action; } + + public bool IsConfigured() + { + return IsConfigured(typeof(TDbContext)); + } + + public bool IsConfigured(Type dbContextType) + { + return ConfigureActions.ContainsKey(dbContextType); + } internal Type GetReplacedTypeOrSelf(Type dbContextType) { diff --git a/framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator.cs b/framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator.cs index c86fc095c7..3f74918d98 100644 --- a/framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator.cs +++ b/framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator.cs @@ -1,6 +1,9 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using IdentityModel.Client; +using JetBrains.Annotations; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; using Volo.Abp.Http.Client.Authentication; using Volo.Abp.IdentityModel; @@ -8,16 +11,18 @@ using Volo.Abp.IdentityModel; namespace Volo.Abp.Http.Client.IdentityModel.WebAssembly { [Dependency(ReplaceServices = true)] - public class AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator : IdentityModelRemoteServiceHttpClientAuthenticator + public class AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator + : IdentityModelRemoteServiceHttpClientAuthenticator { + [CanBeNull] protected IAccessTokenProvider AccessTokenProvider { get; } public AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator( IIdentityModelAuthenticationService identityModelAuthenticationService, - IAccessTokenProvider accessTokenProvider) + IServiceProvider serviceProvider) : base(identityModelAuthenticationService) { - AccessTokenProvider = accessTokenProvider; + AccessTokenProvider = serviceProvider.GetService(); } public override async Task Authenticate(RemoteServiceHttpClientAuthenticateContext context) @@ -37,6 +42,11 @@ namespace Volo.Abp.Http.Client.IdentityModel.WebAssembly protected virtual async Task GetAccessTokenFromAccessTokenProviderOrNullAsync() { + if (AccessTokenProvider == null) + { + return null; + } + var result = await AccessTokenProvider.RequestAccessToken(); if (result.Status != AccessTokenResultStatus.Success) { diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs index d54ee7104a..237953db4a 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using JetBrains.Annotations; using Volo.Abp.Data; @@ -68,7 +69,9 @@ namespace Volo.Abp.ObjectExtending [NotNull] public virtual ImmutableList GetProperties() { - return Properties.Values.ToImmutableList(); + return Properties.OrderBy(t=>t.Key) + .Select(t=>t.Value) + .ToImmutableList(); } [CanBeNull] diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json index 076d4b2517..ff846b3f18 100644 --- a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json @@ -11,7 +11,7 @@ "Close": "Cerrar", "Save": "Guardar", "SavingWithThreeDot": "Guardando...", - "Actions": "Acciones", + "Actions": "Opciones", "Delete": "Borrar", "Edit": "Editar", "Refresh": "Actualizar", @@ -20,10 +20,10 @@ "ProcessingWithThreeDot": "Procesando...", "LoadingWithThreeDot": "Cargardo...", "Welcome": "Bienvenido", - "Login": "Iniciar Sesión", + "Login": "Iniciar sesión", "Register": "Registrarse", "Logout": "Cerrar sesión", - "Submit": "Enviar", + "Submit": "Guardar", "Back": "Volver", "PagerSearch": "Buscar", "PagerNext": "Siguiente", @@ -37,11 +37,11 @@ "NoDataAvailableInDatatable": "No hay datos disponibles", "Total": "Total", "Selected": "Seleccionado", - "PagerShowMenuEntries": "Mostrar _MENU_ entradas", - "DatatableActionDropdownDefaultText": "Acciones", + "PagerShowMenuEntries": "Mostrar _MENU_ registros", + "DatatableActionDropdownDefaultText": "Opciones", "ChangePassword": "Cambiar contraseña", "PersonalInfo": "Mi perfil", - "AreYouSureYouWantToCancelEditingWarningMessage": "Tu tienes cambios sin guardar", + "AreYouSureYouWantToCancelEditingWarningMessage": "Tienes cambios sin guardar", "GoHomePage": "Ir a la página principal", "GoBack": "Volver atrás", "Search": "Buscar", diff --git a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs index c8d31990cf..36174f1004 100644 --- a/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs +++ b/framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs @@ -64,5 +64,26 @@ namespace Volo.Abp.Cli commandLineArgs.Options.ShouldContainKey("optionC"); commandLineArgs.Options["optionC"].ShouldBe("value2"); } + + [Fact] + public void Should_Parse_From_Line_Text() + { + var commandLineArgs = _commandLineArgumentParser.Parse( + "mycommand mytarget -a value1 -b \"value with space\" --optionC" + ); + + commandLineArgs.Command.ShouldBe("mycommand"); + commandLineArgs.Target.ShouldBe("mytarget"); + commandLineArgs.Options.Any().ShouldBeTrue(); + + commandLineArgs.Options.ShouldContainKey("a"); + commandLineArgs.Options["a"].ShouldBe("value1"); + + commandLineArgs.Options.ShouldContainKey("b"); + commandLineArgs.Options["b"].ShouldBe("value with space"); + + commandLineArgs.Options.ShouldContainKey("optionC"); + commandLineArgs.Options["optionC"].ShouldBeNull(); + } } } diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml index 1aa239efd7..a4fcbc0414 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml @@ -7,7 +7,7 @@ @inject IHtmlLocalizer L @model EditModel @{ - ViewBag.PageTitle = "Blog"; + ViewBag.PageTitle = "Edit Blog Post"; } @section styles { diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml index db9644a9ed..c325513079 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml @@ -11,7 +11,7 @@ @inject IHtmlLocalizer L @inject BloggingPageHelper BloggingPageHelper @{ - ViewBag.Title = "Blog"; + ViewBag.Title = "Read All Blog Posts"; } @section scripts { diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml index d57fd1b772..810a0e3203 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml @@ -7,7 +7,7 @@ @inject IHtmlLocalizer L @model NewModel @{ - ViewBag.PageTitle = "Blog"; + ViewBag.PageTitle = "New Blog Post"; } @section styles { diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs index f72644065d..ed36c4dc0e 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs @@ -27,7 +27,7 @@ namespace Volo.Docs.Admin.Documents private readonly IDistributedCache _documentUpdateCache; private readonly IDistributedCache> _versionCache; private readonly IDistributedCache _languageCache; - private readonly IDocumentFullSearch _documentFullSearch; + private readonly IDocumentFullSearch _elasticSearchService; public DocumentAdminAppService(IProjectRepository projectRepository, IDocumentRepository documentRepository, @@ -35,7 +35,7 @@ namespace Volo.Docs.Admin.Documents IDistributedCache documentUpdateCache, IDistributedCache> versionCache, IDistributedCache languageCache, - IDocumentFullSearch documentFullSearch) + IDocumentFullSearch elasticSearchService) { _projectRepository = projectRepository; _documentRepository = documentRepository; @@ -43,7 +43,7 @@ namespace Volo.Docs.Admin.Documents _documentUpdateCache = documentUpdateCache; _versionCache = versionCache; _languageCache = languageCache; - _documentFullSearch = documentFullSearch; + _elasticSearchService = elasticSearchService; LocalizationResource = typeof(DocsResource); } @@ -210,9 +210,11 @@ namespace Volo.Docs.Admin.Documents public async Task ReindexAsync(Guid documentId) { - await _documentFullSearch.DeleteAsync(documentId); + _elasticSearchService.ValidateElasticSearchEnabled(); + + await _elasticSearchService.DeleteAsync(documentId); var document = await _documentRepository.GetAsync(documentId); - await _documentFullSearch.AddOrUpdateAsync(document); + await _elasticSearchService.AddOrUpdateAsync(document); } private async Task UpdateDocumentUpdateInfoCache(Document document) diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs index bc663a0252..24605f1cc8 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Volo.Abp.Application.Dtos; @@ -18,13 +17,13 @@ namespace Volo.Docs.Admin.Projects { private readonly IProjectRepository _projectRepository; private readonly IDocumentRepository _documentRepository; - private readonly IDocumentFullSearch _documentFullSearch; + private readonly IDocumentFullSearch _elasticSearchService; private readonly IGuidGenerator _guidGenerator; public ProjectAdminAppService( IProjectRepository projectRepository, IDocumentRepository documentRepository, - IDocumentFullSearch documentFullSearch, + IDocumentFullSearch elasticSearchService, IGuidGenerator guidGenerator) { ObjectMapperContext = typeof(DocsAdminApplicationModule); @@ -32,7 +31,7 @@ namespace Volo.Docs.Admin.Projects _projectRepository = projectRepository; _documentRepository = documentRepository; - _documentFullSearch = documentFullSearch; + _elasticSearchService = elasticSearchService; _guidGenerator = guidGenerator; } @@ -120,11 +119,21 @@ namespace Volo.Docs.Admin.Projects public async Task ReindexAsync(ReindexInput input) { - var project = await _projectRepository.GetAsync(input.ProjectId); + _elasticSearchService.ValidateElasticSearchEnabled(); - await _documentFullSearch.DeleteAllByProjectIdAsync(project.Id); + await ReindexProjectAsync(input.ProjectId); + } + + private async Task ReindexProjectAsync(Guid projectId) + { + var project = await _projectRepository.FindAsync(projectId); + if (project == null) + { + throw new Exception("Cannot find the project with the Id " + projectId); + } var docs = await _documentRepository.GetListByProjectId(project.Id); + await _elasticSearchService.DeleteAllByProjectIdAsync(project.Id); foreach (var doc in docs) { @@ -138,35 +147,18 @@ namespace Volo.Docs.Admin.Projects continue; } - await _documentFullSearch.AddOrUpdateAsync(doc); + await _elasticSearchService.AddOrUpdateAsync(doc); } } public async Task ReindexAllAsync() { - await _documentFullSearch.DeleteAllAsync(); - - var docs = await _documentRepository.GetListAsync(); + _elasticSearchService.ValidateElasticSearchEnabled(); var projects = await _projectRepository.GetListAsync(); - foreach (var doc in docs) + + foreach (var project in projects) { - var project = projects.FirstOrDefault(x => x.Id == doc.ProjectId); - if (project == null) - { - continue; - } - - if (doc.FileName == project.NavigationDocumentName) - { - continue; - } - - if (doc.FileName == project.ParametersDocumentName) - { - continue; - } - - await _documentFullSearch.AddOrUpdateAsync(doc); + await ReindexProjectAsync(project.Id); } } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs index 7160ca405b..7a47cd7b45 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs @@ -56,8 +56,6 @@ namespace Volo.Docs.Documents.FullSearch.Elastic public virtual async Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default) { - ValidateElasticSearchEnabled(); - var client = _clientProvider.GetClient(); var esDocument = new EsDocument @@ -71,15 +69,13 @@ namespace Volo.Docs.Documents.FullSearch.Elastic Version = NormalizeField(document.Version) }; - await client.IndexAsync(esDocument , x=>x.Index(_options.IndexName), cancellationToken); + await client.IndexAsync(esDocument, x => x.Index(_options.IndexName), cancellationToken); } public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default) { - ValidateElasticSearchEnabled(); - HandleError(await _clientProvider.GetClient() - .DeleteAsync(DocumentPath.Id( NormalizeField(id)), x => x.Index(_options.IndexName), cancellationToken)); + .DeleteAsync(DocumentPath.Id(NormalizeField(id)), x => x.Index(_options.IndexName), cancellationToken)); } public virtual async Task DeleteAllAsync(CancellationToken cancellationToken = default) @@ -210,7 +206,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic } } - protected virtual void ValidateElasticSearchEnabled() + public virtual void ValidateElasticSearchEnabled() { if (!_options.Enable) { diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs index e05c31bda3..c84df577b7 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs @@ -20,5 +20,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic Task> SearchAsync(string context, Guid projectId, string languageCode, string version, int? skipCount = null, int? maxResultCount = null, CancellationToken cancellationToken = default); + + void ValidateElasticSearchEnabled(); } } diff --git a/modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs b/modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs index 9406c2f6f2..9309234be7 100644 --- a/modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs +++ b/modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs @@ -5,14 +5,47 @@ namespace Volo.Docs.Bundling { public class PrismjsScriptBundleContributorDocsExtension : BundleContributor { + private static readonly string[] SupportedLanguages = new[] { + "csharp", + "json", + "aspnet", + "bash", + "css", + "css-extras", + "docker", + "javascript", + "less", + "markdown", + "nginx", + "powershell", + "regex", + "sass", + "scss", + "sql", + "typescript" + }; + public override void ConfigureBundle(BundleConfigurationContext context) + { + AddPlugins(context); + AddLanguages(context); + } + + private static void AddLanguages(IBundleConfigurationContext context) + { + const string componentsPath = "/libs/prismjs/components/prism-{0}.js"; + foreach (var language in SupportedLanguages) + { + context.Files.AddIfNotContains(string.Format(componentsPath, language)); + } + } + + private static void AddPlugins(IBundleConfigurationContext context) { context.Files.AddIfNotContains("/libs/prismjs/plugins/toolbar/prism-toolbar.js"); context.Files.AddIfNotContains("/libs/prismjs/plugins/show-language/prism-show-language.js"); context.Files.AddIfNotContains("/libs/prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard.js"); context.Files.AddIfNotContains("/libs/prismjs/plugins/line-highlight/prism-line-highlight.js"); - context.Files.AddIfNotContains("/libs/prismjs/components/prism-csharp.js"); - context.Files.AddIfNotContains("/libs/prismjs/components/prism-json.js"); } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json index dfc5193e62..20cec1eded 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json @@ -16,8 +16,8 @@ "PersonalInfo": "Mi perfil", "PersonalSettings": "Configuración personal", "UserDeletionConfirmationMessage": "El usuario '{0}' será eliminado. ¿Quieres continuar?", - "RoleDeletionConfirmationMessage": "El role '{0}' será eliminado. ¿Quieres continuar? ", - "DisplayName:RoleName": "Nombre de Role", + "RoleDeletionConfirmationMessage": "El rol '{0}' será eliminado. ¿Quieres continuar? ", + "DisplayName:RoleName": "Nombre de rol", "DisplayName:UserName": "Nombre de usuario", "DisplayName:Name": "Nombre", "DisplayName:Surname": "Apellido", @@ -26,8 +26,8 @@ "DisplayName:PhoneNumber": "Número de teléfono", "DisplayName:TwoFactorEnabled": "Autenticación en dos pasos", "DisplayName:LockoutEnabled": "Bloquear la cuenta después de intentos de inicio de sesión fallidos", - "NewRole": "Nuevo role", - "RoleName": "Nombre de role", + "NewRole": "Nuevo rol", + "RoleName": "Nombre de rol", "CreationTime": "Fecha de creación", "Permissions": "Permisos", "DisplayName:CurrentPassword": "Contraseña actual", @@ -38,12 +38,12 @@ "Volo.Abp.Identity:DefaultError": "Error desconocido.", "Volo.Abp.Identity:ConcurrencyFailure": "Fallo de concurrencia optimista", "Volo.Abp.Identity:DuplicateEmail": "E-mail '{0}' está en uso.", - "Volo.Abp.Identity:DuplicateRoleName": "Nombre de role '{0}' está en uso.", - "Volo.Abp.Identity:DuplicateUserName": "Nombre de usuario '{0}' está en uso.", + "Volo.Abp.Identity:DuplicateRoleName": "El nombre de rol '{0}' ya está en uso.", + "Volo.Abp.Identity:DuplicateUserName": "El nombre de usuario '{0}' ya está en uso.", "Volo.Abp.Identity:InvalidEmail": "E-mail '{0}' no es válido.", "Volo.Abp.Identity:InvalidPasswordHasherCompatibilityMode": "El PasswordHasherCompatibilityMode proporcionado no es válido.", "Volo.Abp.Identity:InvalidPasswordHasherIterationCount": "La cuenta iterativa debe ser una entero positivo.", - "Volo.Abp.Identity:InvalidRoleName": "El nombre de role '{0}' no es válido.", + "Volo.Abp.Identity:InvalidRoleName": "El nombre de rol '{0}' no es válido.", "Volo.Abp.Identity:InvalidToken": "token no válido", "Volo.Abp.Identity:InvalidUserName": "Nombre de usuario '{0}' no es válido. Sólo puede contener letras o dígitos", "Volo.Abp.Identity:LoginAlreadyAssociated": "Usuario con este inicio de sesión en uso.", @@ -53,7 +53,7 @@ "Volo.Abp.Identity:PasswordRequiresNonAlphanumeric": "Contraseñas deben tener al menos un carácter no alfanumérico", "Volo.Abp.Identity:PasswordRequiresUpper": "Contraseñas deben tener al menos un carácter en mayúsculas ('A'-'Z').", "Volo.Abp.Identity:PasswordTooShort": "Contraseñas deben tener al menos {0} caracteres", - "Volo.Abp.Identity:RoleNotFound": "El role {0} no existe.", + "Volo.Abp.Identity:RoleNotFound": "El rol {0} no existe.", "Volo.Abp.Identity:UserAlreadyHasPassword": "El usuario tiene la contraseña establecida", "Volo.Abp.Identity:UserAlreadyInRole": "El usuario ya tiene asignado el rol '{0}'´.", "Volo.Abp.Identity:UserLockedOut": "El usuario está bloqueado.", @@ -65,8 +65,8 @@ "Volo.Abp.Identity:010002": "No puedes establecer más de {MaxUserMembershipCount} unidades organizativas para un usuario!", "Volo.Abp.Identity:010003": "No puedes cambiar la contraseña de un usuario que inicio sesión desde un servicio externo!", "Volo.Abp.Identity:010004": "Hay una unidad organizativa con nombre {0}. Dos unidades con el mismo nombre no pueden ser creadas en el mismo nivel.", - "Volo.Abp.Identity:010005": "Roles estáticos no pueden ser renombrados.", - "Volo.Abp.Identity:010006": "Roles estáticos no pueden ser borrados.", + "Volo.Abp.Identity:010005": "Los roles estáticos no pueden ser renombrados.", + "Volo.Abp.Identity:010006": "Los roles estáticos no pueden ser borrados.", "Volo.Abp.Identity:010007": "No puedes cambiar tu configuración de autentificación de dos pasos.", "Volo.Abp.Identity:010008": "No está permitido cambiar la configuración de autenticación de dos pasos.", "Identity.OrganizationUnit.MaxUserMembershipCount": "Número máximo de unidades organizativas por usuario", diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs index 359babde7e..a4459d99e2 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using IdentityServer4.Models; using IdentityServer4.Validation; @@ -33,7 +34,9 @@ namespace Volo.Abp.IdentityServer var extractResult = FormattedStringValueExtracter.Extract(requestedUri, url, ignoreCase: true); if (extractResult.IsMatch) { - return Task.FromResult(true); + return Task.FromResult(extractResult.Matches + .Aggregate(url, (current, nameValue) => current.Replace($"{{{nameValue.Name}}}", nameValue.Value)) + .Contains(requestedUri, StringComparison.OrdinalIgnoreCase)); } if (url.Replace("{0}.", "").Contains(requestedUri, StringComparison.OrdinalIgnoreCase)) diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs index 1a9d146ca9..daf8d1f6f2 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using IdentityServer4.Configuration; using Microsoft.Extensions.Options; @@ -32,7 +33,8 @@ namespace Volo.Abp.IdentityServer var extractResult = FormattedStringValueExtracter.Extract(origin, url, ignoreCase: true); if (extractResult.IsMatch) { - return true; + return extractResult.Matches.Aggregate(url, (current, nameValue) => current.Replace($"{{{nameValue.Name}}}", nameValue.Value)) + .Contains(origin, StringComparison.OrdinalIgnoreCase); } if (url.Replace("{0}.", "").Contains(origin, StringComparison.OrdinalIgnoreCase)) diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs index a455cd9811..7f00d2e355 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs @@ -54,6 +54,7 @@ namespace Volo.Abp.IdentityServer (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeTrue(); (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse(); (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://ng.t1.abp.io", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://t1.ng.abp.io/index.html.mydomain.com", _testClient)).ShouldBeFalse(); } [Fact] @@ -68,6 +69,7 @@ namespace Volo.Abp.IdentityServer (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeTrue(); (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse(); (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.t1.abp.io", _testClient)).ShouldBeFalse(); + (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://t1.ng.abp.io/index.html.mydomain.com", _testClient)).ShouldBeFalse(); } } } diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs index fcb8e756e0..36949582d7 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs @@ -35,6 +35,8 @@ namespace Volo.Abp.IdentityServer (await _corsPolicyService.IsOriginAllowedAsync("https://abp.io")).ShouldBeTrue(); (await _corsPolicyService.IsOriginAllowedAsync("https://t1.abp.io")).ShouldBeTrue(); (await _corsPolicyService.IsOriginAllowedAsync("https://t1.ng.abp.io")).ShouldBeTrue(); + + (await _corsPolicyService.IsOriginAllowedAsync("https://t1.abp.io.mydomain.com")).ShouldBeFalse(); } } } diff --git a/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts index 4e4313caf5..b130afcc23 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts @@ -12,7 +12,6 @@ describe('LocalizationService', () => { let spectator: SpectatorService; let sessionState: SpyObject; let configState: SpyObject; - let router: SpyObject; let service: LocalizationService; const createService = createServiceFactory({ diff --git a/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts b/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts index c757fad1e5..6c9ffeaa38 100644 --- a/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts +++ b/npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts @@ -110,5 +110,5 @@ function createMockLocalizationService() { const configState = new ConfigStateService(); configState.setState({ localization: mockL10n } as any); - return new LocalizationService(mockSessionState, null, null, null, configState, null); + return new LocalizationService(mockSessionState, null, null, configState, null); } diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/datepicker-config.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/datepicker-config.ts new file mode 100644 index 0000000000..6fc30c8577 --- /dev/null +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/datepicker-config.ts @@ -0,0 +1,11 @@ +import { Injector } from '@angular/core'; +import { NgbInputDatepickerConfig } from '@ng-bootstrap/ng-bootstrap'; + +export function initDatepickerConfig(injector: Injector) { + const datepickerConfig = injector.get(NgbInputDatepickerConfig, null); + if (datepickerConfig) { + datepickerConfig.container = 'body'; + } + + return () => {}; +} diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts index 11eb07398e..a9253984b5 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts @@ -1,2 +1,3 @@ export * from './error.handler'; export * from './lazy-style.handler'; +export * from './datepicker-config'; diff --git a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts index ddf1d6a5a3..72b99aa7e4 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts @@ -38,6 +38,7 @@ import { THEME_SHARED_APPEND_CONTENT } from './tokens/append-content.token'; import { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token'; import { DateParserFormatter } from './utils/date-parser-formatter'; import { ModalCloseDirective } from './components/modal/modal-close.directive'; +import { initDatepickerConfig } from './handlers/datepicker-config'; const declarationsWithExports = [ BreadcrumbComponent, @@ -110,6 +111,12 @@ export class ThemeSharedModule { deps: [Injector], useFactory: initLazyStyleHandler, }, + { + provide: APP_INITIALIZER, + multi: true, + deps: [Injector], + useFactory: initDatepickerConfig, + }, { provide: HTTP_ERROR_CONFIG, useValue: httpErrorConfig }, { provide: 'HTTP_ERROR_CONFIG', diff --git a/npm/ng-packs/packages/theme-shared/src/public-api.ts b/npm/ng-packs/packages/theme-shared/src/public-api.ts index 5a4f9efe95..845655402d 100644 --- a/npm/ng-packs/packages/theme-shared/src/public-api.ts +++ b/npm/ng-packs/packages/theme-shared/src/public-api.ts @@ -8,7 +8,7 @@ export { BOOTSTRAP } from './lib/constants/styles'; export * from './lib/constants/validation'; export * from './lib/directives'; export * from './lib/enums'; -export { ErrorHandler } from './lib/handlers'; +export * from './lib/handlers'; export * from './lib/models'; export * from './lib/providers'; export * from './lib/services';