Browse Source

Merge branch 'dev' into maliming/DateTimeModelBinder

pull/8891/head
maliming 6 years ago
parent
commit
d81364fd47
  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. 12
      framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs
  31. 14
      framework/src/Volo.Abp.AspNetCore.Components.Web.BasicTheme/Themes/Basic/AppWithoutAuth.razor
  32. 23
      framework/src/Volo.Abp.AspNetCore.Components.Web/Volo/Abp/AspNetCore/Components/Web/Security/AbpComponentsClaimsCache.cs
  33. 13
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/BasicThemeToolbarContributor.cs
  34. 27
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor
  35. 49
      framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/LoginDisplay.razor.cs
  36. 24
      framework/src/Volo.Abp.AspNetCore.Components/Volo/Abp/AspNetCore/Components/AbpComponentBase.cs
  37. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/FindTenantResultDto.cs
  38. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/AbpTenantAppService.cs
  39. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/TenantSwitchModal.cshtml.cs
  40. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json
  41. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/cs.json
  42. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de-DE.json
  43. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/de.json
  44. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en-GB.json
  45. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/en.json
  46. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es-mx.json
  47. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/es.json
  48. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fi.json
  49. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/fr.json
  50. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/hu.json
  51. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/nl.json
  52. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json
  53. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pt-BR.json
  54. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ru.json
  55. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/sl.json
  56. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/tr.json
  57. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/vi.json
  58. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hans.json
  59. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/zh-Hant.json
  60. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarItem.cs
  61. 21
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs
  62. 3
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs
  63. 9
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionStateManager.cs
  64. 9
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionStateProvider.cs
  65. 9
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs
  66. 19
      framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionExtensions.cs
  67. 11
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs
  68. 57
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionSimpleStateCheckerExtensions.cs
  69. 51
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionStateManager.cs
  70. 16
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequireAuthenticatedSimpleStateChecker.cs
  71. 66
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs
  72. 24
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateCheckerModel.cs
  73. 34
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs
  74. 22
      framework/src/Volo.Abp.Autofac/Autofac/Builder/AbpRegistrationBuilderExtensions.cs
  75. 16
      framework/src/Volo.Abp.BlazoriseUI/AbpCrudPageBase.cs
  76. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs
  77. 47
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/CommandLineArgumentParser.cs
  78. 2
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Args/ICommandLineArgumentParser.cs
  79. 43
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs
  80. 16
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/LoginInfo.cs
  81. 73
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/CliService.cs
  82. 63
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginInfoCommand..cs
  83. 12
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  84. 10
      framework/src/Volo.Abp.Core/Microsoft/Extensions/DependencyInjection/ServiceCollectionRegistrationActionExtensions.cs
  85. 2
      framework/src/Volo.Abp.Core/Volo/Abp/DependencyInjection/ServiceRegistrationActionList.cs
  86. 3
      framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs
  87. 15
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/AbpSimpleStateCheckerOptions.cs
  88. 10
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/IHasSimpleStateCheckers.cs
  89. 10
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleBatchStateChecker.cs
  90. 10
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs
  91. 12
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerManager.cs
  92. 16
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleBatchStateCheckerBase.cs
  93. 18
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleBatchStateCheckerContext.cs
  94. 18
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerContext.cs
  95. 112
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerManager.cs
  96. 21
      framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerResult.cs
  97. 17
      framework/src/Volo.Abp.Data/Volo/Abp/Data/IConnectionStringResolverExtensions.cs
  98. 15
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContextOptions.cs
  99. 9
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs
  100. 30
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureSimpleStateCheckerExtensions.cs

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

12
framework/src/Volo.Abp.AspNetCore.Authentication.OpenIdConnect/Microsoft/Extensions/DependencyInjection/AbpOpenIdConnectExtensions.cs

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims;
using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Volo.Abp.AspNetCore.MultiTenancy; using Volo.Abp.AspNetCore.MultiTenancy;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
@ -35,6 +36,17 @@ namespace Microsoft.Extensions.DependencyInjection
SetAbpTenantId(receivedContext); SetAbpTenantId(receivedContext);
return authorizationCodeReceived.Invoke(receivedContext); return authorizationCodeReceived.Invoke(receivedContext);
}; };
options.Events.OnRemoteFailure = remoteFailureContext =>
{
if (remoteFailureContext.Failure is OpenIdConnectProtocolException &&
remoteFailureContext.Failure.Message.Contains("access_denied"))
{
remoteFailureContext.HandleResponse();
remoteFailureContext.Response.Redirect($"{remoteFailureContext.Request.PathBase}/");
}
return Task.CompletedTask;
};
}); });
} }

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; }

4
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/MultiTenancy/FindTenantResultDto.cs

@ -10,5 +10,7 @@ namespace Volo.Abp.AspNetCore.Mvc.MultiTenancy
public Guid? TenantId { get; set; } public Guid? TenantId { get; set; }
public string Name { get; set; } public string Name { get; set; }
public bool IsActive { get; set; }
} }
} }

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Pages/Abp/MultiTenancy/AbpTenantAppService.cs

@ -28,10 +28,11 @@ namespace Pages.Abp.MultiTenancy
{ {
Success = true, Success = true,
TenantId = tenant.Id, TenantId = tenant.Id,
Name = tenant.Name Name = tenant.Name,
IsActive = tenant.IsActive
}; };
} }
public async Task<FindTenantResultDto> FindTenantByIdAsync(Guid id) public async Task<FindTenantResultDto> FindTenantByIdAsync(Guid id)
{ {
var tenant = await TenantStore.FindAsync(id); var tenant = await TenantStore.FindAsync(id);
@ -45,8 +46,9 @@ namespace Pages.Abp.MultiTenancy
{ {
Success = true, Success = true,
TenantId = tenant.Id, TenantId = tenant.Id,
Name = tenant.Name Name = tenant.Name,
IsActive = tenant.IsActive
}; };
} }
} }
} }

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

@ -20,7 +20,7 @@ namespace Pages.Abp.MultiTenancy
protected AbpAspNetCoreMultiTenancyOptions Options { get; } protected AbpAspNetCoreMultiTenancyOptions Options { get; }
public TenantSwitchModalModel( public TenantSwitchModalModel(
ITenantStore tenantStore, ITenantStore tenantStore,
IOptions<AbpAspNetCoreMultiTenancyOptions> options) IOptions<AbpAspNetCoreMultiTenancyOptions> options)
{ {
TenantStore = tenantStore; TenantStore = tenantStore;
@ -49,6 +49,11 @@ namespace Pages.Abp.MultiTenancy
{ {
var tenant = await TenantStore.FindAsync(Input.Name); var tenant = await TenantStore.FindAsync(Input.Name);
if (tenant == null) 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]);
} }
@ -71,4 +76,4 @@ namespace Pages.Abp.MultiTenancy
public string Name { get; set; } public string Name { get; set; }
} }
} }
} }

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": "尚未選定"
} }
} }

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarItem.cs

@ -1,9 +1,11 @@
using System; using System;
using System.Collections.Generic;
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
{ {
public class ToolbarItem public class ToolbarItem : IHasSimpleStateCheckers<ToolbarItem>
{ {
public Type ComponentType public Type ComponentType
{ {
@ -15,13 +17,17 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
public int Order { get; set; } public int Order { get; set; }
[CanBeNull] [CanBeNull]
[Obsolete("Use RequirePermissions extension method.")]
public string RequiredPermissionName { get; set; } public string RequiredPermissionName { get; set; }
public List<ISimpleStateChecker<ToolbarItem>> StateCheckers { get; }
public ToolbarItem([NotNull] Type componentType, int order = 0, string requiredPermissionName = null) public ToolbarItem([NotNull] Type componentType, int order = 0, string requiredPermissionName = null)
{ {
Order = order; Order = order;
ComponentType = Check.NotNull(componentType, nameof(componentType)); ComponentType = Check.NotNull(componentType, nameof(componentType));
RequiredPermissionName = requiredPermissionName; RequiredPermissionName = requiredPermissionName;
StateCheckers = new List<ISimpleStateChecker<ToolbarItem>>();
} }
} }
} }

21
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Toolbars/ToolbarManager.cs

@ -7,6 +7,7 @@ using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.UI.Theming; using Volo.Abp.AspNetCore.Mvc.UI.Theming;
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
{ {
@ -15,13 +16,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
protected IThemeManager ThemeManager { get; } protected IThemeManager ThemeManager { get; }
protected AbpToolbarOptions Options { get; } protected AbpToolbarOptions Options { get; }
protected IServiceProvider ServiceProvider { get; } protected IServiceProvider ServiceProvider { get; }
protected ISimpleStateCheckerManager<ToolbarItem> SimpleStateCheckerManager { get; }
public ToolbarManager( public ToolbarManager(
IOptions<AbpToolbarOptions> options, IOptions<AbpToolbarOptions> options,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
IThemeManager themeManager) IThemeManager themeManager,
ISimpleStateCheckerManager<ToolbarItem> simpleStateCheckerManager)
{ {
ThemeManager = themeManager; ThemeManager = themeManager;
SimpleStateCheckerManager = simpleStateCheckerManager;
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
Options = options.Value; Options = options.Value;
} }
@ -47,17 +51,20 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars
protected virtual async Task CheckPermissionsAsync(IServiceProvider serviceProvider, Toolbar toolbar) protected virtual async Task CheckPermissionsAsync(IServiceProvider serviceProvider, Toolbar toolbar)
{ {
var requiredPermissionItems = toolbar.Items.Where(x => !x.RequiredPermissionName.IsNullOrWhiteSpace()).ToList(); foreach (var item in toolbar.Items.Where(x => !x.RequiredPermissionName.IsNullOrWhiteSpace()))
{
item.RequirePermissions(item.RequiredPermissionName);
}
if (requiredPermissionItems.Any()) var checkPermissionsToolbarItems = toolbar.Items.Where(x => x.StateCheckers.Any()).ToArray();
if (checkPermissionsToolbarItems.Any())
{ {
var permissionChecker = serviceProvider.GetRequiredService<IPermissionChecker>(); var result = await SimpleStateCheckerManager.IsEnabledAsync(checkPermissionsToolbarItems);
var grantResult = await permissionChecker.IsGrantedAsync(requiredPermissionItems.Select(x => x.RequiredPermissionName).Distinct().ToArray());
var toBeDeleted = new HashSet<ToolbarItem>(); var toBeDeleted = new HashSet<ToolbarItem>();
foreach (var item in requiredPermissionItems) foreach (var item in checkPermissionsToolbarItems)
{ {
if (grantResult.Result[item.RequiredPermissionName!] != PermissionGrantResult.Granted) if (!result[item])
{ {
toBeDeleted.Add(item); toBeDeleted.Add(item);
} }

3
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/AbpPermissionOptions.cs

@ -8,13 +8,10 @@ namespace Volo.Abp.Authorization.Permissions
public ITypeList<IPermissionValueProvider> ValueProviders { get; } public ITypeList<IPermissionValueProvider> ValueProviders { get; }
public ITypeList<IPermissionStateProvider> GlobalStateProviders { get; }
public AbpPermissionOptions() public AbpPermissionOptions()
{ {
DefinitionProviders = new TypeList<IPermissionDefinitionProvider>(); DefinitionProviders = new TypeList<IPermissionDefinitionProvider>();
ValueProviders = new TypeList<IPermissionValueProvider>(); ValueProviders = new TypeList<IPermissionValueProvider>();
GlobalStateProviders = new TypeList<IPermissionStateProvider>();
} }
} }
} }

9
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionStateManager.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.Authorization.Permissions
{
public interface IPermissionStateManager
{
Task<bool> IsEnabledAsync(PermissionDefinition permission);
}
}

9
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/IPermissionStateProvider.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.Authorization.Permissions
{
public interface IPermissionStateProvider
{
Task<bool> IsEnabledAsync(PermissionStateContext context);
}
}

9
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs

@ -3,10 +3,11 @@ using System.Collections.Immutable;
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions namespace Volo.Abp.Authorization.Permissions
{ {
public class PermissionDefinition public class PermissionDefinition : IHasSimpleStateCheckers<PermissionDefinition>
{ {
/// <summary> /// <summary>
/// Unique name of the permission. /// Unique name of the permission.
@ -31,7 +32,7 @@ namespace Volo.Abp.Authorization.Permissions
/// </summary> /// </summary>
public List<string> Providers { get; } //TODO: Rename to AllowedProviders? public List<string> Providers { get; } //TODO: Rename to AllowedProviders?
public List<IPermissionStateProvider> StateProviders { get; } public List<ISimpleStateChecker<PermissionDefinition>> StateCheckers { get; }
public ILocalizableString DisplayName public ILocalizableString DisplayName
{ {
@ -88,7 +89,7 @@ namespace Volo.Abp.Authorization.Permissions
Properties = new Dictionary<string, object>(); Properties = new Dictionary<string, object>();
Providers = new List<string>(); Providers = new List<string>();
StateProviders = new List<IPermissionStateProvider>(); StateCheckers = new List<ISimpleStateChecker<PermissionDefinition>>();
_children = new List<PermissionDefinition>(); _children = new List<PermissionDefinition>();
} }
@ -123,7 +124,7 @@ namespace Volo.Abp.Authorization.Permissions
} }
/// <summary> /// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary. /// Set the <see cref="StateProviders"/> property.
/// This is a shortcut for nested calls on this object. /// This is a shortcut for nested calls on this object.
/// </summary> /// </summary>
public virtual PermissionDefinition WithProviders(params string[] providers) public virtual PermissionDefinition WithProviders(params string[] providers)

19
framework/src/Volo.Abp.Authorization.Abstractions/Volo/Abp/Authorization/Permissions/PermissionDefinitionExtensions.cs

@ -1,19 +0,0 @@
using JetBrains.Annotations;
namespace Volo.Abp.Authorization.Permissions
{
public static class PermissionDefinitionExtensions
{
public static PermissionDefinition AddStateProviders(
[NotNull] this PermissionDefinition permissionDefinition,
[NotNull] params IPermissionStateProvider[] permissionStateProviders)
{
Check.NotNull(permissionDefinition, nameof(permissionDefinition));
Check.NotNull(permissionStateProviders, nameof(permissionStateProviders));
permissionDefinition.StateProviders.AddRange(permissionStateProviders);
return permissionDefinition;
}
}
}

11
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions namespace Volo.Abp.Authorization.Permissions
{ {
@ -15,20 +16,20 @@ namespace Volo.Abp.Authorization.Permissions
protected ICurrentPrincipalAccessor PrincipalAccessor { get; } protected ICurrentPrincipalAccessor PrincipalAccessor { get; }
protected ICurrentTenant CurrentTenant { get; } protected ICurrentTenant CurrentTenant { get; }
protected IPermissionValueProviderManager PermissionValueProviderManager { get; } protected IPermissionValueProviderManager PermissionValueProviderManager { get; }
protected IPermissionStateManager PermissionStateManager { get; } protected ISimpleStateCheckerManager<PermissionDefinition> StateCheckerManager { get; }
public PermissionChecker( public PermissionChecker(
ICurrentPrincipalAccessor principalAccessor, ICurrentPrincipalAccessor principalAccessor,
IPermissionDefinitionManager permissionDefinitionManager, IPermissionDefinitionManager permissionDefinitionManager,
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
IPermissionValueProviderManager permissionValueProviderManager, IPermissionValueProviderManager permissionValueProviderManager,
IPermissionStateManager permissionStateManager) ISimpleStateCheckerManager<PermissionDefinition> stateCheckerManager)
{ {
PrincipalAccessor = principalAccessor; PrincipalAccessor = principalAccessor;
PermissionDefinitionManager = permissionDefinitionManager; PermissionDefinitionManager = permissionDefinitionManager;
CurrentTenant = currentTenant; CurrentTenant = currentTenant;
PermissionValueProviderManager = permissionValueProviderManager; PermissionValueProviderManager = permissionValueProviderManager;
PermissionStateManager = permissionStateManager; StateCheckerManager = stateCheckerManager;
} }
public virtual async Task<bool> IsGrantedAsync(string name) public virtual async Task<bool> IsGrantedAsync(string name)
@ -49,7 +50,7 @@ namespace Volo.Abp.Authorization.Permissions
return false; return false;
} }
if (!await PermissionStateManager.IsEnabledAsync(permission)) if (!await StateCheckerManager.IsEnabledAsync(permission))
{ {
return false; return false;
} }
@ -112,7 +113,7 @@ namespace Volo.Abp.Authorization.Permissions
result.Result.Add(name, PermissionGrantResult.Undefined); result.Result.Add(name, PermissionGrantResult.Undefined);
if (permission.IsEnabled && if (permission.IsEnabled &&
await PermissionStateManager.IsEnabledAsync(permission) && await StateCheckerManager.IsEnabledAsync(permission) &&
permission.MultiTenancySide.HasFlag(multiTenancySide)) permission.MultiTenancySide.HasFlag(multiTenancySide))
{ {
permissionDefinitions.Add(permission); permissionDefinitions.Add(permission);

57
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionSimpleStateCheckerExtensions.cs

@ -0,0 +1,57 @@
using JetBrains.Annotations;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions
{
public static class PermissionSimpleStateCheckerExtensions
{
public static TState RequireAuthenticated<TState>([NotNull] this TState state)
where TState : IHasSimpleStateCheckers<TState>
{
state.StateCheckers.Add(new RequireAuthenticatedSimpleStateChecker<TState>());
return state;
}
public static TState RequirePermissions<TState>(
[NotNull] this TState state,
params string[] permissions)
where TState : IHasSimpleStateCheckers<TState>
{
state.RequirePermissions(requiresAll: true, batchCheck: true, permissions);
return state;
}
public static TState RequirePermissions<TState>(
[NotNull] this TState state,
bool requiresAll,
params string[] permissions)
where TState : IHasSimpleStateCheckers<TState>
{
state.RequirePermissions(requiresAll: requiresAll, batchCheck: true, permissions);
return state;
}
public static TState RequirePermissions<TState>(
[NotNull] this TState state,
bool requiresAll,
bool batchCheck,
params string[] permissions)
where TState : IHasSimpleStateCheckers<TState>
{
Check.NotNull(state, nameof(state));
Check.NotNullOrEmpty(permissions, nameof(permissions));
if (batchCheck)
{
RequirePermissionsSimpleBatchStateChecker<TState>.Instance.AddCheckModels(new RequirePermissionsSimpleBatchStateCheckerModel<TState>(state, permissions, requiresAll));
state.StateCheckers.Add(RequirePermissionsSimpleBatchStateChecker<TState>.Instance);
}
else
{
state.StateCheckers.Add(new RequirePermissionsSimpleStateChecker<TState>(new RequirePermissionsSimpleBatchStateCheckerModel<TState>(state, permissions, requiresAll)));
}
return state;
}
}
}

51
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionStateManager.cs

@ -1,51 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions
{
public class PermissionStateManager : IPermissionStateManager, ITransientDependency
{
protected IServiceProvider ServiceProvider { get; }
protected AbpPermissionOptions Options { get; }
public PermissionStateManager(IServiceProvider serviceProvider, IOptions<AbpPermissionOptions> options)
{
ServiceProvider = serviceProvider;
Options = options.Value;
}
public async Task<bool> IsEnabledAsync(PermissionDefinition permission)
{
using (var scope = ServiceProvider.CreateScope())
{
var context = new PermissionStateContext
{
Permission = permission,
ServiceProvider = scope.ServiceProvider.GetRequiredService<ICachedServiceProvider>()
};
foreach (var provider in permission.StateProviders)
{
if (!await provider.IsEnabledAsync(context))
{
return false;
}
}
foreach (IPermissionStateProvider provider in Options.GlobalStateProviders.Select(x => ServiceProvider.GetRequiredService(x)))
{
if (!await provider.IsEnabledAsync(context))
{
return false;
}
}
return true;
}
}
}
}

16
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequireAuthenticatedSimpleStateChecker.cs

@ -0,0 +1,16 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
using Volo.Abp.Users;
namespace Volo.Abp.Authorization.Permissions
{
public class RequireAuthenticatedSimpleStateChecker<TState> : ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context)
{
return Task.FromResult(context.ServiceProvider.GetRequiredService<ICurrentUser>().IsAuthenticated);
}
}
}

66
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateChecker.cs

@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions
{
public class RequirePermissionsSimpleBatchStateChecker<TState> : SimpleBatchStateCheckerBase<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public static readonly RequirePermissionsSimpleBatchStateChecker<TState> Instance = new RequirePermissionsSimpleBatchStateChecker<TState>();
private readonly List<RequirePermissionsSimpleBatchStateCheckerModel<TState>> _models;
public RequirePermissionsSimpleBatchStateChecker()
{
_models = new List<RequirePermissionsSimpleBatchStateCheckerModel<TState>>();
}
public RequirePermissionsSimpleBatchStateChecker<TState> AddCheckModels(params RequirePermissionsSimpleBatchStateCheckerModel<TState>[] models)
{
Check.NotNullOrEmpty(models, nameof(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;
}
public override async Task<SimpleStateCheckerResult<TState>> IsEnabledAsync(SimpleBatchStateCheckerContext<TState> context)
{
var permissionChecker = context.ServiceProvider.GetRequiredService<IPermissionChecker>();
var result = new SimpleStateCheckerResult<TState>(context.States);
var permissions = _models.Where(x => context.States.Any(s => s.Equals(x.State))).SelectMany(x => x.Permissions).Distinct().ToArray();
var grantResult = await permissionChecker.IsGrantedAsync(permissions);
foreach (var state in context.States)
{
var model = _models.FirstOrDefault(x => x.State.Equals(state));
if (model != null)
{
if (model.RequiresAll)
{
result[model.State] = model.Permissions.All(x => grantResult.Result.Any(y => y.Key == x && y.Value == PermissionGrantResult.Granted));
}
else
{
result[model.State] = grantResult.Result.Any(x => model.Permissions.Contains(x.Key) && x.Value == PermissionGrantResult.Granted);
}
}
}
return result;
}
}
}

24
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleBatchStateCheckerModel.cs

@ -0,0 +1,24 @@
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions
{
public class RequirePermissionsSimpleBatchStateCheckerModel<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public TState State { get; }
public string[] Permissions { get; }
public bool RequiresAll { get; }
public RequirePermissionsSimpleBatchStateCheckerModel(TState state, string[] permissions, bool requiresAll = true)
{
Check.NotNull(state, nameof(state));
Check.NotNullOrEmpty(permissions, nameof(permissions));
State = state;
Permissions = permissions;
RequiresAll = requiresAll;
}
}
}

34
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RequirePermissionsSimpleStateChecker.cs

@ -0,0 +1,34 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Authorization.Permissions
{
public class RequirePermissionsSimpleStateChecker<TState> : ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
private readonly RequirePermissionsSimpleBatchStateCheckerModel<TState> _model;
public RequirePermissionsSimpleStateChecker(RequirePermissionsSimpleBatchStateCheckerModel<TState> model)
{
_model = model;
}
public async Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context)
{
var permissionChecker = context.ServiceProvider.GetRequiredService<IPermissionChecker>();
if (_model.Permissions.Length == 1)
{
return await permissionChecker.IsGrantedAsync(_model.Permissions.First());
}
var grantResult = await permissionChecker.IsGrantedAsync(_model.Permissions);
return _model.RequiresAll
? grantResult.AllGranted
: grantResult.Result.Any(x => _model.Permissions.Contains(x.Key) && x.Value == PermissionGrantResult.Granted);
}
}
}

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)
{ {

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/AbpCliCoreModule.cs

@ -36,6 +36,7 @@ namespace Volo.Abp.Cli
options.Commands["add-module"] = typeof(AddModuleCommand); options.Commands["add-module"] = typeof(AddModuleCommand);
options.Commands["list-modules"] = typeof(ListModulesCommand); options.Commands["list-modules"] = typeof(ListModulesCommand);
options.Commands["login"] = typeof(LoginCommand); options.Commands["login"] = typeof(LoginCommand);
options.Commands["login-info"] = typeof(LoginInfoCommand);
options.Commands["logout"] = typeof(LogoutCommand); options.Commands["logout"] = typeof(LogoutCommand);
options.Commands[GenerateProxyCommand.Name] = typeof(GenerateProxyCommand); options.Commands[GenerateProxyCommand.Name] = typeof(GenerateProxyCommand);
options.Commands[RemoveProxyCommand.Name] = typeof(RemoveProxyCommand); options.Commands[RemoveProxyCommand.Name] = typeof(RemoveProxyCommand);

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);
} }
} }

43
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/AuthService.cs

@ -9,8 +9,10 @@ using IdentityModel;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.Cli.Commands; using Volo.Abp.Cli.Commands;
using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.IdentityModel; using Volo.Abp.IdentityModel;
using Volo.Abp.Json;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.Cli.Auth namespace Volo.Abp.Cli.Auth
@ -18,21 +20,54 @@ namespace Volo.Abp.Cli.Auth
public class AuthService : ITransientDependency public class AuthService : ITransientDependency
{ {
protected IIdentityModelAuthenticationService AuthenticationService { get; } protected IIdentityModelAuthenticationService AuthenticationService { get; }
protected ILogger<NewCommand> Logger { get; } protected ILogger<AuthService> Logger { get; }
protected CliHttpClientFactory CliHttpClientFactory { get; } protected CliHttpClientFactory CliHttpClientFactory { get; }
public RemoteServiceExceptionHandler RemoteServiceExceptionHandler { get; }
public IJsonSerializer JsonSerializer { get; }
public ICancellationTokenProvider CancellationTokenProvider { get; } public ICancellationTokenProvider CancellationTokenProvider { get; }
public AuthService( public AuthService(
IIdentityModelAuthenticationService authenticationService, IIdentityModelAuthenticationService authenticationService,
ILogger<NewCommand> logger, ILogger<AuthService> logger,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
CliHttpClientFactory cliHttpClientFactory CliHttpClientFactory cliHttpClientFactory,
RemoteServiceExceptionHandler remoteServiceExceptionHandler,
IJsonSerializer jsonSerializer
) )
{ {
AuthenticationService = authenticationService; AuthenticationService = authenticationService;
Logger = logger; Logger = logger;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
CliHttpClientFactory = cliHttpClientFactory; CliHttpClientFactory = cliHttpClientFactory;
RemoteServiceExceptionHandler = remoteServiceExceptionHandler;
JsonSerializer = jsonSerializer;
}
public async Task<LoginInfo> GetLoginInfoAsync()
{
if (!IsLoggedIn())
{
return null;
}
var url = $"{CliUrls.WwwAbpIo}api/license/login-info";
var client = CliHttpClientFactory.CreateClient();
using (var response = await client.GetHttpResponseMessageWithRetryAsync(url, CancellationTokenProvider.Token, Logger))
{
if (!response.IsSuccessStatusCode)
{
Logger.LogError("Remote server returns '{response.StatusCode}'");
return null;
}
await RemoteServiceExceptionHandler.EnsureSuccessfulHttpResponseAsync(response);
var responseContent = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<LoginInfo>(responseContent);
}
} }
public async Task LoginAsync(string userName, string password, string organizationName = null) public async Task LoginAsync(string userName, string password, string organizationName = null)
@ -108,4 +143,4 @@ namespace Volo.Abp.Cli.Auth
return File.Exists(CliPaths.AccessToken); return File.Exists(CliPaths.AccessToken);
} }
} }
} }

16
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Auth/LoginInfo.cs

@ -0,0 +1,16 @@

namespace Volo.Abp.Cli.Auth
{
public class LoginInfo
{
public string Name { get; set; }
public string Surname { get; set; }
public string Username { get; set; }
public string EmailAddress { get; set; }
public string Organization { get; set; }
}
}

73
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;
@ -41,33 +43,76 @@ namespace Volo.Abp.Cli
{ {
Logger.LogInformation("ABP CLI (https://abp.io)"); Logger.LogInformation("ABP CLI (https://abp.io)");
var commandLineArgs = CommandLineArgumentParser.Parse(args);
#if !DEBUG #if !DEBUG
if (!commandLineArgs.Options.ContainsKey("skip-cli-version-check")) if (!commandLineArgs.Options.ContainsKey("skip-cli-version-check"))
{ {
await CheckCliVersionAsync(); await CheckCliVersionAsync();
} }
#endif #endif
var commandLineArgs = CommandLineArgumentParser.Parse(args);
var commandType = CommandSelector.Select(commandLineArgs);
try
using (var scope = ServiceScopeFactory.CreateScope())
{ {
var command = (IConsoleCommand)scope.ServiceProvider.GetRequiredService(commandType); if (commandLineArgs.IsCommand("batch"))
{
try await RunBatchAsync(commandLineArgs);
}
else
{ {
await command.ExecuteAsync(commandLineArgs); await RunInternalAsync(commandLineArgs);
} }
catch (CliUsageException usageException) }
catch (CliUsageException usageException)
{
Logger.LogWarning(usageException.Message);
}
catch (Exception ex)
{
Logger.LogException(ex);
}
}
private async Task RunBatchAsync(CommandLineArgs commandLineArgs)
{
var targetFile = commandLineArgs.Target;
if (targetFile.IsNullOrWhiteSpace())
{
throw new CliUsageException(
"Must provide a file name/path that contains a list of commands" +
Environment.NewLine + Environment.NewLine +
"Example: " +
" abp batch commands.txt"
);
}
var filePath = Path.Combine(Directory.GetCurrentDirectory(), targetFile);
var fileLines = File.ReadAllLines(filePath);
foreach (var line in fileLines)
{
var lineText = line;
if (lineText.IsNullOrWhiteSpace() || lineText.StartsWith("#"))
{ {
Logger.LogWarning(usageException.Message); continue;
} }
catch (Exception ex)
if (lineText.Contains('#'))
{ {
Logger.LogException(ex); lineText = lineText.Substring(0, lineText.IndexOf('#'));
} }
var args = CommandLineArgumentParser.Parse(lineText);
await RunInternalAsync(args);
}
}
private async Task RunInternalAsync(CommandLineArgs commandLineArgs)
{
var commandType = CommandSelector.Select(commandLineArgs);
using (var scope = ServiceScopeFactory.CreateScope())
{
var command = (IConsoleCommand) scope.ServiceProvider.GetRequiredService(commandType);
await command.ExecuteAsync(commandLineArgs);
} }
} }

63
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/LoginInfoCommand..cs

@ -0,0 +1,63 @@
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.Auth;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Cli.Commands
{
public class LoginInfoCommand : IConsoleCommand, ITransientDependency
{
public ILogger<LoginInfoCommand> Logger { get; set; }
protected AuthService AuthService { get; }
public LoginInfoCommand(AuthService authService)
{
AuthService = authService;
Logger = NullLogger<LoginInfoCommand>.Instance;
}
public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
var loginInfo = await AuthService.GetLoginInfoAsync();
if (loginInfo == null)
{
Logger.LogError("Unable to get login info.");
return;
}
var sb = new StringBuilder();
sb.AppendLine("");
sb.AppendLine($"Login info:");
sb.AppendLine($"Name: {loginInfo.Name}");
sb.AppendLine($"Surname: {loginInfo.Surname}");
sb.AppendLine($"Username: {loginInfo.Username}");
sb.AppendLine($"Email Address: {loginInfo.EmailAddress}");
sb.AppendLine($"Organization: {loginInfo.Organization}");
Logger.LogInformation(sb.ToString());
}
public string GetUsageInfo()
{
var sb = new StringBuilder();
sb.AppendLine("");
sb.AppendLine("Usage:");
sb.AppendLine(" abp login-info");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
return sb.ToString();
}
public string GetShortDescription()
{
return "Show your login info.";
}
}
}

12
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -301,13 +301,21 @@ namespace Volo.Abp.Cli.ProjectModification
private async Task RemoveProjectFromSolutionAsync(string moduleSolutionFile, string projectName) private async Task RemoveProjectFromSolutionAsync(string moduleSolutionFile, string projectName)
{ {
await SolutionFileModifier.RemoveProjectFromSolutionFileAsync(moduleSolutionFile, projectName); await SolutionFileModifier.RemoveProjectFromSolutionFileAsync(moduleSolutionFile, projectName);
var projectFolderPath = Path.Combine(Path.GetDirectoryName(moduleSolutionFile), "src", projectName); var projectFolderPath = Path.Combine(Path.GetDirectoryName(moduleSolutionFile), "src", projectName);
if (Directory.Exists(projectFolderPath))
{
Directory.Delete(projectFolderPath, true);
}
else
{
projectFolderPath = Path.Combine(Path.GetDirectoryName(moduleSolutionFile), "test", projectName);
if (Directory.Exists(projectFolderPath)) if (Directory.Exists(projectFolderPath))
{ {
Directory.Delete(projectFolderPath, true); Directory.Delete(projectFolderPath, true);
} }
}
} }
private void ChangeDomainTestReferenceToMongoDB(ModuleWithMastersInfo module, string moduleSolutionFile) private void ChangeDomainTestReferenceToMongoDB(ModuleWithMastersInfo module, string moduleSolutionFile)

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; }
} }
} }

3
framework/src/Volo.Abp.Core/Volo/Abp/Internal/InternalServiceCollectionExtensions.cs

@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Logging; using Volo.Abp.Logging;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Reflection; using Volo.Abp.Reflection;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Internal namespace Volo.Abp.Internal
{ {
@ -40,6 +41,8 @@ namespace Volo.Abp.Internal
services.AddAssemblyOf<IAbpApplication>(); services.AddAssemblyOf<IAbpApplication>();
services.AddTransient(typeof(ISimpleStateCheckerManager<>), typeof(SimpleStateCheckerManager<>));
services.Configure<AbpModuleLifecycleOptions>(options => services.Configure<AbpModuleLifecycleOptions>(options =>
{ {
options.Contributors.Add<OnPreApplicationInitializationModuleLifecycleContributor>(); options.Contributors.Add<OnPreApplicationInitializationModuleLifecycleContributor>();

15
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/AbpSimpleStateCheckerOptions.cs

@ -0,0 +1,15 @@
using Volo.Abp.Collections;
namespace Volo.Abp.SimpleStateChecking
{
public class AbpSimpleStateCheckerOptions<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public ITypeList<ISimpleStateChecker<TState>> GlobalStateCheckers { get; }
public AbpSimpleStateCheckerOptions()
{
GlobalStateCheckers = new TypeList<ISimpleStateChecker<TState>>();
}
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/IHasSimpleStateCheckers.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Volo.Abp.SimpleStateChecking
{
public interface IHasSimpleStateCheckers<TState>
where TState : IHasSimpleStateCheckers<TState>
{
List<ISimpleStateChecker<TState>> StateCheckers { get; }
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleBatchStateChecker.cs

@ -0,0 +1,10 @@
using System.Threading.Tasks;
namespace Volo.Abp.SimpleStateChecking
{
public interface ISimpleBatchStateChecker<TState> : ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
Task<SimpleStateCheckerResult<TState>> IsEnabledAsync(SimpleBatchStateCheckerContext<TState> context);
}
}

10
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs

@ -0,0 +1,10 @@
using System.Threading.Tasks;
namespace Volo.Abp.SimpleStateChecking
{
public interface ISimpleStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context);
}
}

12
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerManager.cs

@ -0,0 +1,12 @@
using System.Threading.Tasks;
namespace Volo.Abp.SimpleStateChecking
{
public interface ISimpleStateCheckerManager<TState>
where TState : IHasSimpleStateCheckers<TState>
{
Task<bool> IsEnabledAsync(TState state);
Task<SimpleStateCheckerResult<TState>> IsEnabledAsync(TState[] states);
}
}

16
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleBatchStateCheckerBase.cs

@ -0,0 +1,16 @@
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Abp.SimpleStateChecking
{
public abstract class SimpleBatchStateCheckerBase<TState>: ISimpleBatchStateChecker<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public async Task<bool> IsEnabledAsync(SimpleStateCheckerContext<TState> context)
{
return (await IsEnabledAsync(new SimpleBatchStateCheckerContext<TState>(context.ServiceProvider, new[] {context.State}))).Values.All(x => x);
}
public abstract Task<SimpleStateCheckerResult<TState>> IsEnabledAsync(SimpleBatchStateCheckerContext<TState> context);
}
}

18
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleBatchStateCheckerContext.cs

@ -0,0 +1,18 @@
using System;
namespace Volo.Abp.SimpleStateChecking
{
public class SimpleBatchStateCheckerContext<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public IServiceProvider ServiceProvider { get; }
public TState[] States { get; }
public SimpleBatchStateCheckerContext(IServiceProvider serviceProvider, TState[] states)
{
ServiceProvider = serviceProvider;
States = states;
}
}
}

18
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerContext.cs

@ -0,0 +1,18 @@
using System;
namespace Volo.Abp.SimpleStateChecking
{
public class SimpleStateCheckerContext<TState>
where TState : IHasSimpleStateCheckers<TState>
{
public IServiceProvider ServiceProvider { get; }
public TState State { get; }
public SimpleStateCheckerContext(IServiceProvider serviceProvider, TState state)
{
ServiceProvider = serviceProvider;
State = state;
}
}
}

112
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerManager.cs

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.SimpleStateChecking
{
public class SimpleStateCheckerManager<TState> : ISimpleStateCheckerManager<TState>
where TState : IHasSimpleStateCheckers<TState>
{
protected IServiceProvider ServiceProvider { get; }
protected AbpSimpleStateCheckerOptions<TState> Options { get; }
public SimpleStateCheckerManager(IServiceProvider serviceProvider, IOptions<AbpSimpleStateCheckerOptions<TState>> options)
{
ServiceProvider = serviceProvider;
Options = options.Value;
}
public virtual async Task<bool> IsEnabledAsync(TState state)
{
return await InternalIsEnabledAsync(state, true);
}
public virtual async Task<SimpleStateCheckerResult<TState>> IsEnabledAsync(TState[] states)
{
var result = new SimpleStateCheckerResult<TState>(states);
using (var scope = ServiceProvider.CreateScope())
{
var batchStateCheckers = states.SelectMany(x => x.StateCheckers)
.Where(x => x is ISimpleBatchStateChecker<TState>)
.Cast<ISimpleBatchStateChecker<TState>>()
.GroupBy(x => x)
.Select(x => x.Key);
foreach (var stateChecker in batchStateCheckers)
{
var context = new SimpleBatchStateCheckerContext<TState>(
scope.ServiceProvider.GetRequiredService<ICachedServiceProvider>(),
states.Where(x => x.StateCheckers.Contains(stateChecker)).ToArray());
foreach (var x in await stateChecker.IsEnabledAsync(context))
{
result[x.Key] = x.Value;
}
if (result.Values.All(x => !x))
{
return result;
}
}
foreach (ISimpleBatchStateChecker<TState> globalStateChecker in Options.GlobalStateCheckers
.Where(x => typeof(ISimpleBatchStateChecker<TState>).IsAssignableFrom(x))
.Select(x => ServiceProvider.GetRequiredService(x)))
{
var context = new SimpleBatchStateCheckerContext<TState>(
scope.ServiceProvider.GetRequiredService<ICachedServiceProvider>(),
states.Where(x => result.Any(y => y.Key.Equals(x) && y.Value)).ToArray());
foreach (var x in await globalStateChecker.IsEnabledAsync(context))
{
result[x.Key] = x.Value;
}
}
foreach (var state in states)
{
if (result[state])
{
result[state] = await InternalIsEnabledAsync(state, false);
}
}
return result;
}
}
protected virtual async Task<bool> InternalIsEnabledAsync(TState state, bool useBatchChecker)
{
using (var scope = ServiceProvider.CreateScope())
{
var context = new SimpleStateCheckerContext<TState>(scope.ServiceProvider.GetRequiredService<ICachedServiceProvider>(), state);
foreach (var provider in state.StateCheckers.WhereIf(!useBatchChecker, x => x is not ISimpleBatchStateChecker<TState>))
{
if (!await provider.IsEnabledAsync(context))
{
return false;
}
}
foreach (ISimpleStateChecker<TState> provider in Options.GlobalStateCheckers
.WhereIf(!useBatchChecker, x => !typeof(ISimpleBatchStateChecker<TState>).IsAssignableFrom(x))
.Select(x => ServiceProvider.GetRequiredService(x)))
{
if (!await provider.IsEnabledAsync(context))
{
return false;
}
}
return true;
}
}
}
}

21
framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerResult.cs

@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace Volo.Abp.SimpleStateChecking
{
public class SimpleStateCheckerResult<TState> : Dictionary<TState, bool>
where TState : IHasSimpleStateCheckers<TState>
{
public SimpleStateCheckerResult()
{
}
public SimpleStateCheckerResult(IEnumerable<TState> states, bool initValue = true)
{
foreach (var state in states)
{
Add(state, initValue);
}
}
}
}

17
framework/src/Volo.Abp.Data/Volo/Abp/Data/IConnectionStringResolverExtensions.cs

@ -10,13 +10,26 @@ namespace Volo.Abp.Data
[Obsolete("Use ResolveAsync method")] [Obsolete("Use ResolveAsync method")]
public static string Resolve<T>(this IConnectionStringResolver resolver) public static string Resolve<T>(this IConnectionStringResolver resolver)
{ {
return resolver.Resolve(ConnectionStringNameAttribute.GetConnStringName<T>()); return resolver.Resolve(typeof(T));
} }
[NotNull] [NotNull]
public static Task<string> ResolveAsync<T>(this IConnectionStringResolver resolver) public static Task<string> ResolveAsync<T>(this IConnectionStringResolver resolver)
{ {
return resolver.ResolveAsync(ConnectionStringNameAttribute.GetConnStringName<T>()); return resolver.ResolveAsync(typeof(T));
}
[NotNull]
[Obsolete("Use ResolveAsync method")]
public static string Resolve(this IConnectionStringResolver resolver, Type type)
{
return resolver.Resolve(ConnectionStringNameAttribute.GetConnStringName(type));
}
[NotNull]
public static Task<string> ResolveAsync(this IConnectionStringResolver resolver, Type type)
{
return resolver.ResolveAsync(ConnectionStringNameAttribute.GetConnStringName(type));
} }
} }
} }

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)
{ {

9
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Uow/EntityFrameworkCore/UnitOfWorkDbContextProvider.cs

@ -62,10 +62,9 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
throw new AbpException("A DbContext can only be created inside a unit of work!"); throw new AbpException("A DbContext can only be created inside a unit of work!");
} }
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>();
var connectionString = ResolveConnectionString(connectionStringName);
var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TDbContext)); var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TDbContext));
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName(targetDbContextType);
var connectionString = ResolveConnectionString(connectionStringName);
var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}"; var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}";
var databaseApi = unitOfWork.GetOrAddDatabaseApi( var databaseApi = unitOfWork.GetOrAddDatabaseApi(
@ -85,10 +84,10 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
throw new AbpException("A DbContext can only be created inside a unit of work!"); throw new AbpException("A DbContext can only be created inside a unit of work!");
} }
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName<TDbContext>(); var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TDbContext));
var connectionStringName = ConnectionStringNameAttribute.GetConnStringName(targetDbContextType);
var connectionString = await ResolveConnectionStringAsync(connectionStringName); var connectionString = await ResolveConnectionStringAsync(connectionStringName);
var targetDbContextType = _options.GetReplacedTypeOrSelf(typeof(TDbContext));
var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}"; var dbContextKey = $"{targetDbContextType.FullName}_{connectionString}";
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey); var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);

30
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureSimpleStateCheckerExtensions.cs

@ -0,0 +1,30 @@
using JetBrains.Annotations;
using Volo.Abp.SimpleStateChecking;
namespace Volo.Abp.Features
{
public static class FeatureSimpleStateCheckerExtensions
{
public static TState RequireFeatures<TState>(
[NotNull] this TState state,
params string[] features)
where TState : IHasSimpleStateCheckers<TState>
{
state.RequireFeatures(true, features);
return state;
}
public static TState RequireFeatures<TState>(
[NotNull] this TState state,
bool requiresAll,
params string[] features)
where TState : IHasSimpleStateCheckers<TState>
{
Check.NotNull(state, nameof(state));
Check.NotNullOrEmpty(features, nameof(features));
state.StateCheckers.Add(new RequireFeaturesSimpleStateChecker<TState>(requiresAll, features));
return state;
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save