Browse Source

Merge branch 'dev' into maliming/prompt

pull/8906/head
maliming 5 years ago
parent
commit
943972809b
  1. 5
      .gitignore
  2. 86
      docs/en/Blog-Posts/2021-04-05 CmsKit/POST.md
  3. BIN
      docs/en/Blog-Posts/2021-04-05 CmsKit/cms-kit-menu.png
  4. 22
      docs/en/Community-Articles/2021-03-12-Simple-SignalR-Notification/POST.md
  5. 61
      docs/en/Entity-Framework-Core-Migrations.md
  6. 20
      docs/en/Entity-Framework-Core.md
  7. 4
      docs/en/Features.md
  8. 158
      docs/en/Modules/Cms-Kit.md
  9. 117
      docs/en/Modules/cms-kit/Comment-System.md
  10. 116
      docs/en/Modules/cms-kit/Rating-System.md
  11. 128
      docs/en/Modules/cms-kit/Reaction-System.md
  12. 135
      docs/en/Modules/cms-kit/Tag-Management.md
  13. 18
      docs/en/MongoDB.md
  14. 30
      docs/en/UI/Angular/Page-Component.md
  15. 83
      docs/en/docs-nav.json
  16. BIN
      docs/en/images/cmskit-module-blog-post-edit.png
  17. BIN
      docs/en/images/cmskit-module-blog-posts-page.png
  18. BIN
      docs/en/images/cmskit-module-blogs-edit.png
  19. BIN
      docs/en/images/cmskit-module-blogs-feature-action.png
  20. BIN
      docs/en/images/cmskit-module-blogs-page.png
  21. BIN
      docs/en/images/cmskit-module-comment-page.png
  22. BIN
      docs/en/images/cmskit-module-comments-detail.png
  23. BIN
      docs/en/images/cmskit-module-features-dialog.png
  24. BIN
      docs/en/images/cmskit-module-pages-edit.png
  25. BIN
      docs/en/images/cmskit-module-pages-page.png
  26. BIN
      docs/en/images/cmskit-module-ratings.png
  27. BIN
      docs/en/images/cmskit-module-reactions.png
  28. BIN
      docs/en/images/cmskit-module-tag-edit.png
  29. BIN
      docs/en/images/cmskit-module-tags-page.png
  30. 14
      framework/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/AppWithoutAuth.razor
  31. 23
      framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs
  32. 13
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BasicThemeToolbarContributor.cs
  33. 27
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor
  34. 49
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor.cs
  35. 24
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs
  36. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs
  37. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json
  38. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json
  39. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de-DE.json
  40. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de.json
  41. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en-GB.json
  42. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json
  43. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es-mx.json
  44. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es.json
  45. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fi.json
  46. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fr.json
  47. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json
  48. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/nl.json
  49. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json
  50. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json
  51. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ru.json
  52. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/sl.json
  53. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/tr.json
  54. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/vi.json
  55. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hans.json
  56. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hant.json
  57. 13
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs
  58. 22
      framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
  59. 16
      framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs
  60. 47
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs
  61. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs
  62. 6
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs
  63. 10
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs
  64. 2
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceRegistrationActionList.cs
  65. 15
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs
  66. 18
      framework/src/Volo.Abp.Http.Client.IdentityModel.WebAssembly/Volo/Abp/Http/Client/IdentityModel/WebAssembly/AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator.cs
  67. 5
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs
  68. 12
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json
  69. 21
      framework/test/Volo.Abp.Cli.Core.Tests/Volo/Abp/Cli/CommandLineArgumentParser_Tests.cs
  70. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml
  71. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml
  72. 2
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml
  73. 12
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs
  74. 48
      modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs
  75. 10
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/ElasticDocumentFullSearch.cs
  76. 2
      modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs
  77. 37
      modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs
  78. 20
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json
  79. 5
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs
  80. 4
      modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs
  81. 2
      modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator_Tests.cs
  82. 2
      modules/identityserver/test/Volo.Abp.IdentityServer.Domain.Tests/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService_Tests.cs
  83. 1
      npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts
  84. 2
      npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts
  85. 11
      npm/ng-packs/packages/theme-shared/src/lib/handlers/datepicker-config.ts
  86. 1
      npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts
  87. 7
      npm/ng-packs/packages/theme-shared/src/lib/theme-shared.module.ts
  88. 2
      npm/ng-packs/packages/theme-shared/src/public-api.ts

5
.gitignore

@ -310,4 +310,7 @@ abp-build-config.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Pages/Test/ /templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Blazor/Pages/Test/
# Identity Server temp signature file # Identity Server temp signature file
tempkey.jwk tempkey.jwk
# ABP Analyzing files
*.abppkg.analyze.json

86
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...

BIN
docs/en/Blog-Posts/2021-04-05 CmsKit/cms-kit-menu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

22
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- 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<AbpBundlingOptions>(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) ![Script Bundles](add-script-bundles.jpg)
#### 3- Add Hub Endpoint #### 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. 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) ![Add SignalR package](signalr-package.jpg)
#### 3- Add resource Mapping #### 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 #### 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) ![Result](result.jpg)

61
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. 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<MyTenantDbContext>
{
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 ## 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. 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.

20
docs/en/Entity-Framework-Core.md

@ -626,6 +626,24 @@ See the "*ConfigureByConvention Method*" section above for more information.
## Advanced Topics ## 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<MyDbContext>
{
...
}
````
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 ### 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. 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 ## See Also
* [Entities](Entities.md) * [Entities](Entities.md)
* [Repositories](Repositories.md) * [Repositories](Repositories.md)

4
docs/en/Features.md

@ -146,9 +146,9 @@ A feature should be defined to be able to check it.
### FeatureDefinitionProvider ### 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 ```csharp
using Volo.Abp.Features; using Volo.Abp.Features;

158
docs/en/Modules/Cms-Kit.md

@ -1,3 +1,159 @@
# CMS Kit Module # CMS Kit Module
TODO 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)

117
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<CmsKitCommentOptions>(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.

116
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<CmsKitRatingOptions>(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.

128
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<CmsKitReactionOptions>(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.

135
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<CmsKitTagOptions>(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.

18
docs/en/MongoDB.md

@ -292,6 +292,24 @@ Configure<AbpUnitOfWorkDefaultOptions>(options =>
### Advanced Topics ### 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 #### 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. Default generic repositories are implemented by `MongoDbRepository` class by default. You can create your own implementation and use it for default repository implementation.

30
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. 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 ```html
<div class="row entry-row"> <div class="row entry-row">
<div class="col-auto"> <div class="col-auto">
<h1 class="content-header-title">{{ '::Dashboard' | abpLocalization }}</h1> <h1 class="content-header-title">{%{{{ '::Dashboard' | abpLocalization }}}%}</h1>
</div> </div>
<div id="breadcrumb" class="col-lg-auto pl-lg-0"> <div id="breadcrumb" class="col-lg-auto pl-lg-0">
<abp-breadcrumb></abp-breadcrumb> <abp-breadcrumb></abp-breadcrumb>
@ -26,7 +26,7 @@ Let's look at the following example without `abp-page` component.
</div> </div>
``` ```
## 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. 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:
</abp-page> </abp-page>
``` ```
## Inputs ## Inputs
* title: `string`: Will be be rendered within `h1.content-header-title`. If not provided, the parent `div` will not be rendered * 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`. * 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. * 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. If you need to replace the template of any part, you can use the following sub-components.
```html ```html
<abp-page> <abp-page>
<abp-page-title-container> <abp-page-title-container class="col">
<div class="col"> <h2>Custom Title</h2>
<h2>Custom Title</h2>
</div>
</abp-page-title-container> </abp-page-title-container>
<abp-page-breacrumb-container> <abp-page-breacrumb-container class="col">
<div class="col"> <my-breadcrumb></my-breadcrumb>
<my-breadcrumb></my-breadcrumb>
</div>
</abp-page-breacrumb-container> </abp-page-breacrumb-container>
<abp-page-toolbar-container> <abp-page-toolbar-container class="col">
<div class="col"> <button (click)="doSth()">Some Action</button>
<!-- ... -->
</div>
</abp-page-toolbar-container> </abp-page-toolbar-container>
</abp-page> </abp-page>
``` ```
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. 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. `PageModule` provides a structural directive that is used internally within `PageComponent` and can also be used externally.

83
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", "text": "Tutorials",
"items": [ "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", "text": "Fundamentals",
"items": [ "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", "text": "Contribution Guide",
"path": "Contribution/Index.md" "path": "Contribution/Index.md"

BIN
docs/en/images/cmskit-module-blog-post-edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
docs/en/images/cmskit-module-blog-posts-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/images/cmskit-module-blogs-edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/images/cmskit-module-blogs-feature-action.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
docs/en/images/cmskit-module-blogs-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/en/images/cmskit-module-comment-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
docs/en/images/cmskit-module-comments-detail.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/en/images/cmskit-module-features-dialog.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/images/cmskit-module-pages-edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
docs/en/images/cmskit-module-pages-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
docs/en/images/cmskit-module-ratings.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
docs/en/images/cmskit-module-reactions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
docs/en/images/cmskit-module-tag-edit.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
docs/en/images/cmskit-module-tags-page.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

14
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<AbpRouterOptions> RouterOptions
<Router AppAssembly="RouterOptions.Value.AppAssembly"
AdditionalAssemblies="RouterOptions.Value.AdditionalAssemblies">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

23
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.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -14,21 +15,29 @@ namespace Volo.Abp.AspNetCore.Components.Web.Security
{ {
public ClaimsPrincipal Principal { get; private set; } public ClaimsPrincipal Principal { get; private set; }
[CanBeNull]
private readonly AuthenticationStateProvider _authenticationStateProvider; private readonly AuthenticationStateProvider _authenticationStateProvider;
public AbpComponentsClaimsCache(IClientScopeServiceProviderAccessor clientScopeServiceProviderAccessor) public AbpComponentsClaimsCache(
IClientScopeServiceProviderAccessor serviceProviderAccessor)
{ {
_authenticationStateProvider = clientScopeServiceProviderAccessor.ServiceProvider.GetRequiredService<AuthenticationStateProvider>(); _authenticationStateProvider = serviceProviderAccessor.ServiceProvider.GetService<AuthenticationStateProvider>();
_authenticationStateProvider.AuthenticationStateChanged += async (task) => if (_authenticationStateProvider != null)
{ {
Principal = (await task).User; _authenticationStateProvider.AuthenticationStateChanged += async (task) =>
}; {
Principal = (await task).User;
};
}
} }
public virtual async Task InitializeAsync() public virtual async Task InitializeAsync()
{ {
var authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync(); if (_authenticationStateProvider != null)
Principal = authenticationState.User; {
var authenticationState = await _authenticationStateProvider.GetAuthenticationStateAsync();
Principal = authenticationState.User;
}
} }
} }
} }

13
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BasicThemeToolbarContributor.cs

@ -1,7 +1,10 @@
using System.Threading.Tasks; 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.BasicTheme.Themes.Basic;
using Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars; using Volo.Abp.AspNetCore.Components.Web.Theming.Toolbars;
using Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic; using Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme
{ {
@ -12,7 +15,15 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme
if (context.Toolbar.Name == StandardToolbars.Main) if (context.Toolbar.Name == StandardToolbars.Main)
{ {
context.Toolbar.Items.Add(new ToolbarItem(typeof(LanguageSwitch))); 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<AuthenticationStateProvider>();
if (authenticationStateProvider != null)
{
context.Toolbar.Items.Add(new ToolbarItem(typeof(LoginDisplay)));
}
} }
return Task.CompletedTask; return Task.CompletedTask;

27
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.Users
@using Volo.Abp.MultiTenancy @using Volo.Abp.MultiTenancy
@using global::Localization.Resources.AbpUi @using global::Localization.Resources.AbpUi
@inherits AbpComponentBase
@inject ICurrentUser CurrentUser @inject ICurrentUser CurrentUser
@inject ICurrentTenant CurrentTenant @inject ICurrentTenant CurrentTenant
@inject IJSRuntime JsRuntime @inject IJSRuntime JsRuntime
@inject NavigationManager Navigation @inject NavigationManager Navigation
@inject SignOutSessionStateManager SignOutManager
@inject IStringLocalizer<AbpUiResource> UiLocalizer @inject IStringLocalizer<AbpUiResource> UiLocalizer
<AuthorizeView> <AuthorizeView>
<Authorized> <Authorized>
@ -38,24 +37,4 @@
<NotAuthorized> <NotAuthorized>
<a class="nav-link" href="authentication/login">@UiLocalizer["Login"]</a> <a class="nav-link" href="authentication/login">@UiLocalizer["Login"]</a>
</NotAuthorized> </NotAuthorized>
</AuthorizeView> </AuthorizeView>
@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");
}
}

49
framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor.cs

@ -1,8 +1,11 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Routing; using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.JSInterop;
using Volo.Abp.UI.Navigation; using Volo.Abp.UI.Navigation;
namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic
@ -11,23 +14,32 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic
{ {
[Inject] [Inject]
protected IMenuManager MenuManager { get; set; } 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() protected override async Task OnInitializedAsync()
{ {
Menu = await MenuManager.GetAsync(StandardMenus.User); Menu = await MenuManager.GetAsync(StandardMenus.User);
Navigation.LocationChanged += OnLocationChanged; Navigation.LocationChanged += OnLocationChanged;
AuthenticationStateProvider.AuthenticationStateChanged += async (task) => LazyGetService(ref AuthenticationStateProvider);
LazyGetService(ref SignOutManager);
if (AuthenticationStateProvider != null)
{ {
Menu = await MenuManager.GetAsync(StandardMenus.User); AuthenticationStateProvider.AuthenticationStateChanged += async (task) =>
await InvokeAsync(StateHasChanged); {
}; Menu = await MenuManager.GetAsync(StandardMenus.User);
await InvokeAsync(StateHasChanged);
};
}
} }
protected virtual void OnLocationChanged(object sender, LocationChangedEventArgs e) protected virtual void OnLocationChanged(object sender, LocationChangedEventArgs e)
@ -39,5 +51,26 @@ namespace Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme.Themes.Basic
{ {
Navigation.LocationChanged -= OnLocationChanged; 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");
}
}
} }
} }

24
framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs

@ -108,6 +108,18 @@ namespace Volo.Abp.AspNetCore.Components
return reference; return reference;
} }
protected TService LazyGetService<TService>(ref TService reference) => LazyGetService(typeof(TService), ref reference);
protected TRef LazyGetService<TRef>(Type serviceType, ref TRef reference)
{
if (reference == null)
{
reference = (TRef)ScopedServices.GetService(serviceType);
}
return reference;
}
protected TService LazyGetNonScopedRequiredService<TService>(ref TService reference) => LazyGetNonScopedRequiredService(typeof(TService), ref reference); protected TService LazyGetNonScopedRequiredService<TService>(ref TService reference) => LazyGetNonScopedRequiredService(typeof(TService), ref reference);
@ -120,6 +132,18 @@ namespace Volo.Abp.AspNetCore.Components
return reference; return reference;
} }
protected TService LazyGetNonScopedService<TService>(ref TService reference) => LazyGetNonScopedService(typeof(TService), ref reference);
protected TRef LazyGetNonScopedService<TRef>(Type serviceType, ref TRef reference)
{
if (reference == null)
{
reference = (TRef)NonScopedServices.GetService(serviceType);
}
return reference;
}
[Inject] [Inject]
protected IServiceProvider NonScopedServices { get; set; } protected IServiceProvider NonScopedServices { get; set; }

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs

@ -48,7 +48,12 @@ namespace Pages.Abp.MultiTenancy
else else
{ {
var tenant = await TenantStore.FindAsync(Input.Name); 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]); throw new UserFriendlyException(L["GivenTenantIsNotAvailable", Input.Name]);
} }

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json

@ -1,6 +1,7 @@
{ {
"culture": "ar", "culture": "ar",
"texts": { "texts": {
"GivenTenantIsNotExist": "المستأجر المحدد غير موجود: {0}",
"GivenTenantIsNotAvailable": "الجهة المحددة غير متاحة: {0}", "GivenTenantIsNotAvailable": "الجهة المحددة غير متاحة: {0}",
"Tenant": "الجهة", "Tenant": "الجهة",
"Switch": "تغيير", "Switch": "تغيير",
@ -9,4 +10,4 @@
"SwitchTenant": "تغيير الجهة", "SwitchTenant": "تغيير الجهة",
"NotSelected": "غير محدد" "NotSelected": "غير محدد"
} }
} }

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json

@ -1,6 +1,7 @@
{ {
"culture": "cs", "culture": "cs",
"texts": { "texts": {
"GivenTenantIsNotExist": "Vybraný tenant neexistuje: {0}",
"GivenTenantIsNotAvailable": "Zvolený tenant není dostupný: {0}", "GivenTenantIsNotAvailable": "Zvolený tenant není dostupný: {0}",
"Tenant": "Tenant", "Tenant": "Tenant",
"Switch": "změnit", "Switch": "změnit",

3
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", "culture": "de-DE",
"texts": { "texts": {
"GivenTenantIsNotExist": "Der angegebene Client ist nicht vorhanden: {0}",
"GivenTenantIsNotAvailable": "Der angegebene Mandant ist nicht verfügbar: {0}", "GivenTenantIsNotAvailable": "Der angegebene Mandant ist nicht verfügbar: {0}",
"Tenant": "Mandant", "Tenant": "Mandant",
"Switch": "wechseln", "Switch": "wechseln",
@ -9,4 +10,4 @@
"SwitchTenant": "Mandant wechseln", "SwitchTenant": "Mandant wechseln",
"NotSelected": "Nicht ausgewählt" "NotSelected": "Nicht ausgewählt"
} }
} }

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de.json

@ -1,6 +1,7 @@
{ {
"culture": "de", "culture": "de",
"texts": { "texts": {
"GivenTenantIsNotExist": "Der angegebene Client ist nicht vorhanden: {0}",
"GivenTenantIsNotAvailable": "Der angegebene Mandant ist nicht verfügbar: {0}", "GivenTenantIsNotAvailable": "Der angegebene Mandant ist nicht verfügbar: {0}",
"Tenant": "Mandant", "Tenant": "Mandant",
"Switch": "wechseln", "Switch": "wechseln",
@ -9,4 +10,4 @@
"SwitchTenant": "Mandant wechseln", "SwitchTenant": "Mandant wechseln",
"NotSelected": "Nicht ausgewählt" "NotSelected": "Nicht ausgewählt"
} }
} }

3
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", "culture": "en-GB",
"texts": { "texts": {
"GivenTenantIsNotExist": "Given tenant is not exist: {0}",
"GivenTenantIsNotAvailable": "Given tenant is not available: {0}", "GivenTenantIsNotAvailable": "Given tenant is not available: {0}",
"Tenant": "Tenant", "Tenant": "Tenant",
"Switch": "switch", "Switch": "switch",
@ -9,4 +10,4 @@
"SwitchTenant": "Switch tenant", "SwitchTenant": "Switch tenant",
"NotSelected": "Not selected" "NotSelected": "Not selected"
} }
} }

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json

@ -1,6 +1,7 @@
{ {
"culture": "en", "culture": "en",
"texts": { "texts": {
"GivenTenantIsNotExist": "Given tenant is not exist: {0}",
"GivenTenantIsNotAvailable": "Given tenant is not available: {0}", "GivenTenantIsNotAvailable": "Given tenant is not available: {0}",
"Tenant": "Tenant", "Tenant": "Tenant",
"Switch": "switch", "Switch": "switch",
@ -9,4 +10,4 @@
"SwitchTenant": "Switch tenant", "SwitchTenant": "Switch tenant",
"NotSelected": "Not selected" "NotSelected": "Not selected"
} }
} }

1
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", "culture": "es-MX",
"texts": { "texts": {
"GivenTenantIsNotExist": "El inquilino {0} no existe",
"GivenTenantIsNotAvailable": "El inquilino {0} no está disponible", "GivenTenantIsNotAvailable": "El inquilino {0} no está disponible",
"Tenant": "Inquilino", "Tenant": "Inquilino",
"Switch": "cambiar", "Switch": "cambiar",

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es.json

@ -1,6 +1,7 @@
{ {
"culture": "es", "culture": "es",
"texts": { "texts": {
"GivenTenantIsNotExist": "El inquilino {0} no existe",
"GivenTenantIsNotAvailable": "El inquilino {0} no está disponible", "GivenTenantIsNotAvailable": "El inquilino {0} no está disponible",
"Tenant": "Inquilino", "Tenant": "Inquilino",
"Switch": "cambiar", "Switch": "cambiar",
@ -9,4 +10,4 @@
"SwitchTenantHint": "Deje en blanco el campo nombre para cambiar el lado del host.", "SwitchTenantHint": "Deje en blanco el campo nombre para cambiar el lado del host.",
"NotSelected": "No seleccionado" "NotSelected": "No seleccionado"
} }
} }

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fi.json

@ -1,6 +1,7 @@
{ {
"culture": "fi", "culture": "fi",
"texts": { "texts": {
"GivenTenantIsNotExist": "Annettua vuokralaista ei ole olemassa: {0}",
"GivenTenantIsNotAvailable": "Annettua vuokralaista ei ole saatavilla: {0}", "GivenTenantIsNotAvailable": "Annettua vuokralaista ei ole saatavilla: {0}",
"Tenant": "Vuokralainen", "Tenant": "Vuokralainen",
"Switch": "vaihtaa", "Switch": "vaihtaa",
@ -9,4 +10,4 @@
"SwitchTenant": "Vaihda vuokralainen", "SwitchTenant": "Vaihda vuokralainen",
"NotSelected": "Ei valittu" "NotSelected": "Ei valittu"
} }
} }

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fr.json

@ -1,6 +1,7 @@
{ {
"culture": "fr", "culture": "fr",
"texts": { "texts": {
"GivenTenantIsNotExist": "Adott bérlő nem létezik: {0}",
"GivenTenantIsNotAvailable": "Le Tenant donné n'est pas disponible: {0}", "GivenTenantIsNotAvailable": "Le Tenant donné n'est pas disponible: {0}",
"Tenant": "Tenant", "Tenant": "Tenant",
"Switch": "Changer", "Switch": "Changer",
@ -9,4 +10,4 @@
"SwitchTenant": "Changer le Tenant", "SwitchTenant": "Changer le Tenant",
"NotSelected": "Non séléctionné" "NotSelected": "Non séléctionné"
} }
} }

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json

@ -1,6 +1,7 @@
{ {
"culture": "hu", "culture": "hu",
"texts": { "texts": {
"GivenTenantIsNotExist": "Adott bérlő nem létezik: {0}",
"GivenTenantIsNotAvailable": "A megadott előfizető nem érhető el: {0}", "GivenTenantIsNotAvailable": "A megadott előfizető nem érhető el: {0}",
"Tenant": "Előfizető", "Tenant": "Előfizető",
"Switch": "váltás", "Switch": "váltás",
@ -9,4 +10,4 @@
"SwitchTenant": "Előfizető váltás", "SwitchTenant": "Előfizető váltás",
"NotSelected": "Nincs kiválasztva" "NotSelected": "Nincs kiválasztva"
} }
} }

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/nl.json

@ -1,6 +1,7 @@
{ {
"culture": "nl", "culture": "nl",
"texts": { "texts": {
"GivenTenantIsNotExist": "Opgegeven huurder bestaat niet: {0}",
"GivenTenantIsNotAvailable": "Gegeven klant is niet beschikbaar: {0}", "GivenTenantIsNotAvailable": "Gegeven klant is niet beschikbaar: {0}",
"Tenant": "Klant", "Tenant": "Klant",
"Switch": "Schakel over", "Switch": "Schakel over",
@ -9,4 +10,4 @@
"SwitchTenant": "Klant wisselen", "SwitchTenant": "Klant wisselen",
"NotSelected": "Niet geselecteerd" "NotSelected": "Niet geselecteerd"
} }
} }

3
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", "culture": "pl-PL",
"texts": { "texts": {
"GivenTenantIsNotExist": "Podany najemca nie istnieje: {0}",
"GivenTenantIsNotAvailable": "Podany tenant jest niedostępny: {0}", "GivenTenantIsNotAvailable": "Podany tenant jest niedostępny: {0}",
"Tenant": "Tenant", "Tenant": "Tenant",
"Switch": "zmień", "Switch": "zmień",
@ -8,4 +9,4 @@
"SwitchTenantHint": "Pozostaw pole nazwy puste, aby przełączyć się na stronę hosta.", "SwitchTenantHint": "Pozostaw pole nazwy puste, aby przełączyć się na stronę hosta.",
"NotSelected": "Nie wybrano" "NotSelected": "Nie wybrano"
} }
} }

3
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", "culture": "pt-BR",
"texts": { "texts": {
"GivenTenantIsNotExist": "O inquilino dado não existe: {0}",
"GivenTenantIsNotAvailable": "Inquilino não está disponível: {0}", "GivenTenantIsNotAvailable": "Inquilino não está disponível: {0}",
"Tenant": "Inquilino", "Tenant": "Inquilino",
"Switch": "trocar", "Switch": "trocar",
@ -9,4 +10,4 @@
"SwitchTenant": "Alterar Inquilino", "SwitchTenant": "Alterar Inquilino",
"NotSelected": "Não selecionado" "NotSelected": "Não selecionado"
} }
} }

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ru.json

@ -1,6 +1,7 @@
{ {
"culture": "ru", "culture": "ru",
"texts": { "texts": {
"GivenTenantIsNotExist": "Данный арендатор не существует: {0}",
"GivenTenantIsNotAvailable": "Данный арендатор недоступен: {0}", "GivenTenantIsNotAvailable": "Данный арендатор недоступен: {0}",
"Tenant": "Арендатор", "Tenant": "Арендатор",
"Switch": "переключиться", "Switch": "переключиться",

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/sl.json

@ -1,6 +1,7 @@
{ {
"culture": "sl", "culture": "sl",
"texts": { "texts": {
"GivenTenantIsNotExist": "Dani najemnik ne obstaja: {0}",
"GivenTenantIsNotAvailable": "Najemnik ni na voljo: {0}", "GivenTenantIsNotAvailable": "Najemnik ni na voljo: {0}",
"Tenant": "Najemnik", "Tenant": "Najemnik",
"Switch": "Zamenjaj", "Switch": "Zamenjaj",

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/tr.json

@ -1,6 +1,7 @@
{ {
"culture": "tr", "culture": "tr",
"texts": { "texts": {
"GivenTenantIsNotExist": "İstenilen müşteri bulunamadı: {0}",
"GivenTenantIsNotAvailable": "İstenilen müşteri bulunamadı: {0}", "GivenTenantIsNotAvailable": "İstenilen müşteri bulunamadı: {0}",
"Tenant": "Müşteri", "Tenant": "Müşteri",
"Switch": "değiştir", "Switch": "değiştir",
@ -9,4 +10,4 @@
"SwitchTenant": "Müşteri değiştir", "SwitchTenant": "Müşteri değiştir",
"NotSelected": "Seçili değil" "NotSelected": "Seçili değil"
} }
} }

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/vi.json

@ -1,6 +1,7 @@
{ {
"culture": "vi", "culture": "vi",
"texts": { "texts": {
"GivenTenantIsNotExist": "Người thuê đã cho không tồn tại: {0}",
"GivenTenantIsNotAvailable": "Người thuê không có sẵn: {0}", "GivenTenantIsNotAvailable": "Người thuê không có sẵn: {0}",
"Tenant": "Người thuê", "Tenant": "Người thuê",
"Switch": "Chuyển đổi", "Switch": "Chuyển đổi",

3
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", "culture": "zh-Hans",
"texts": { "texts": {
"GivenTenantIsNotExist": "给定的租户不存在: {0}",
"GivenTenantIsNotAvailable": "给定的租户不可用: {0}", "GivenTenantIsNotAvailable": "给定的租户不可用: {0}",
"Tenant": "租户", "Tenant": "租户",
"Switch": "切换", "Switch": "切换",
@ -9,4 +10,4 @@
"SwitchTenant": "切换租户", "SwitchTenant": "切换租户",
"NotSelected": "未选中" "NotSelected": "未选中"
} }
} }

3
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", "culture": "zh-Hant",
"texts": { "texts": {
"GivenTenantIsNotExist": "指定的租戶不存在: {0}",
"GivenTenantIsNotAvailable": "指定的租戶不可用: {0}", "GivenTenantIsNotAvailable": "指定的租戶不可用: {0}",
"SwitchTenant": "切換租戶", "SwitchTenant": "切換租戶",
"Name": "名稱", "Name": "名稱",
@ -9,4 +10,4 @@
"Switch": "切換", "Switch": "切換",
"NotSelected": "尚未選定" "NotSelected": "尚未選定"
} }
} }

13
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.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -22,7 +22,16 @@ namespace Volo.Abp.Authorization.Permissions
{ {
Check.NotNullOrEmpty(models, nameof(models)); 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; return this;
} }

22
framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs

@ -35,7 +35,11 @@ namespace Autofac.Builder
return registrationBuilder; return registrationBuilder;
} }
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InvokeRegistrationActions<TLimit, TActivatorData, TRegistrationStyle>(this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder, ServiceRegistrationActionList registrationActionList, Type serviceType, Type implementationType) private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> InvokeRegistrationActions<TLimit, TActivatorData, TRegistrationStyle>(
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
ServiceRegistrationActionList registrationActionList,
Type serviceType,
Type implementationType)
where TActivatorData : ReflectionActivatorData where TActivatorData : ReflectionActivatorData
{ {
var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType); var serviceRegistredArgs = new OnServiceRegistredContext(serviceType, implementationType);
@ -48,6 +52,7 @@ namespace Autofac.Builder
if (serviceRegistredArgs.Interceptors.Any()) if (serviceRegistredArgs.Interceptors.Any())
{ {
registrationBuilder = registrationBuilder.AddInterceptors( registrationBuilder = registrationBuilder.AddInterceptors(
registrationActionList,
serviceType, serviceType,
serviceRegistredArgs.Interceptors serviceRegistredArgs.Interceptors
); );
@ -71,10 +76,12 @@ namespace Autofac.Builder
return registrationBuilder; return registrationBuilder;
} }
private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> AddInterceptors<TLimit, TActivatorData, TRegistrationStyle>( private static IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle>
this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder, AddInterceptors<TLimit, TActivatorData, TRegistrationStyle>(
Type serviceType, this IRegistrationBuilder<TLimit, TActivatorData, TRegistrationStyle> registrationBuilder,
IEnumerable<Type> interceptors) ServiceRegistrationActionList serviceRegistrationActionList,
Type serviceType,
IEnumerable<Type> interceptors)
where TActivatorData : ReflectionActivatorData where TActivatorData : ReflectionActivatorData
{ {
if (serviceType.IsInterface) if (serviceType.IsInterface)
@ -83,6 +90,11 @@ namespace Autofac.Builder
} }
else else
{ {
if (serviceRegistrationActionList.IsClassInterceptorsDisabled)
{
return registrationBuilder;
}
(registrationBuilder as IRegistrationBuilder<TLimit, ConcreteReflectionActivatorData, TRegistrationStyle>)?.EnableClassInterceptors(); (registrationBuilder as IRegistrationBuilder<TLimit, ConcreteReflectionActivatorData, TRegistrationStyle>)?.EnableClassInterceptors();
} }

16
framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs

@ -485,16 +485,26 @@ namespace Volo.Abp.BlazoriseUI
try try
{ {
await CheckDeletePolicyAsync(); await CheckDeletePolicyAsync();
await OnDeletingEntityAsync();
await AppService.DeleteAsync(entity.Id); await AppService.DeleteAsync(entity.Id);
await GetEntitiesAsync(); await OnDeletedEntityAsync();
await InvokeAsync(StateHasChanged);
} }
catch (Exception ex) catch (Exception ex)
{ {
await HandleErrorAsync(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) protected virtual string GetDeleteConfirmationMessage(TListViewModel entity)
{ {

47
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Args namespace Volo.Abp.Cli.Args
@ -71,6 +72,11 @@ namespace Volo.Abp.Cli.Args
return commandLineArgs; return commandLineArgs;
} }
public CommandLineArgs Parse(string lineText)
{
return Parse(GetArgsArrayFromLine(lineText));
}
private static bool IsOptionName(string argument) private static bool IsOptionName(string argument)
{ {
return argument.StartsWith("-") || argument.StartsWith("--"); return argument.StartsWith("-") || argument.StartsWith("--");
@ -100,5 +106,46 @@ namespace Volo.Abp.Cli.Args
throw new ArgumentException("Option names should start with '-' or '--'."); throw new ArgumentException("Option names should start with '-' or '--'.");
} }
private static string[] GetArgsArrayFromLine(string lineText)
{
var args = new List<string>();
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();
}
} }
} }

2
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs

@ -3,5 +3,7 @@
public interface ICommandLineArgumentParser public interface ICommandLineArgumentParser
{ {
CommandLineArgs Parse(string[] args); CommandLineArgs Parse(string[] args);
CommandLineArgs Parse(string lineText);
} }
} }

6
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 Microsoft.Extensions.Logging.Abstractions;
using NuGet.Versioning; using NuGet.Versioning;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Cli.Args; using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Commands; using Volo.Abp.Cli.Commands;
@ -141,11 +143,11 @@ namespace Volo.Abp.Cli
lineText = lineText.Substring(0, lineText.IndexOf('#')); lineText = lineText.Substring(0, lineText.IndexOf('#'));
} }
var args = CommandLineArgumentParser.Parse(lineText.Split(' ')); var args = CommandLineArgumentParser.Parse(lineText);
await RunInternalAsync(args); await RunInternalAsync(args);
} }
} }
private async Task RunInternalAsync(CommandLineArgs commandLineArgs) private async Task RunInternalAsync(CommandLineArgs commandLineArgs)
{ {
var commandType = CommandSelector.Select(commandLineArgs); var commandType = CommandSelector.Select(commandLineArgs);

10
framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs

@ -28,6 +28,16 @@ namespace Microsoft.Extensions.DependencyInjection
return actionList; 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 // OnExposing

2
framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceRegistrationActionList.cs

@ -5,6 +5,6 @@ namespace Volo.Abp.DependencyInjection
{ {
public class ServiceRegistrationActionList : List<Action<IOnServiceRegistredContext>> public class ServiceRegistrationActionList : List<Action<IOnServiceRegistredContext>>
{ {
public bool IsClassInterceptorsDisabled { get; set; }
} }
} }

15
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs

@ -39,6 +39,11 @@ namespace Volo.Abp.EntityFrameworkCore
DefaultConfigureAction = action; DefaultConfigureAction = action;
} }
public bool IsConfiguredDefault()
{
return DefaultConfigureAction != null;
}
public void PreConfigure<TDbContext>([NotNull] Action<AbpDbContextConfigurationContext<TDbContext>> action) public void PreConfigure<TDbContext>([NotNull] Action<AbpDbContextConfigurationContext<TDbContext>> action)
where TDbContext : AbpDbContext<TDbContext> where TDbContext : AbpDbContext<TDbContext>
{ {
@ -60,6 +65,16 @@ namespace Volo.Abp.EntityFrameworkCore
ConfigureActions[typeof(TDbContext)] = action; ConfigureActions[typeof(TDbContext)] = action;
} }
public bool IsConfigured<TDbContext>()
{
return IsConfigured(typeof(TDbContext));
}
public bool IsConfigured(Type dbContextType)
{
return ConfigureActions.ContainsKey(dbContextType);
}
internal Type GetReplacedTypeOrSelf(Type dbContextType) internal Type GetReplacedTypeOrSelf(Type dbContextType)
{ {

18
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 IdentityModel.Client;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.Authentication; using Volo.Abp.Http.Client.Authentication;
using Volo.Abp.IdentityModel; using Volo.Abp.IdentityModel;
@ -8,16 +11,18 @@ using Volo.Abp.IdentityModel;
namespace Volo.Abp.Http.Client.IdentityModel.WebAssembly namespace Volo.Abp.Http.Client.IdentityModel.WebAssembly
{ {
[Dependency(ReplaceServices = true)] [Dependency(ReplaceServices = true)]
public class AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator : IdentityModelRemoteServiceHttpClientAuthenticator public class AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator
: IdentityModelRemoteServiceHttpClientAuthenticator
{ {
[CanBeNull]
protected IAccessTokenProvider AccessTokenProvider { get; } protected IAccessTokenProvider AccessTokenProvider { get; }
public AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator( public AccessTokenProviderIdentityModelRemoteServiceHttpClientAuthenticator(
IIdentityModelAuthenticationService identityModelAuthenticationService, IIdentityModelAuthenticationService identityModelAuthenticationService,
IAccessTokenProvider accessTokenProvider) IServiceProvider serviceProvider)
: base(identityModelAuthenticationService) : base(identityModelAuthenticationService)
{ {
AccessTokenProvider = accessTokenProvider; AccessTokenProvider = serviceProvider.GetService<IAccessTokenProvider>();
} }
public override async Task Authenticate(RemoteServiceHttpClientAuthenticateContext context) public override async Task Authenticate(RemoteServiceHttpClientAuthenticateContext context)
@ -37,6 +42,11 @@ namespace Volo.Abp.Http.Client.IdentityModel.WebAssembly
protected virtual async Task<string> GetAccessTokenFromAccessTokenProviderOrNullAsync() protected virtual async Task<string> GetAccessTokenFromAccessTokenProviderOrNullAsync()
{ {
if (AccessTokenProvider == null)
{
return null;
}
var result = await AccessTokenProvider.RequestAccessToken(); var result = await AccessTokenProvider.RequestAccessToken();
if (result.Status != AccessTokenResultStatus.Success) if (result.Status != AccessTokenResultStatus.Success)
{ {

5
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs

@ -2,6 +2,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Data; using Volo.Abp.Data;
@ -68,7 +69,9 @@ namespace Volo.Abp.ObjectExtending
[NotNull] [NotNull]
public virtual ImmutableList<ObjectExtensionPropertyInfo> GetProperties() public virtual ImmutableList<ObjectExtensionPropertyInfo> GetProperties()
{ {
return Properties.Values.ToImmutableList(); return Properties.OrderBy(t=>t.Key)
.Select(t=>t.Value)
.ToImmutableList();
} }
[CanBeNull] [CanBeNull]

12
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json

@ -11,7 +11,7 @@
"Close": "Cerrar", "Close": "Cerrar",
"Save": "Guardar", "Save": "Guardar",
"SavingWithThreeDot": "Guardando...", "SavingWithThreeDot": "Guardando...",
"Actions": "Acciones", "Actions": "Opciones",
"Delete": "Borrar", "Delete": "Borrar",
"Edit": "Editar", "Edit": "Editar",
"Refresh": "Actualizar", "Refresh": "Actualizar",
@ -20,10 +20,10 @@
"ProcessingWithThreeDot": "Procesando...", "ProcessingWithThreeDot": "Procesando...",
"LoadingWithThreeDot": "Cargardo...", "LoadingWithThreeDot": "Cargardo...",
"Welcome": "Bienvenido", "Welcome": "Bienvenido",
"Login": "Iniciar Sesión", "Login": "Iniciar sesión",
"Register": "Registrarse", "Register": "Registrarse",
"Logout": "Cerrar sesión", "Logout": "Cerrar sesión",
"Submit": "Enviar", "Submit": "Guardar",
"Back": "Volver", "Back": "Volver",
"PagerSearch": "Buscar", "PagerSearch": "Buscar",
"PagerNext": "Siguiente", "PagerNext": "Siguiente",
@ -37,11 +37,11 @@
"NoDataAvailableInDatatable": "No hay datos disponibles", "NoDataAvailableInDatatable": "No hay datos disponibles",
"Total": "Total", "Total": "Total",
"Selected": "Seleccionado", "Selected": "Seleccionado",
"PagerShowMenuEntries": "Mostrar _MENU_ entradas", "PagerShowMenuEntries": "Mostrar _MENU_ registros",
"DatatableActionDropdownDefaultText": "Acciones", "DatatableActionDropdownDefaultText": "Opciones",
"ChangePassword": "Cambiar contraseña", "ChangePassword": "Cambiar contraseña",
"PersonalInfo": "Mi perfil", "PersonalInfo": "Mi perfil",
"AreYouSureYouWantToCancelEditingWarningMessage": "Tu tienes cambios sin guardar", "AreYouSureYouWantToCancelEditingWarningMessage": "Tienes cambios sin guardar",
"GoHomePage": "Ir a la página principal", "GoHomePage": "Ir a la página principal",
"GoBack": "Volver atrás", "GoBack": "Volver atrás",
"Search": "Buscar", "Search": "Buscar",

21
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.ShouldContainKey("optionC");
commandLineArgs.Options["optionC"].ShouldBe("value2"); 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();
}
} }
} }

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Edit.cshtml

@ -7,7 +7,7 @@
@inject IHtmlLocalizer<BloggingResource> L @inject IHtmlLocalizer<BloggingResource> L
@model EditModel @model EditModel
@{ @{
ViewBag.PageTitle = "Blog"; ViewBag.PageTitle = "Edit Blog Post";
} }
@section styles { @section styles {
<abp-style-bundle name="@typeof(EditModel).FullName"> <abp-style-bundle name="@typeof(EditModel).FullName">

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml

@ -11,7 +11,7 @@
@inject IHtmlLocalizer<BloggingResource> L @inject IHtmlLocalizer<BloggingResource> L
@inject BloggingPageHelper BloggingPageHelper @inject BloggingPageHelper BloggingPageHelper
@{ @{
ViewBag.Title = "Blog"; ViewBag.Title = "Read All Blog Posts";
} }
@section scripts { @section scripts {
<abp-script-bundle name="@typeof(IndexModel).FullName"> <abp-script-bundle name="@typeof(IndexModel).FullName">

2
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/New.cshtml

@ -7,7 +7,7 @@
@inject IHtmlLocalizer<BloggingResource> L @inject IHtmlLocalizer<BloggingResource> L
@model NewModel @model NewModel
@{ @{
ViewBag.PageTitle = "Blog"; ViewBag.PageTitle = "New Blog Post";
} }
@section styles { @section styles {
<abp-style-bundle name="@typeof(NewModel).FullName"> <abp-style-bundle name="@typeof(NewModel).FullName">

12
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<DocumentUpdateInfo> _documentUpdateCache; private readonly IDistributedCache<DocumentUpdateInfo> _documentUpdateCache;
private readonly IDistributedCache<List<VersionInfo>> _versionCache; private readonly IDistributedCache<List<VersionInfo>> _versionCache;
private readonly IDistributedCache<LanguageConfig> _languageCache; private readonly IDistributedCache<LanguageConfig> _languageCache;
private readonly IDocumentFullSearch _documentFullSearch; private readonly IDocumentFullSearch _elasticSearchService;
public DocumentAdminAppService(IProjectRepository projectRepository, public DocumentAdminAppService(IProjectRepository projectRepository,
IDocumentRepository documentRepository, IDocumentRepository documentRepository,
@ -35,7 +35,7 @@ namespace Volo.Docs.Admin.Documents
IDistributedCache<DocumentUpdateInfo> documentUpdateCache, IDistributedCache<DocumentUpdateInfo> documentUpdateCache,
IDistributedCache<List<VersionInfo>> versionCache, IDistributedCache<List<VersionInfo>> versionCache,
IDistributedCache<LanguageConfig> languageCache, IDistributedCache<LanguageConfig> languageCache,
IDocumentFullSearch documentFullSearch) IDocumentFullSearch elasticSearchService)
{ {
_projectRepository = projectRepository; _projectRepository = projectRepository;
_documentRepository = documentRepository; _documentRepository = documentRepository;
@ -43,7 +43,7 @@ namespace Volo.Docs.Admin.Documents
_documentUpdateCache = documentUpdateCache; _documentUpdateCache = documentUpdateCache;
_versionCache = versionCache; _versionCache = versionCache;
_languageCache = languageCache; _languageCache = languageCache;
_documentFullSearch = documentFullSearch; _elasticSearchService = elasticSearchService;
LocalizationResource = typeof(DocsResource); LocalizationResource = typeof(DocsResource);
} }
@ -210,9 +210,11 @@ namespace Volo.Docs.Admin.Documents
public async Task ReindexAsync(Guid documentId) public async Task ReindexAsync(Guid documentId)
{ {
await _documentFullSearch.DeleteAsync(documentId); _elasticSearchService.ValidateElasticSearchEnabled();
await _elasticSearchService.DeleteAsync(documentId);
var document = await _documentRepository.GetAsync(documentId); var document = await _documentRepository.GetAsync(documentId);
await _documentFullSearch.AddOrUpdateAsync(document); await _elasticSearchService.AddOrUpdateAsync(document);
} }
private async Task UpdateDocumentUpdateInfoCache(Document document) private async Task UpdateDocumentUpdateInfoCache(Document document)

48
modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
@ -18,13 +17,13 @@ namespace Volo.Docs.Admin.Projects
{ {
private readonly IProjectRepository _projectRepository; private readonly IProjectRepository _projectRepository;
private readonly IDocumentRepository _documentRepository; private readonly IDocumentRepository _documentRepository;
private readonly IDocumentFullSearch _documentFullSearch; private readonly IDocumentFullSearch _elasticSearchService;
private readonly IGuidGenerator _guidGenerator; private readonly IGuidGenerator _guidGenerator;
public ProjectAdminAppService( public ProjectAdminAppService(
IProjectRepository projectRepository, IProjectRepository projectRepository,
IDocumentRepository documentRepository, IDocumentRepository documentRepository,
IDocumentFullSearch documentFullSearch, IDocumentFullSearch elasticSearchService,
IGuidGenerator guidGenerator) IGuidGenerator guidGenerator)
{ {
ObjectMapperContext = typeof(DocsAdminApplicationModule); ObjectMapperContext = typeof(DocsAdminApplicationModule);
@ -32,7 +31,7 @@ namespace Volo.Docs.Admin.Projects
_projectRepository = projectRepository; _projectRepository = projectRepository;
_documentRepository = documentRepository; _documentRepository = documentRepository;
_documentFullSearch = documentFullSearch; _elasticSearchService = elasticSearchService;
_guidGenerator = guidGenerator; _guidGenerator = guidGenerator;
} }
@ -120,11 +119,21 @@ namespace Volo.Docs.Admin.Projects
public async Task ReindexAsync(ReindexInput input) 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); var docs = await _documentRepository.GetListByProjectId(project.Id);
await _elasticSearchService.DeleteAllByProjectIdAsync(project.Id);
foreach (var doc in docs) foreach (var doc in docs)
{ {
@ -138,35 +147,18 @@ namespace Volo.Docs.Admin.Projects
continue; continue;
} }
await _documentFullSearch.AddOrUpdateAsync(doc); await _elasticSearchService.AddOrUpdateAsync(doc);
} }
} }
public async Task ReindexAllAsync() public async Task ReindexAllAsync()
{ {
await _documentFullSearch.DeleteAllAsync(); _elasticSearchService.ValidateElasticSearchEnabled();
var docs = await _documentRepository.GetListAsync();
var projects = await _projectRepository.GetListAsync(); var projects = await _projectRepository.GetListAsync();
foreach (var doc in docs)
foreach (var project in projects)
{ {
var project = projects.FirstOrDefault(x => x.Id == doc.ProjectId); await ReindexProjectAsync(project.Id);
if (project == null)
{
continue;
}
if (doc.FileName == project.NavigationDocumentName)
{
continue;
}
if (doc.FileName == project.ParametersDocumentName)
{
continue;
}
await _documentFullSearch.AddOrUpdateAsync(doc);
} }
} }
} }

10
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) public virtual async Task AddOrUpdateAsync(Document document, CancellationToken cancellationToken = default)
{ {
ValidateElasticSearchEnabled();
var client = _clientProvider.GetClient(); var client = _clientProvider.GetClient();
var esDocument = new EsDocument var esDocument = new EsDocument
@ -71,15 +69,13 @@ namespace Volo.Docs.Documents.FullSearch.Elastic
Version = NormalizeField(document.Version) 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) public virtual async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)
{ {
ValidateElasticSearchEnabled();
HandleError(await _clientProvider.GetClient() HandleError(await _clientProvider.GetClient()
.DeleteAsync(DocumentPath<Document>.Id( NormalizeField(id)), x => x.Index(_options.IndexName), cancellationToken)); .DeleteAsync(DocumentPath<Document>.Id(NormalizeField(id)), x => x.Index(_options.IndexName), cancellationToken));
} }
public virtual async Task DeleteAllAsync(CancellationToken cancellationToken = default) 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) if (!_options.Enable)
{ {

2
modules/docs/src/Volo.Docs.Domain/Volo/Docs/Documents/FullSearch/Elastic/IDocumentFullSearch.cs

@ -20,5 +20,7 @@ namespace Volo.Docs.Documents.FullSearch.Elastic
Task<List<EsDocument>> SearchAsync(string context, Guid projectId, string languageCode, Task<List<EsDocument>> SearchAsync(string context, Guid projectId, string languageCode,
string version, int? skipCount = null, int? maxResultCount = null, string version, int? skipCount = null, int? maxResultCount = null,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
void ValidateElasticSearchEnabled();
} }
} }

37
modules/docs/src/Volo.Docs.Web/Bundling/PrismjsScriptBundleContributorDocsExtension.cs

@ -5,14 +5,47 @@ namespace Volo.Docs.Bundling
{ {
public class PrismjsScriptBundleContributorDocsExtension : BundleContributor 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) 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/toolbar/prism-toolbar.js");
context.Files.AddIfNotContains("/libs/prismjs/plugins/show-language/prism-show-language.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/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/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");
} }
} }
} }

20
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/es.json

@ -16,8 +16,8 @@
"PersonalInfo": "Mi perfil", "PersonalInfo": "Mi perfil",
"PersonalSettings": "Configuración personal", "PersonalSettings": "Configuración personal",
"UserDeletionConfirmationMessage": "El usuario '{0}' será eliminado. ¿Quieres continuar?", "UserDeletionConfirmationMessage": "El usuario '{0}' será eliminado. ¿Quieres continuar?",
"RoleDeletionConfirmationMessage": "El role '{0}' será eliminado. ¿Quieres continuar? ", "RoleDeletionConfirmationMessage": "El rol '{0}' será eliminado. ¿Quieres continuar? ",
"DisplayName:RoleName": "Nombre de Role", "DisplayName:RoleName": "Nombre de rol",
"DisplayName:UserName": "Nombre de usuario", "DisplayName:UserName": "Nombre de usuario",
"DisplayName:Name": "Nombre", "DisplayName:Name": "Nombre",
"DisplayName:Surname": "Apellido", "DisplayName:Surname": "Apellido",
@ -26,8 +26,8 @@
"DisplayName:PhoneNumber": "Número de teléfono", "DisplayName:PhoneNumber": "Número de teléfono",
"DisplayName:TwoFactorEnabled": "Autenticación en dos pasos", "DisplayName:TwoFactorEnabled": "Autenticación en dos pasos",
"DisplayName:LockoutEnabled": "Bloquear la cuenta después de intentos de inicio de sesión fallidos", "DisplayName:LockoutEnabled": "Bloquear la cuenta después de intentos de inicio de sesión fallidos",
"NewRole": "Nuevo role", "NewRole": "Nuevo rol",
"RoleName": "Nombre de role", "RoleName": "Nombre de rol",
"CreationTime": "Fecha de creación", "CreationTime": "Fecha de creación",
"Permissions": "Permisos", "Permissions": "Permisos",
"DisplayName:CurrentPassword": "Contraseña actual", "DisplayName:CurrentPassword": "Contraseña actual",
@ -38,12 +38,12 @@
"Volo.Abp.Identity:DefaultError": "Error desconocido.", "Volo.Abp.Identity:DefaultError": "Error desconocido.",
"Volo.Abp.Identity:ConcurrencyFailure": "Fallo de concurrencia optimista", "Volo.Abp.Identity:ConcurrencyFailure": "Fallo de concurrencia optimista",
"Volo.Abp.Identity:DuplicateEmail": "E-mail '{0}' está en uso.", "Volo.Abp.Identity:DuplicateEmail": "E-mail '{0}' está en uso.",
"Volo.Abp.Identity:DuplicateRoleName": "Nombre de role '{0}' está en uso.", "Volo.Abp.Identity:DuplicateRoleName": "El nombre de rol '{0}' ya está en uso.",
"Volo.Abp.Identity:DuplicateUserName": "Nombre de usuario '{0}' 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:InvalidEmail": "E-mail '{0}' no es válido.",
"Volo.Abp.Identity:InvalidPasswordHasherCompatibilityMode": "El PasswordHasherCompatibilityMode proporcionado 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: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: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: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.", "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: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: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: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:UserAlreadyHasPassword": "El usuario tiene la contraseña establecida",
"Volo.Abp.Identity:UserAlreadyInRole": "El usuario ya tiene asignado el rol '{0}'´.", "Volo.Abp.Identity:UserAlreadyInRole": "El usuario ya tiene asignado el rol '{0}'´.",
"Volo.Abp.Identity:UserLockedOut": "El usuario está bloqueado.", "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: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: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: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:010005": "Los roles estáticos no pueden ser renombrados.",
"Volo.Abp.Identity:010006": "Roles estáticos no pueden ser borrados.", "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: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.", "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", "Identity.OrganizationUnit.MaxUserMembershipCount": "Número máximo de unidades organizativas por usuario",

5
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpStrictRedirectUriValidator.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Validation; using IdentityServer4.Validation;
@ -33,7 +34,9 @@ namespace Volo.Abp.IdentityServer
var extractResult = FormattedStringValueExtracter.Extract(requestedUri, url, ignoreCase: true); var extractResult = FormattedStringValueExtracter.Extract(requestedUri, url, ignoreCase: true);
if (extractResult.IsMatch) 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)) if (url.Replace("{0}.", "").Contains(requestedUri, StringComparison.OrdinalIgnoreCase))

4
modules/identityserver/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpWildcardSubdomainCorsPolicyService.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4.Configuration; using IdentityServer4.Configuration;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -32,7 +33,8 @@ namespace Volo.Abp.IdentityServer
var extractResult = FormattedStringValueExtracter.Extract(origin, url, ignoreCase: true); var extractResult = FormattedStringValueExtracter.Extract(origin, url, ignoreCase: true);
if (extractResult.IsMatch) 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)) if (url.Replace("{0}.", "").Contains(origin, StringComparison.OrdinalIgnoreCase))

2
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("http://ng.abp.io", _testClient)).ShouldBeTrue();
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse(); (await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://ng.t1.abp.io", _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] [Fact]
@ -68,6 +69,7 @@ namespace Volo.Abp.IdentityServer
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeTrue(); (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeTrue();
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse(); (await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.t1.abp.io", _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();
} }
} }
} }

2
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://abp.io")).ShouldBeTrue();
(await _corsPolicyService.IsOriginAllowedAsync("https://t1.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.ng.abp.io")).ShouldBeTrue();
(await _corsPolicyService.IsOriginAllowedAsync("https://t1.abp.io.mydomain.com")).ShouldBeFalse();
} }
} }
} }

1
npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts

@ -12,7 +12,6 @@ describe('LocalizationService', () => {
let spectator: SpectatorService<LocalizationService>; let spectator: SpectatorService<LocalizationService>;
let sessionState: SpyObject<SessionStateService>; let sessionState: SpyObject<SessionStateService>;
let configState: SpyObject<ConfigStateService>; let configState: SpyObject<ConfigStateService>;
let router: SpyObject<Router>;
let service: LocalizationService; let service: LocalizationService;
const createService = createServiceFactory({ const createService = createServiceFactory({

2
npm/ng-packs/packages/theme-shared/extensions/src/tests/enum.util.spec.ts

@ -110,5 +110,5 @@ function createMockLocalizationService() {
const configState = new ConfigStateService(); const configState = new ConfigStateService();
configState.setState({ localization: mockL10n } as any); configState.setState({ localization: mockL10n } as any);
return new LocalizationService(mockSessionState, null, null, null, configState, null); return new LocalizationService(mockSessionState, null, null, configState, null);
} }

11
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 () => {};
}

1
npm/ng-packs/packages/theme-shared/src/lib/handlers/index.ts

@ -1,2 +1,3 @@
export * from './error.handler'; export * from './error.handler';
export * from './lazy-style.handler'; export * from './lazy-style.handler';
export * from './datepicker-config';

7
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 { HTTP_ERROR_CONFIG, httpErrorConfigFactory } from './tokens/http-error.token';
import { DateParserFormatter } from './utils/date-parser-formatter'; import { DateParserFormatter } from './utils/date-parser-formatter';
import { ModalCloseDirective } from './components/modal/modal-close.directive'; import { ModalCloseDirective } from './components/modal/modal-close.directive';
import { initDatepickerConfig } from './handlers/datepicker-config';
const declarationsWithExports = [ const declarationsWithExports = [
BreadcrumbComponent, BreadcrumbComponent,
@ -110,6 +111,12 @@ export class ThemeSharedModule {
deps: [Injector], deps: [Injector],
useFactory: initLazyStyleHandler, useFactory: initLazyStyleHandler,
}, },
{
provide: APP_INITIALIZER,
multi: true,
deps: [Injector],
useFactory: initDatepickerConfig,
},
{ provide: HTTP_ERROR_CONFIG, useValue: httpErrorConfig }, { provide: HTTP_ERROR_CONFIG, useValue: httpErrorConfig },
{ {
provide: 'HTTP_ERROR_CONFIG', provide: 'HTTP_ERROR_CONFIG',

2
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/constants/validation';
export * from './lib/directives'; export * from './lib/directives';
export * from './lib/enums'; export * from './lib/enums';
export { ErrorHandler } from './lib/handlers'; export * from './lib/handlers';
export * from './lib/models'; export * from './lib/models';
export * from './lib/providers'; export * from './lib/providers';
export * from './lib/services'; export * from './lib/services';

Loading…
Cancel
Save