Browse Source

Merge pull request #2 from abpframework/master

pull/1162/head
Nokecy 7 years ago
committed by GitHub
parent
commit
34ffc5c9ee
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj
  2. 2
      common.props
  3. 54
      docs/en/Blog-Posts/2019-02-22/Post.md
  4. BIN
      docs/en/Blog-Posts/2019-02-22/scott-and-jon.png
  5. 20
      docs/en/Samples/Microservice-Demo.md
  6. BIN
      docs/en/images/microservice-sample-diagram-2.png
  7. 18
      framework/Volo.Abp.sln
  8. 24
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs
  9. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs
  10. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs
  11. 9
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs
  12. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
  13. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs
  14. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs
  15. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs
  16. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Bundling/BasicThemeGlobalScriptContributor.cs
  17. 47
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml
  18. 36
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml
  19. 24
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/layout.css
  20. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/layout.js
  21. 5
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs
  22. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs
  23. 30
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  24. 42
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs
  25. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
  26. 23
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs
  27. 26
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs
  28. 7
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/IAuthorizationEnabled.cs
  29. 25
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs
  30. 9
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/AlwaysAllowPermissionChecker.cs
  31. 13
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs
  32. 4
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionChecker.cs
  33. 6
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionDefinitionProvider.cs
  34. 6
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs
  35. 3
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullPermissionStore.cs
  36. 19
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionChecker.cs
  37. 20
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionCheckerExtensions.cs
  38. 10
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs
  39. 39
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs
  40. 6
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionProvider.cs
  41. 9
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionGrantResult.cs
  42. 8
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs
  43. 4
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueCheckContext.cs
  44. 5
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProvider.cs
  45. 8
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs
  46. 13
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/UserPermissionValueProvider.cs
  47. 1
      framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs
  48. 12
      framework/src/Volo.Abp.Core/Volo/Abp/NameValue.cs
  49. 16
      framework/src/Volo.Abp.Core/Volo/Abp/Threading/TaskCache.cs
  50. 7
      framework/src/Volo.Abp.Data/Volo/Abp/Data/HasExtraPropertiesExtensions.cs
  51. 1
      framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj
  52. 7
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs
  53. 4
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs
  54. 9
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs
  55. 5
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs
  56. 4
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj
  57. 8
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs
  58. 6
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsMySQLExtensions.cs
  59. 22
      framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj
  60. 51
      framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs
  61. 22
      framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs
  62. 10
      framework/src/Volo.Abp.Features/Volo/Abp/Features/DisableFeatureCheckAttribute.cs
  63. 68
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs
  64. 32
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerBase.cs
  65. 143
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs
  66. 178
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs
  67. 51
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionContext.cs
  68. 117
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs
  69. 9
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionProvider.cs
  70. 111
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs
  71. 56
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptor.cs
  72. 36
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs
  73. 17
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureOptions.cs
  74. 19
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValue.cs
  75. 19
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProvider.cs
  76. 12
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureChecker.cs
  77. 14
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionContext.cs
  78. 15
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs
  79. 7
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionProvider.cs
  80. 14
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureStore.cs
  81. 12
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureValueProvider.cs
  82. 11
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IMethodInvocationFeatureCheckerService.cs
  83. 14
      framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerContext.cs
  84. 59
      framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerService.cs
  85. 23
      framework/src/Volo.Abp.Features/Volo/Abp/Features/NullFeatureStore.cs
  86. 33
      framework/src/Volo.Abp.Features/Volo/Abp/Features/RequiresFeatureAttribute.cs
  87. 25
      framework/src/Volo.Abp.Features/Volo/Abp/Features/TenantFeatureValueProvider.cs
  88. 5
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs
  89. 1
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo.Abp.MultiTenancy.Abstractions.csproj
  90. 4
      framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpMultiTenancyAbstractionsModule.cs
  91. 0
      framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs
  92. 29
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs
  93. 20
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs
  94. 2
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs
  95. 27
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs
  96. 2
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs
  97. 2
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs
  98. 14
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/AlwaysValidValueValidator.cs
  99. 24
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/BooleanValueValidator.cs
  100. 19
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/FreeTextStringValueType.cs

2
abp_io/src/Volo.AbpWebSite.Web/Volo.AbpWebSite.Web.csproj

@ -9,7 +9,7 @@
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<PreserveCompilationContext>true</PreserveCompilationContext> <PreserveCompilationContext>true</PreserveCompilationContext>
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish> <MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
<MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
<UserSecretsId>c140514f-e488-4c99-8b9a-fabee0f53ce0</UserSecretsId> <UserSecretsId>c140514f-e488-4c99-8b9a-fabee0f53ce0</UserSecretsId>
</PropertyGroup> </PropertyGroup>

2
common.props

@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Version>0.13.0</Version> <Version>0.14.0</Version>
<NoWarn>$(NoWarn);CS1591</NoWarn> <NoWarn>$(NoWarn);CS1591</NoWarn>
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl> <PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
<PackageProjectUrl>https://abp.io</PackageProjectUrl> <PackageProjectUrl>https://abp.io</PackageProjectUrl>

54
docs/en/Blog-Posts/2019-02-22/Post.md

@ -0,0 +1,54 @@
# Microservice Demo, Projects Status and Road Map
After [the first announcement](https://abp.io/blog/abp/Abp-vNext-Announcement) on the ABP vNext, we have a lot of improvements on the codebase (1100+ commits on the [GitHub repository](https://github.com/abpframework/abp)). We've created features, samples, documentation and much more. In this post, I want to inform you about some news and the status of the project.
## Microservice Demo Solution
One of the major goals of the ABP framework is to provide a [convenient infrastructure to create microservice solutions](https://abp.io/documents/abp/latest/Microservice-Architecture).
We've been working to develop a microservice solution demo. Initial version was completed and [documented](https://abp.io/documents/abp/latest/Samples/Microservice-Demo). This sample solution aims to demonstrate a simple yet complete microservice solution;
- Has multiple, independent, self-deployable **microservices**.
- Multiple **web applications**, each uses a different API gateway.
- Has multiple **gateways** / BFFs (Backend for Frontends) developed using the [Ocelot](https://github.com/ThreeMammals/Ocelot) library.
- Has an **authentication service** developed using the [IdentityServer](https://identityserver.io/) framework. It's also a SSO (Single Sign On) application with necessary UIs.
- Has **multiple databases**. Some microservices has their own database while some services/applications shares a database (to demonstrate different use cases).
- Has different types of databases: **SQL Server** (with **Entity Framework Core** ORM) and **MongoDB**.
- Has a **console application** to show the simplest way of using a service by authenticating.
- Uses [Redis](https://redis.io/) for **distributed caching**.
- Uses [RabbitMQ](https://www.rabbitmq.com/) for service-to-service **messaging**.
- Uses [Docker](https://www.docker.com/) & [Kubernates](https://kubernetes.io/) to **deploy** & run all services and applications.
- Uses [Elasticsearch](https://www.elastic.co/products/elasticsearch) & [Kibana](https://www.elastic.co/products/kibana) to store and visualize the logs (written using [Serilog](https://serilog.net/)).
See [its documentation](https://abp.io/documents/abp/latest/Samples/Microservice-Demo) for a detailed explanation of the solution.
## Improvements/Features
We've worked on so many features including **distributed event bus** (with RabbitMQ integration), **IdentityServer4 integration** and enhancements for almost all features. We are continuously refactoring and adding tests to make the framework more stable and production ready. It is [rapidly growing](https://github.com/abpframework/abp/graphs/contributors).
## Road Map
There are still too much work to be done before the first stable release (v1.0). You can see [prioritized backlog items](https://github.com/abpframework/abp/issues?q=is%3Aopen+is%3Aissue+milestone%3ABacklog) on the GitHub repo.
According to our estimation, we have planned to release v1.0 in Q2 of 2019 (probably in May or June). So, not too much time to wait. We are also very excited for the first stable release.
We will also work on [the documentation](https://abp.io/documents/abp/latest) since it is far from complete now.
First release may not include a SPA template. However, we want to prepare a simple one if it can be possible. Haven't decided yet about the SPA framework. Alternatives: **Angular, React and Blazor**. Please write your thought as a comment to this post.
## Chinese Web Site
There is a big ABP community in China. They have created a Chinese version of the abp.io web site: https://cn.abp.io/ They are keeping it up to date. Thanks to the Chinese developers and especially to [Liming Ma](https://github.com/maliming).
## NDC {London} 2019
It was a pleasure to be in [NDC {London}](https://ndc-london.com/) 2019 as a partner. We've talked to many developers about the current ASP.NET Boilerplate and the ABP vNext and we got good feedbacks.
We also had a chance to talk with [Scott Hanselman](https://twitter.com/shanselman) and [Jon Galloway](https://twitter.com/jongalloway). They visited our booth and we talked about the ideas for ABP vNext. They liked features, approaches and the goal of new ABP framework. See some photos and comments on twitter:
![scott-and-jon](scott-and-jon.png)
## Follow It
* You can star and follow the **GitHub** repository: https://github.com/abpframework/abp
* You can follow the official **Twitter** account for news: https://twitter.com/abpframework

BIN
docs/en/Blog-Posts/2019-02-22/scott-and-jon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 KiB

20
docs/en/Samples/Microservice-Demo.md

@ -24,7 +24,7 @@ This sample aims to demonstrate a simple yet complete microservice solution;
The diagram below shows the system: The diagram below shows the system:
![microservice-sample-diagram](../images/microservice-sample-diagram.png) ![microservice-sample-diagram-2](../images/microservice-sample-diagram-2.png)
### Source Code ### Source Code
@ -32,7 +32,7 @@ You can get the source code from [the GitHub repository](https://github.com/abpf
### Status ### Status
This sample is still in development, not completed yet. Initial version of this sample has been completed. Additional improvement are still in development.
## Running the Solution ## Running the Solution
@ -50,6 +50,20 @@ Running as docker containers is easier since all dependencies are pre-configured
- Open a command line in the `samples/MicroserviceDemo` folder of the repository. - Open a command line in the `samples/MicroserviceDemo` folder of the repository.
- Pull images from Docker Hub:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml pull
```
- If you want to build images locally you may skip the above step and instead use build command:
```
docker-compose -f docker-compose.yml -f docker-compose.migrations.yml build
```
Building images may take a **long time** depending on your machine.
- Restore SQL Server databases: - Restore SQL Server databases:
``` ```
@ -62,8 +76,6 @@ Running as docker containers is easier since all dependencies are pre-configured
docker-compose up -d docker-compose up -d
``` ```
At the first run, it will take a **long time** because it will build all docker images.
- Add this line to the end of your `hosts` file: - Add this line to the end of your `hosts` file:
``` ```

BIN
docs/en/images/microservice-sample-diagram-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

18
framework/Volo.Abp.sln

@ -222,9 +222,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http.Client.Identi
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.IdentityModel", "src\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj", "{64D99E19-EE25-465A-82E5-17B25F4C4E18}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.IdentityModel", "src\Volo.Abp.IdentityModel\Volo.Abp.IdentityModel.csproj", "{64D99E19-EE25-465A-82E5-17B25F4C4E18}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Client", "src\Volo.Abp.AspNetCore.Mvc.Client\Volo.Abp.AspNetCore.Mvc.Client.csproj", "{E803DDB8-81EA-454B-9A66-9C2941100B67}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.Client", "src\Volo.Abp.AspNetCore.Mvc.Client\Volo.Abp.AspNetCore.Mvc.Client.csproj", "{E803DDB8-81EA-454B-9A66-9C2941100B67}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Contracts", "src\Volo.Abp.AspNetCore.Mvc.Contracts\Volo.Abp.AspNetCore.Mvc.Contracts.csproj", "{88F6D091-CA16-4B71-9499-8D5B8FA2E712}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.Contracts", "src\Volo.Abp.AspNetCore.Mvc.Contracts\Volo.Abp.AspNetCore.Mvc.Contracts.csproj", "{88F6D091-CA16-4B71-9499-8D5B8FA2E712}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Features", "src\Volo.Abp.Features\Volo.Abp.Features.csproj", "{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Features.Tests", "test\Volo.Abp.Features.Tests\Volo.Abp.Features.Tests.csproj", "{575BEFA1-19C2-49B1-8D31-B5D4472328DE}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -640,6 +644,14 @@ Global
{88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Debug|Any CPU.Build.0 = Debug|Any CPU {88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Release|Any CPU.ActiveCfg = Release|Any CPU {88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Release|Any CPU.Build.0 = Release|Any CPU {88F6D091-CA16-4B71-9499-8D5B8FA2E712}.Release|Any CPU.Build.0 = Release|Any CPU
{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}.Release|Any CPU.Build.0 = Release|Any CPU
{575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{575BEFA1-19C2-49B1-8D31-B5D4472328DE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -749,6 +761,8 @@ Global
{64D99E19-EE25-465A-82E5-17B25F4C4E18} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {64D99E19-EE25-465A-82E5-17B25F4C4E18} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{E803DDB8-81EA-454B-9A66-9C2941100B67} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {E803DDB8-81EA-454B-9A66-9C2941100B67} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{88F6D091-CA16-4B71-9499-8D5B8FA2E712} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {88F6D091-CA16-4B71-9499-8D5B8FA2E712} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{575BEFA1-19C2-49B1-8D31-B5D4472328DE} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

24
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Features;
namespace Volo.Abp.AspNetCore.Mvc.Client
{
public class RemoteFeatureChecker : FeatureCheckerBase
{
protected ICachedApplicationConfigurationClient ConfigurationClient { get; }
public RemoteFeatureChecker(ICachedApplicationConfigurationClient configurationClient)
{
ConfigurationClient = configurationClient;
}
public override async Task<string> GetOrNullAsync(string name)
{
var configuration = await ConfigurationClient.GetAsync();
return configuration.Features.Values.GetOrDefault(name);
}
}
}

12
framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemotePermissionChecker.cs

@ -14,19 +14,17 @@ namespace Volo.Abp.AspNetCore.Mvc.Client
ConfigurationClient = configurationClient; ConfigurationClient = configurationClient;
} }
public async Task<PermissionGrantInfo> CheckAsync(string name) public async Task<bool> IsGrantedAsync(string name)
{ {
var configuration = await ConfigurationClient.GetAsync(); var configuration = await ConfigurationClient.GetAsync();
return new PermissionGrantInfo( return configuration.Auth.GrantedPolicies.ContainsKey(name);
name,
configuration.Auth.GrantedPolicies.ContainsKey(name)
);
} }
public Task<PermissionGrantInfo> CheckAsync(ClaimsPrincipal claimsPrincipal, string name) public Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name)
{ {
return CheckAsync(name); /* This provider always works for the current principal. */
return IsGrantedAsync(name);
} }
} }
} }

2
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs

@ -12,5 +12,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
public ApplicationSettingConfigurationDto Setting { get; set; } public ApplicationSettingConfigurationDto Setting { get; set; }
public CurrentUserDto CurrentUser { get; set; } public CurrentUserDto CurrentUser { get; set; }
public ApplicationFeatureConfigurationDto Features { get; set; }
} }
} }

9
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
{
public class ApplicationFeatureConfigurationDto
{
public Dictionary<string, string> Values { get; set; }
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs

@ -23,7 +23,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default; public AbpFormControlSize Size { get; set; } = AbpFormControlSize.Default;
[HtmlAttributeNotBound] [HtmlAttributeName("required-symbol")]
public bool DisplayRequiredSymbol { get; set; } = true; public bool DisplayRequiredSymbol { get; set; } = true;
[HtmlAttributeNotBound] [HtmlAttributeNotBound]

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelper.cs

@ -18,7 +18,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
[HtmlAttributeName("info")] [HtmlAttributeName("info")]
public string InfoText { get; set; } public string InfoText { get; set; }
[HtmlAttributeNotBound] [HtmlAttributeName("required-symbol")]
public bool DisplayRequiredSymbol { get; set; } = true; public bool DisplayRequiredSymbol { get; set; } = true;
[HtmlAttributeNotBound] [HtmlAttributeNotBound]

13
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/FlagIconCss/FlagIconCssStyleContributor.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.FlagIconCss
{
public class FlagIconCssStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/flag-icon-css/css/flag-icon.min.css");
}
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/AbpAspNetCoreMvcUIBasicThemeModule.cs

@ -52,7 +52,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic
.ScriptBundles .ScriptBundles
.Add(BasicThemeBundles.Scripts.Global, bundle => .Add(BasicThemeBundles.Scripts.Global, bundle =>
{ {
bundle.AddBaseBundles(StandardBundles.Scripts.Global); bundle
.AddBaseBundles(StandardBundles.Scripts.Global)
.AddContributors(typeof(BasicThemeGlobalScriptContributor));
}); });
}); });
} }

12
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Bundling/BasicThemeGlobalScriptContributor.cs

@ -0,0 +1,12 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Bundling
{
public class BasicThemeGlobalScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.Add("/themes/basic/layout.js");
}
}
}

47
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/Default.cshtml

@ -5,39 +5,36 @@
var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\""; var elementId = string.IsNullOrEmpty(menuItem.ElementId) ? string.Empty : $"id=\"{menuItem.ElementId}\"";
var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass; var cssClass = string.IsNullOrEmpty(menuItem.CssClass) ? string.Empty : menuItem.CssClass;
var disabled = menuItem.IsDisabled ? "disabled" : string.Empty; var disabled = menuItem.IsDisabled ? "disabled" : string.Empty;
if (menuItem.IsLeaf) if (menuItem.IsLeaf)
{ {
if (menuItem.Url == null) @if (menuItem.Url != null)
{ {
continue; <li class="nav-item @cssClass @disabled" @elementId>
} <a class="nav-link" href="@(menuItem.Url ?? "#")">
@if (menuItem.Icon != null)
<li class="nav-item @cssClass @disabled" @elementId>
<a class="nav-link" href="@(menuItem.Url ?? "#")">
@if (menuItem.Icon != null)
{
if (menuItem.Icon.StartsWith("fa"))
{ {
<i class="@menuItem.Icon"></i> if (menuItem.Icon.StartsWith("fa"))
{
<i class="@menuItem.Icon"></i>
}
} }
} @menuItem.DisplayName
@menuItem.DisplayName </a>
</a> @*<span class="sr-only">(current)</span>*@ </li>
</li> }
} }
else else
{ {
<li class="nav-item dropdown"> <li class="nav-item">
<a class="nav-link dropdown-toggle" href="#" id="Menu_@(menuItem.Name)" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@menuItem.DisplayName</a> <div class="dropdown">
<div class="dropdown-menu" aria-labelledby="Menu_@(menuItem.Name)"> <a class="nav-link dropdown-toggle" href="#" id="Menu_@(menuItem.Name)" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@menuItem.DisplayName</a>
@foreach (var childMenuItem in menuItem.Items) <div class="dropdown-menu" aria-labelledby="Menu_@(menuItem.Name)">
{ @foreach (var childMenuItem in menuItem.Items)
<a class="dropdown-item @cssClass @disabled" href="@(childMenuItem.Url ?? "#")" @Html.Raw(elementId)> {
@childMenuItem.DisplayName @await Html.PartialAsync("~/Themes/Basic/Components/Menu/_MenuItem.cshtml", childMenuItem)
</a> }
} </div>
</div> </div>
</li> </li>
} }
} }

36
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Menu/_MenuItem.cshtml

@ -0,0 +1,36 @@
@using Volo.Abp.UI.Navigation
@model ApplicationMenuItem
@{
var elementId = string.IsNullOrEmpty(Model.ElementId) ? string.Empty : $"id=\"{Model.ElementId}\"";
var cssClass = string.IsNullOrEmpty(Model.CssClass) ? string.Empty : Model.CssClass;
var disabled = Model.IsDisabled ? "disabled" : string.Empty;
}
@if (Model.IsLeaf)
{
@if (Model.Url != null)
{
<a class="dropdown-item @cssClass @disabled" href="@(Model.Url ?? "#")" @Html.Raw(elementId)>
@Model.DisplayName
</a>
}
}
else
{
<div class="dropdown-submenu">
<a role="button" class="btn dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<span class="lp-icon">
<i class="@(Model.Icon ?? "")"></i>
</span>
<span class="lp-text">
@Model.DisplayName
</span>
</a>
<div class="dropdown-menu">
@foreach (var childMenuItem in Model.Items)
{
@await Html.PartialAsync("~/Themes/Basic/Components/Menu/_MenuItem.cshtml", childMenuItem)
}
</div>
</div>
}

24
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/layout.css

@ -10,3 +10,27 @@ body {
text-decoration: none; text-decoration: none;
color: #fff; color: #fff;
} }
/* Main Menu */
.navbar .dropdown-submenu {
position: relative;
}
.navbar .dropdown-submenu a {
padding: 0.25rem 1.4rem;
}
.navbar .dropdown-submenu a::after {
transform: rotate(-90deg);
position: absolute;
right: 16px;
top: 18px;
}
.navbar .dropdown-submenu .dropdown-menu {
top: 0;
left: 100%;
margin-left: .1rem;
margin-right: .1rem;
}

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/wwwroot/themes/basic/layout.js

@ -0,0 +1,16 @@
$(function () {
$('.dropdown-menu a.dropdown-toggle').on('click', function (e) {
if (!$(this).next().hasClass('show')) {
$(this).parents('.dropdown-menu').first().find('.show').removeClass("show");
}
var $subMenu = $(this).next(".dropdown-menu");
$subMenu.toggleClass('show');
$(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function (e) {
$('.dropdown-submenu .show').removeClass("show");
});
return false;
});
});

5
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs

@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Aspects; using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Mvc.Validation; using Volo.Abp.AspNetCore.Mvc.Validation;
using Volo.Abp.Features;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping; using Volo.Abp.ObjectMapping;
@ -33,7 +34,9 @@ namespace Volo.Abp.AspNetCore.Mvc
public IClock Clock { get; set; } public IClock Clock { get; set; }
public IModelStateValidator ModelValidator { get; set; } public IModelStateValidator ModelValidator { get; set; }
public IFeatureChecker FeatureChecker { get; set; }
public List<string> AppliedCrossCuttingConcerns { get; } = new List<string>(); public List<string> AppliedCrossCuttingConcerns { get; } = new List<string>();
protected virtual void ValidateModel() protected virtual void ValidateModel()

2
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs

@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.Auditing; using Volo.Abp.AspNetCore.Mvc.Auditing;
using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling; using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.Features;
using Volo.Abp.AspNetCore.Mvc.Uow; using Volo.Abp.AspNetCore.Mvc.Uow;
using Volo.Abp.AspNetCore.Mvc.Validation; using Volo.Abp.AspNetCore.Mvc.Validation;
@ -26,6 +27,7 @@ namespace Volo.Abp.AspNetCore.Mvc
private static void AddFilters(MvcOptions options) private static void AddFilters(MvcOptions options)
{ {
options.Filters.AddService(typeof(AbpAuditActionFilter)); options.Filters.AddService(typeof(AbpAuditActionFilter));
options.Filters.AddService(typeof(AbpFeatureActionFilter));
options.Filters.AddService(typeof(AbpValidationActionFilter)); options.Filters.AddService(typeof(AbpValidationActionFilter));
options.Filters.AddService(typeof(AbpUowActionFilter)); options.Filters.AddService(typeof(AbpUowActionFilter));
options.Filters.AddService(typeof(AbpExceptionFilter)); options.Filters.AddService(typeof(AbpExceptionFilter));

30
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs

@ -4,10 +4,10 @@ using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.Features;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Settings; using Volo.Abp.Settings;
using Volo.Abp.Users; using Volo.Abp.Users;
@ -23,6 +23,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
private readonly ICurrentUser _currentUser; private readonly ICurrentUser _currentUser;
private readonly ISettingProvider _settingProvider; private readonly ISettingProvider _settingProvider;
private readonly ISettingDefinitionManager _settingDefinitionManager; private readonly ISettingDefinitionManager _settingDefinitionManager;
private readonly IFeatureDefinitionManager _featureDefinitionManager;
public AbpApplicationConfigurationAppService( public AbpApplicationConfigurationAppService(
IOptions<AbpLocalizationOptions> localizationOptions, IOptions<AbpLocalizationOptions> localizationOptions,
@ -31,7 +32,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
IAuthorizationService authorizationService, IAuthorizationService authorizationService,
ICurrentUser currentUser, ICurrentUser currentUser,
ISettingProvider settingProvider, ISettingProvider settingProvider,
SettingDefinitionManager settingDefinitionManager) SettingDefinitionManager settingDefinitionManager,
IFeatureDefinitionManager featureDefinitionManager)
{ {
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider; _abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider;
@ -39,16 +41,18 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
_currentUser = currentUser; _currentUser = currentUser;
_settingProvider = settingProvider; _settingProvider = settingProvider;
_settingDefinitionManager = settingDefinitionManager; _settingDefinitionManager = settingDefinitionManager;
_featureDefinitionManager = featureDefinitionManager;
_localizationOptions = localizationOptions.Value; _localizationOptions = localizationOptions.Value;
} }
public async Task<ApplicationConfigurationDto> GetAsync() public virtual async Task<ApplicationConfigurationDto> GetAsync()
{ {
//TODO: Optimize & cache..? //TODO: Optimize & cache..?
return new ApplicationConfigurationDto return new ApplicationConfigurationDto
{ {
Auth = await GetAuthConfigAsync(), Auth = await GetAuthConfigAsync(),
Features = await GetFeaturesConfigAsync(),
Localization = GetLocalizationConfig(), Localization = GetLocalizationConfig(),
CurrentUser = GetCurrentUser(), CurrentUser = GetCurrentUser(),
Setting = await GetSettingConfigAsync() Setting = await GetSettingConfigAsync()
@ -126,5 +130,25 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
return result; return result;
} }
protected virtual async Task<ApplicationFeatureConfigurationDto> GetFeaturesConfigAsync()
{
var result = new ApplicationFeatureConfigurationDto
{
Values = new Dictionary<string, string>()
};
foreach (var featureDefinition in _featureDefinitionManager.GetAll())
{
if (!featureDefinition.IsVisibleToClients)
{
continue;
}
result.Values[featureDefinition.Name] = await FeatureChecker.GetOrNullAsync(featureDefinition.Name);
}
return result;
}
} }
} }

42
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Volo.Abp.Aspects;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
namespace Volo.Abp.AspNetCore.Mvc.Features
{
public class AbpFeatureActionFilter : IAsyncActionFilter, ITransientDependency
{
private readonly IMethodInvocationFeatureCheckerService _methodInvocationAuthorizationService;
public AbpFeatureActionFilter(IMethodInvocationFeatureCheckerService methodInvocationAuthorizationService)
{
_methodInvocationAuthorizationService = methodInvocationAuthorizationService;
}
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
if (!context.ActionDescriptor.IsControllerAction())
{
await next();
return;
}
var methodInfo = context.ActionDescriptor.GetMethodInfo();
using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.FeatureChecking))
{
await _methodInvocationAuthorizationService.CheckAsync(
new MethodInvocationFeatureCheckerContext(methodInfo)
);
await next();
}
}
}
}

1
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs

@ -12,7 +12,6 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
{ {
public class AbpUowActionFilter : IAsyncActionFilter, ITransientDependency public class AbpUowActionFilter : IAsyncActionFilter, ITransientDependency
{ {
private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly UnitOfWorkDefaultOptions _defaultOptions; private readonly UnitOfWorkDefaultOptions _defaultOptions;

23
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs

@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Authorization; using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization; using Volo.Abp.Localization;
@ -16,6 +18,7 @@ namespace Volo.Abp.Authorization
public override void PreConfigureServices(ServiceConfigurationContext context) public override void PreConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded); context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded);
AutoAddDefinitionProviders(context.Services);
} }
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
@ -31,5 +34,23 @@ namespace Volo.Abp.Authorization
options.ValueProviders.Add<ClientPermissionValueProvider>(); options.ValueProviders.Add<ClientPermissionValueProvider>();
}); });
} }
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
{
if (typeof(IPermissionDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
services.Configure<PermissionOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
} }
} }

26
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs

@ -1,4 +1,8 @@
using Volo.Abp.DependencyInjection; using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization namespace Volo.Abp.Authorization
{ {
@ -6,10 +10,28 @@ namespace Volo.Abp.Authorization
{ {
public static void RegisterIfNeeded(IOnServiceRegistredContext context) public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{ {
if (typeof(IAuthorizationEnabled).IsAssignableFrom(context.ImplementationType)) if (ShouldIntercept(context.ImplementationType))
{ {
context.Interceptors.TryAdd<AuthorizationInterceptor>(); context.Interceptors.TryAdd<AuthorizationInterceptor>();
} }
} }
private static bool ShouldIntercept(Type type)
{
return type.IsDefined(typeof(AuthorizeAttribute), true) ||
AnyMethodHasAuthorizeAttribute(type);
}
private static bool AnyMethodHasAuthorizeAttribute(Type implementationType)
{
return implementationType
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Any(HasAuthorizeAttribute);
}
private static bool HasAuthorizeAttribute(MemberInfo methodInfo)
{
return methodInfo.IsDefined(typeof(AuthorizeAttribute), true);
}
} }
} }

7
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/IAuthorizationEnabled.cs

@ -1,7 +0,0 @@
namespace Volo.Abp.Authorization
{
public interface IAuthorizationEnabled
{
}
}

25
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs

@ -1,4 +1,6 @@
using System.Linq; using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Clients; using Volo.Abp.Clients;
@ -30,8 +32,7 @@ namespace Volo.Abp.Authorization
return; return;
} }
var authorizationAttributes = GetAuthorizationDataAttributes(context); foreach (var authorizationAttribute in GetAuthorizationDataAttributes(context.Method))
foreach (var authorizationAttribute in authorizationAttributes)
{ {
await CheckAsync(authorizationAttribute); await CheckAsync(authorizationAttribute);
} }
@ -42,17 +43,23 @@ namespace Volo.Abp.Authorization
return context.Method.GetCustomAttributes(true).OfType<IAllowAnonymous>().Any(); return context.Method.GetCustomAttributes(true).OfType<IAllowAnonymous>().Any();
} }
protected virtual IAuthorizeData[] GetAuthorizationDataAttributes(MethodInvocationAuthorizationContext context) protected virtual IEnumerable<IAuthorizeData> GetAuthorizationDataAttributes(MethodInfo methodInfo)
{ {
var classAttributes = context.Method.DeclaringType var attributes = methodInfo
.GetCustomAttributes(true) .GetCustomAttributes(true)
.OfType<IAuthorizeData>(); .OfType<IAuthorizeData>();
var methodAttributes = context.Method if (methodInfo.IsPublic)
.GetCustomAttributes(true) {
.OfType<IAuthorizeData>(); attributes = attributes
.Union(
methodInfo.DeclaringType
.GetCustomAttributes(true)
.OfType<IAuthorizeData>()
);
}
return classAttributes.Union(methodAttributes).ToArray(); return attributes;
} }
protected async Task CheckAsync(IAuthorizeData authorizationAttribute) protected async Task CheckAsync(IAuthorizeData authorizationAttribute)

9
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/AlwaysAllowPermissionChecker.cs

@ -1,5 +1,6 @@
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Threading;
namespace Volo.Abp.Authorization.Permissions namespace Volo.Abp.Authorization.Permissions
{ {
@ -11,14 +12,14 @@ namespace Volo.Abp.Authorization.Permissions
/// </summary> /// </summary>
public class AlwaysAllowPermissionChecker : IPermissionChecker public class AlwaysAllowPermissionChecker : IPermissionChecker
{ {
public Task<PermissionGrantInfo> CheckAsync(string name) public Task<bool> IsGrantedAsync(string name)
{ {
return Task.FromResult(new PermissionGrantInfo(name, true, "AlwaysAllow")); return TaskCache.TrueResult;
} }
public Task<PermissionGrantInfo> CheckAsync(ClaimsPrincipal claimsPrincipal, string name) public Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name)
{ {
return Task.FromResult(new PermissionGrantInfo(name, true, "AlwaysAllow")); return TaskCache.TrueResult;
} }
} }
} }

13
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/ClientPermissionValueProvider.cs

@ -15,21 +15,18 @@ namespace Volo.Abp.Authorization.Permissions
} }
public override async Task<PermissionValueProviderGrantInfo> CheckAsync(PermissionValueCheckContext context) public override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
{ {
var clientId = context.Principal?.FindFirst(AbpClaimTypes.ClientId)?.Value; var clientId = context.Principal?.FindFirst(AbpClaimTypes.ClientId)?.Value;
if (clientId == null) if (clientId == null)
{ {
return PermissionValueProviderGrantInfo.NonGranted; return PermissionGrantResult.Undefined;
} }
if (await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, clientId)) return await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, clientId)
{ ? PermissionGrantResult.Granted
return new PermissionValueProviderGrantInfo(true, clientId); : PermissionGrantResult.Undefined;
}
return PermissionValueProviderGrantInfo.NonGranted;
} }
} }
} }

4
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionChecker.cs

@ -6,8 +6,8 @@ namespace Volo.Abp.Authorization.Permissions
{ {
public interface IPermissionChecker public interface IPermissionChecker
{ {
Task<PermissionGrantInfo> CheckAsync([NotNull]string name); Task<bool> IsGrantedAsync([NotNull]string name);
Task<PermissionGrantInfo> CheckAsync([CanBeNull] ClaimsPrincipal claimsPrincipal, [NotNull]string name); Task<bool> IsGrantedAsync([CanBeNull] ClaimsPrincipal claimsPrincipal, [NotNull]string name);
} }
} }

6
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionDefinitionProvider.cs

@ -1,8 +1,6 @@
using Volo.Abp.DependencyInjection; namespace Volo.Abp.Authorization.Permissions
namespace Volo.Abp.Authorization.Permissions
{ {
public interface IPermissionDefinitionProvider : ISingletonDependency public interface IPermissionDefinitionProvider
{ {
void Define(IPermissionDefinitionContext context); void Define(IPermissionDefinitionContext context);
} }

6
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs

@ -1,12 +1,12 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions namespace Volo.Abp.Authorization.Permissions
{ {
public interface IPermissionValueProvider : ISingletonDependency public interface IPermissionValueProvider
{ {
string Name { get; } string Name { get; }
Task<PermissionValueProviderGrantInfo> CheckAsync(PermissionValueCheckContext context); //TODO: Rename to GetResult? (CheckAsync throws exception by naming convention)
Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context);
} }
} }

3
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/NullPermissionStore.cs

@ -2,6 +2,7 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.Authorization.Permissions namespace Volo.Abp.Authorization.Permissions
{ {
@ -16,7 +17,7 @@ namespace Volo.Abp.Authorization.Permissions
public Task<bool> IsGrantedAsync(string name, string providerName, string providerKey) public Task<bool> IsGrantedAsync(string name, string providerName, string providerKey)
{ {
return Task.FromResult(false); return TaskCache.FalseResult;
} }
} }
} }

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

@ -41,12 +41,12 @@ namespace Volo.Abp.Authorization.Permissions
); );
} }
public virtual Task<PermissionGrantInfo> CheckAsync(string name) public virtual Task<bool> IsGrantedAsync(string name)
{ {
return CheckAsync(PrincipalAccessor.Principal, name); return IsGrantedAsync(PrincipalAccessor.Principal, name);
} }
public virtual async Task<PermissionGrantInfo> CheckAsync(ClaimsPrincipal claimsPrincipal, string name) public virtual async Task<bool> IsGrantedAsync(ClaimsPrincipal claimsPrincipal, string name)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
@ -55,6 +55,8 @@ namespace Volo.Abp.Authorization.Permissions
claimsPrincipal claimsPrincipal
); );
var isGranted = false;
foreach (var provider in ValueProviders) foreach (var provider in ValueProviders)
{ {
if (context.Permission.Providers.Any() && if (context.Permission.Providers.Any() &&
@ -64,13 +66,18 @@ namespace Volo.Abp.Authorization.Permissions
} }
var result = await provider.CheckAsync(context); var result = await provider.CheckAsync(context);
if (result.IsGranted)
if (result == PermissionGrantResult.Granted)
{
isGranted = true;
}
else if (result == PermissionGrantResult.Prohibited)
{ {
return new PermissionGrantInfo(context.Permission.Name, true, provider.Name, result.ProviderKey); return false;
} }
} }
return new PermissionGrantInfo(context.Permission.Name, false); return isGranted;
} }
} }
} }

20
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionCheckerExtensions.cs

@ -1,20 +0,0 @@
using System.Security.Claims;
using System.Threading.Tasks;
namespace Volo.Abp.Authorization.Permissions
{
public static class PermissionCheckerExtensions
{
public static async Task<bool> IsGrantedAsync(this IPermissionChecker permissionChecker, string name)
{
return (await permissionChecker.CheckAsync(name)).IsGranted;
}
public static async Task<bool> IsGrantedAsync(this IPermissionChecker permissionChecker, ClaimsPrincipal principal, string name)
{
return (await permissionChecker.CheckAsync(principal, name)).IsGranted;
}
//TODO: Add sync extensions
}
}

10
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinition.cs

@ -22,7 +22,7 @@ namespace Volo.Abp.Authorization.Permissions
/// A list of allowed providers to get/set value of this permission. /// A list of allowed providers to get/set value of this permission.
/// An empty list indicates that all providers are allowed. /// An empty list indicates that all providers are allowed.
/// </summary> /// </summary>
public List<string> Providers { get; } public List<string> Providers { get; } //TODO: Rename to AllowedProviders?
public ILocalizableString DisplayName public ILocalizableString DisplayName
{ {
@ -53,7 +53,9 @@ namespace Volo.Abp.Authorization.Permissions
set => Properties[name] = value; set => Properties[name] = value;
} }
protected internal PermissionDefinition([NotNull] string name, ILocalizableString displayName = null) protected internal PermissionDefinition(
[NotNull] string name,
ILocalizableString displayName = null)
{ {
Name = Check.NotNull(name, nameof(name)); Name = Check.NotNull(name, nameof(name));
DisplayName = displayName ?? new FixedLocalizableString(name); DisplayName = displayName ?? new FixedLocalizableString(name);
@ -63,7 +65,9 @@ namespace Volo.Abp.Authorization.Permissions
_children = new List<PermissionDefinition>(); _children = new List<PermissionDefinition>();
} }
public virtual PermissionDefinition AddChild([NotNull] string name, ILocalizableString displayName = null) public virtual PermissionDefinition AddChild(
[NotNull] string name,
ILocalizableString displayName = null)
{ {
var child = new PermissionDefinition(name, displayName) var child = new PermissionDefinition(name, displayName)
{ {

39
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs

@ -10,9 +10,6 @@ namespace Volo.Abp.Authorization.Permissions
{ {
public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingletonDependency public class PermissionDefinitionManager : IPermissionDefinitionManager, ISingletonDependency
{ {
protected List<IPermissionDefinitionProvider> Providers => _lazyProviders.Value;
private readonly Lazy<List<IPermissionDefinitionProvider>> _lazyProviders;
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value; protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions; private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
@ -30,9 +27,15 @@ namespace Volo.Abp.Authorization.Permissions
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
Options = options.Value; Options = options.Value;
_lazyProviders = new Lazy<List<IPermissionDefinitionProvider>>(CreatePermissionProviders, true); _lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(CreatePermissionDefinitions, true); CreatePermissionDefinitions,
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(CreatePermissionGroupDefinitions, true); isThreadSafe: true
);
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(
CreatePermissionGroupDefinitions,
isThreadSafe: true
);
} }
public virtual PermissionDefinition Get(string name) public virtual PermissionDefinition Get(string name)
@ -64,14 +67,6 @@ namespace Volo.Abp.Authorization.Permissions
return PermissionGroupDefinitions.Values.ToImmutableList(); return PermissionGroupDefinitions.Values.ToImmutableList();
} }
protected virtual List<IPermissionDefinitionProvider> CreatePermissionProviders()
{
return Options
.DefinitionProviders
.Select(p => _serviceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
.ToList();
}
protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions() protected virtual Dictionary<string, PermissionDefinition> CreatePermissionDefinitions()
{ {
var permissions = new Dictionary<string, PermissionDefinition>(); var permissions = new Dictionary<string, PermissionDefinition>();
@ -87,7 +82,9 @@ namespace Volo.Abp.Authorization.Permissions
return permissions; return permissions;
} }
protected virtual void AddPermissionToDictionaryRecursively(Dictionary<string, PermissionDefinition> permissions, PermissionDefinition permission) protected virtual void AddPermissionToDictionaryRecursively(
Dictionary<string, PermissionDefinition> permissions,
PermissionDefinition permission)
{ {
if (permissions.ContainsKey(permission.Name)) if (permissions.ContainsKey(permission.Name))
{ {
@ -106,9 +103,17 @@ namespace Volo.Abp.Authorization.Permissions
{ {
var context = new PermissionDefinitionContext(); var context = new PermissionDefinitionContext();
foreach (var provider in Providers) using (var scope = _serviceProvider.CreateScope())
{ {
provider.Define(context); var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.Define(context);
}
} }
return context.Groups; return context.Groups;

6
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionProvider.cs

@ -1,6 +1,8 @@
namespace Volo.Abp.Authorization.Permissions using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions
{ {
public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider, ITransientDependency
{ {
public abstract void Define(IPermissionDefinitionContext context); public abstract void Define(IPermissionDefinitionContext context);
} }

9
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionGrantResult.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.Authorization.Permissions
{
public enum PermissionGrantResult
{
Undefined,
Granted,
Prohibited
}
}

8
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionGroupDefinition.cs

@ -37,7 +37,9 @@ namespace Volo.Abp.Authorization.Permissions
set => Properties[name] = value; set => Properties[name] = value;
} }
protected internal PermissionGroupDefinition(string name, ILocalizableString displayName = null) protected internal PermissionGroupDefinition(
string name,
ILocalizableString displayName = null)
{ {
Name = name; Name = name;
DisplayName = displayName ?? new FixedLocalizableString(Name); DisplayName = displayName ?? new FixedLocalizableString(Name);
@ -46,7 +48,9 @@ namespace Volo.Abp.Authorization.Permissions
_permissions = new List<PermissionDefinition>(); _permissions = new List<PermissionDefinition>();
} }
public virtual PermissionDefinition AddPermission(string name, ILocalizableString displayName = null) public virtual PermissionDefinition AddPermission(
string name,
ILocalizableString displayName = null)
{ {
var permission = new PermissionDefinition(name, displayName); var permission = new PermissionDefinition(name, displayName);

4
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueCheckContext.cs

@ -11,7 +11,9 @@ namespace Volo.Abp.Authorization.Permissions
[CanBeNull] [CanBeNull]
public ClaimsPrincipal Principal { get; } public ClaimsPrincipal Principal { get; }
public PermissionValueCheckContext([NotNull] PermissionDefinition permission, [CanBeNull] ClaimsPrincipal principal) public PermissionValueCheckContext(
[NotNull] PermissionDefinition permission,
[CanBeNull] ClaimsPrincipal principal)
{ {
Check.NotNull(permission, nameof(permission)); Check.NotNull(permission, nameof(permission));

5
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProvider.cs

@ -1,8 +1,9 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions namespace Volo.Abp.Authorization.Permissions
{ {
public abstract class PermissionValueProvider : IPermissionValueProvider public abstract class PermissionValueProvider : IPermissionValueProvider, ISingletonDependency
{ {
public abstract string Name { get; } public abstract string Name { get; }
@ -13,6 +14,6 @@ namespace Volo.Abp.Authorization.Permissions
PermissionStore = permissionStore; PermissionStore = permissionStore;
} }
public abstract Task<PermissionValueProviderGrantInfo> CheckAsync(PermissionValueCheckContext context); public abstract Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context);
} }
} }

8
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/RolePermissionValueProvider.cs

@ -16,23 +16,23 @@ namespace Volo.Abp.Authorization.Permissions
} }
public override async Task<PermissionValueProviderGrantInfo> CheckAsync(PermissionValueCheckContext context) public override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
{ {
var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray(); var roles = context.Principal?.FindAll(AbpClaimTypes.Role).Select(c => c.Value).ToArray();
if (roles == null || !roles.Any()) if (roles == null || !roles.Any())
{ {
return PermissionValueProviderGrantInfo.NonGranted; return PermissionGrantResult.Undefined;
} }
foreach (var role in roles) foreach (var role in roles)
{ {
if (await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, role)) if (await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, role))
{ {
return new PermissionValueProviderGrantInfo(true, role); return PermissionGrantResult.Granted;
} }
} }
return PermissionValueProviderGrantInfo.NonGranted; return PermissionGrantResult.Undefined;
} }
} }
} }

13
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/UserPermissionValueProvider.cs

@ -15,21 +15,18 @@ namespace Volo.Abp.Authorization.Permissions
} }
public override async Task<PermissionValueProviderGrantInfo> CheckAsync(PermissionValueCheckContext context) public override async Task<PermissionGrantResult> CheckAsync(PermissionValueCheckContext context)
{ {
var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value; var userId = context.Principal?.FindFirst(AbpClaimTypes.UserId)?.Value;
if (userId == null) if (userId == null)
{ {
return PermissionValueProviderGrantInfo.NonGranted; return PermissionGrantResult.Undefined;
} }
if (await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, userId)) return await PermissionStore.IsGrantedAsync(context.Permission.Name, Name, userId)
{ ? PermissionGrantResult.Granted
return new PermissionValueProviderGrantInfo(true, userId); : PermissionGrantResult.Undefined;
}
return PermissionValueProviderGrantInfo.NonGranted;
} }
} }
} }

1
framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs

@ -12,6 +12,7 @@ namespace Volo.Abp.Aspects
public const string Validation = "AbpValidation"; public const string Validation = "AbpValidation";
public const string UnitOfWork = "AbpUnitOfWork"; public const string UnitOfWork = "AbpUnitOfWork";
public const string Authorization = "AbpAuthorization"; public const string Authorization = "AbpAuthorization";
public const string FeatureChecking = "AbpFeatureChecking";
public static void AddApplied(object obj, params string[] concerns) public static void AddApplied(object obj, params string[] concerns)
{ {

12
framework/src/Volo.Abp.Core/Volo/Abp/NameValue.cs

@ -8,17 +8,11 @@ namespace Volo.Abp
[Serializable] [Serializable]
public class NameValue : NameValue<string> public class NameValue : NameValue<string>
{ {
/// <summary>
/// Creates a new <see cref="NameValue"/>.
/// </summary>
public NameValue() public NameValue()
{ {
} }
/// <summary>
/// Creates a new <see cref="NameValue"/>.
/// </summary>
public NameValue(string name, string value) public NameValue(string name, string value)
{ {
Name = name; Name = name;
@ -42,17 +36,11 @@ namespace Volo.Abp
/// </summary> /// </summary>
public T Value { get; set; } public T Value { get; set; }
/// <summary>
/// Creates a new <see cref="NameValue"/>.
/// </summary>
public NameValue() public NameValue()
{ {
} }
/// <summary>
/// Creates a new <see cref="NameValue"/>.
/// </summary>
public NameValue(string name, T value) public NameValue(string name, T value)
{ {
Name = name; Name = name;

16
framework/src/Volo.Abp.Core/Volo/Abp/Threading/TaskCache.cs

@ -0,0 +1,16 @@
using System.Threading.Tasks;
namespace Volo.Abp.Threading
{
public static class TaskCache
{
public static Task<bool> TrueResult { get; }
public static Task<bool> FalseResult { get; }
static TaskCache()
{
TrueResult = Task.FromResult(true);
FalseResult = Task.FromResult(false);
}
}
}

7
framework/src/Volo.Abp.Data/Volo/Abp/Data/HasExtraPropertiesExtensions.cs

@ -39,5 +39,12 @@ namespace Volo.Abp.Data
source.ExtraProperties[name] = value; source.ExtraProperties[name] = value;
return source; return source;
} }
public static TSource RemoveProperty<TSource>(this TSource source, string name)
where TSource : IHasExtraProperties
{
source.ExtraProperties.Remove(name);
return source;
}
} }
} }

1
framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj

@ -17,6 +17,7 @@
<ProjectReference Include="..\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" /> <ProjectReference Include="..\Volo.Abp.Authorization\Volo.Abp.Authorization.csproj" />
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> <ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" /> <ProjectReference Include="..\Volo.Abp.Ddd.Domain\Volo.Abp.Ddd.Domain.csproj" />
<ProjectReference Include="..\Volo.Abp.Features\Volo.Abp.Features.csproj" />
<ProjectReference Include="..\Volo.Abp.Http.Abstractions\Volo.Abp.Http.Abstractions.csproj" /> <ProjectReference Include="..\Volo.Abp.Http.Abstractions\Volo.Abp.Http.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.ObjectMapping\Volo.Abp.ObjectMapping.csproj" /> <ProjectReference Include="..\Volo.Abp.ObjectMapping\Volo.Abp.ObjectMapping.csproj" />
<ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" /> <ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" />

7
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs

@ -2,6 +2,7 @@
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.Domain; using Volo.Abp.Domain;
using Volo.Abp.Features;
using Volo.Abp.Http; using Volo.Abp.Http;
using Volo.Abp.Http.Modeling; using Volo.Abp.Http.Modeling;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
@ -20,7 +21,8 @@ namespace Volo.Abp.Application
typeof(AbpValidationModule), typeof(AbpValidationModule),
typeof(AbpAuthorizationModule), typeof(AbpAuthorizationModule),
typeof(AbpHttpAbstractionsModule), typeof(AbpHttpAbstractionsModule),
typeof(AbpSettingsModule) typeof(AbpSettingsModule),
typeof(AbpFeaturesModule)
)] )]
public class AbpDddApplicationModule : AbpModule public class AbpDddApplicationModule : AbpModule
{ {
@ -30,8 +32,7 @@ namespace Volo.Abp.Application
{ {
options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService)); options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService)); options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled)); //TODO: Move to it's own module if possible? options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled));
options.IgnoredInterfaces.AddIfNotContains(typeof(IAuthorizationEnabled)); //TODO: Move to it's own module if possible?
}); });
} }
} }

4
framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs

@ -9,6 +9,7 @@ using Volo.Abp.Aspects;
using Volo.Abp.Auditing; using Volo.Abp.Auditing;
using Volo.Abp.Authorization; using Volo.Abp.Authorization;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Features;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping; using Volo.Abp.ObjectMapping;
@ -25,7 +26,6 @@ namespace Volo.Abp.Application.Services
IAvoidDuplicateCrossCuttingConcerns, IAvoidDuplicateCrossCuttingConcerns,
IValidationEnabled, IValidationEnabled,
IUnitOfWorkEnabled, IUnitOfWorkEnabled,
IAuthorizationEnabled,
IAuditingEnabled, IAuditingEnabled,
ITransientDependency ITransientDependency
{ {
@ -51,6 +51,8 @@ namespace Volo.Abp.Application.Services
public IAuthorizationService AuthorizationService { get; set; } public IAuthorizationService AuthorizationService { get; set; }
public IFeatureChecker FeatureChecker { get; set; }
protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current;
protected ILogger Logger => _lazyLogger.Value; protected ILogger Logger => _lazyLogger.Value;

9
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs

@ -75,11 +75,10 @@ namespace Volo.Abp.Domain.Entities
where TEntity : IEntity<TKey> where TEntity : IEntity<TKey>
{ {
var lambdaParam = Expression.Parameter(typeof(TEntity)); var lambdaParam = Expression.Parameter(typeof(TEntity));
var lambdaBody = Expression.Equal( var leftExpression = Expression.PropertyOrField(lambdaParam, "Id");
Expression.PropertyOrField(lambdaParam, nameof(Entity<TKey>.Id)), Expression<Func<object>> closure = () => id;
Expression.Constant(id, typeof(TKey)) var rightExpression = Expression.Convert(closure.Body, leftExpression.Type);
); var lambdaBody = Expression.Equal(leftExpression, rightExpression);
return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam); return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
} }
} }

5
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs

@ -18,11 +18,6 @@ namespace Volo.Abp.Emailing
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
Configure<SettingOptions>(options =>
{
options.DefinitionProviders.Add<EmailSettingProvider>();
});
Configure<VirtualFileSystemOptions>(options => Configure<VirtualFileSystemOptions>(options =>
{ {
options.FileSets.AddEmbedded<AbpEmailingModule>(); options.FileSets.AddEmbedded<AbpEmailingModule>();

4
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj

@ -16,9 +16,9 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.EntityFrameworkCore\Volo.Abp.EntityFrameworkCore.csproj" /> <ProjectReference Include="..\Volo.Abp.EntityFrameworkCore\Volo.Abp.EntityFrameworkCore.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MySql.Data.EntityFrameworkCore" Version="8.0.13" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

8
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs

@ -1,7 +1,7 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using MySql.Data.EntityFrameworkCore.Infraestructure;
using System; using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Volo.Abp.EntityFrameworkCore.DependencyInjection; using Volo.Abp.EntityFrameworkCore.DependencyInjection;
namespace Volo.Abp.EntityFrameworkCore namespace Volo.Abp.EntityFrameworkCore
@ -10,15 +10,15 @@ namespace Volo.Abp.EntityFrameworkCore
{ {
public static DbContextOptionsBuilder UseMySQL( public static DbContextOptionsBuilder UseMySQL(
[NotNull] this AbpDbContextConfigurationContext context, [NotNull] this AbpDbContextConfigurationContext context,
[CanBeNull] Action<MySQLDbContextOptionsBuilder> mySQLOptionsAction = null) [CanBeNull] Action<MySqlDbContextOptionsBuilder> mySQLOptionsAction = null)
{ {
if (context.ExistingConnection != null) if (context.ExistingConnection != null)
{ {
return context.DbContextOptions.UseMySQL(context.ExistingConnection, mySQLOptionsAction); return context.DbContextOptions.UseMySql(context.ExistingConnection, mySQLOptionsAction);
} }
else else
{ {
return context.DbContextOptions.UseMySQL(context.ConnectionString, mySQLOptionsAction); return context.DbContextOptions.UseMySql(context.ConnectionString, mySQLOptionsAction);
} }
} }
} }

6
framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsMySQLExtensions.cs

@ -1,6 +1,6 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using MySql.Data.EntityFrameworkCore.Infraestructure;
using System; using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Volo.Abp.EntityFrameworkCore namespace Volo.Abp.EntityFrameworkCore
{ {
@ -8,7 +8,7 @@ namespace Volo.Abp.EntityFrameworkCore
{ {
public static void UseMySQL( public static void UseMySQL(
[NotNull] this AbpDbContextOptions options, [NotNull] this AbpDbContextOptions options,
[CanBeNull] Action<MySQLDbContextOptionsBuilder> mySQLOptionsAction = null) [CanBeNull] Action<MySqlDbContextOptionsBuilder> mySQLOptionsAction = null)
{ {
options.Configure(context => options.Configure(context =>
{ {
@ -18,7 +18,7 @@ namespace Volo.Abp.EntityFrameworkCore
public static void UseMySQL<TDbContext>( public static void UseMySQL<TDbContext>(
[NotNull] this AbpDbContextOptions options, [NotNull] this AbpDbContextOptions options,
[CanBeNull] Action<MySQLDbContextOptionsBuilder> mySQLOptionsAction = null) [CanBeNull] Action<MySqlDbContextOptionsBuilder> mySQLOptionsAction = null)
where TDbContext : AbpDbContext<TDbContext> where TDbContext : AbpDbContext<TDbContext>
{ {
options.Configure<TDbContext>(context => options.Configure<TDbContext>(context =>

22
framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.Features</AssemblyName>
<PackageId>Volo.Abp.Features</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy.Abstractions\Volo.Abp.MultiTenancy.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
</ItemGroup>
</Project>

51
framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs

@ -0,0 +1,51 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Validation;
namespace Volo.Abp.Features
{
[DependsOn(
typeof(AbpLocalizationAbstractionsModule),
typeof(AbpMultiTenancyAbstractionsModule),
typeof(AbpValidationModule)
)]
public class AbpFeaturesModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(FeatureInterceptorRegistrar.RegisterIfNeeded);
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<FeatureOptions>(options =>
{
options.ValueProviders.Add<DefaultValueFeatureValueProvider>();
options.ValueProviders.Add<TenantFeatureValueProvider>();
});
}
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
{
if (typeof(IFeatureDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
services.Configure<FeatureOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
}
}

22
framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs

@ -0,0 +1,22 @@
using System.Threading.Tasks;
namespace Volo.Abp.Features
{
public class DefaultValueFeatureValueProvider : FeatureValueProvider
{
public const string ProviderName = "Default";
public override string Name => ProviderName;
public DefaultValueFeatureValueProvider(IFeatureStore settingStore)
: base(settingStore)
{
}
public override Task<string> GetOrNullAsync(FeatureDefinition setting)
{
return Task.FromResult(setting.DefaultValue);
}
}
}

10
framework/src/Volo.Abp.Features/Volo/Abp/Features/DisableFeatureCheckAttribute.cs

@ -0,0 +1,10 @@
using System;
namespace Volo.Abp.Features
{
[AttributeUsage(AttributeTargets.Method)]
public class DisableFeatureCheckAttribute : Attribute
{
}
}

68
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Volo.Abp.Features
{
public class FeatureChecker : FeatureCheckerBase
{
protected FeatureOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
protected IFeatureDefinitionManager FeatureDefinitionManager { get; }
protected List<IFeatureValueProvider> Providers => _providers.Value;
private readonly Lazy<List<IFeatureValueProvider>> _providers;
public FeatureChecker(
IOptions<FeatureOptions> options,
IServiceProvider serviceProvider,
IFeatureDefinitionManager featureDefinitionManager)
{
ServiceProvider = serviceProvider;
FeatureDefinitionManager = featureDefinitionManager;
Options = options.Value;
_providers = new Lazy<List<IFeatureValueProvider>>(
() => Options
.ValueProviders
.Select(type => ServiceProvider.GetRequiredService(type) as IFeatureValueProvider)
.ToList(),
true
);
}
public override async Task<string> GetOrNullAsync(string name)
{
var featureDefinition = FeatureDefinitionManager.Get(name);
var providers = Enumerable
.Reverse(Providers);
if (featureDefinition.AllowedProviders.Any())
{
providers = providers.Where(p => featureDefinition.AllowedProviders.Contains(p.Name));
}
return await GetOrNullValueFromProvidersAsync(providers, featureDefinition);
}
protected virtual async Task<string> GetOrNullValueFromProvidersAsync(
IEnumerable<IFeatureValueProvider> providers,
FeatureDefinition feature)
{
foreach (var provider in providers)
{
var value = await provider.GetOrNullAsync(feature);
if (value != null)
{
return value;
}
}
return null;
}
}
}

32
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerBase.cs

@ -0,0 +1,32 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public abstract class FeatureCheckerBase : IFeatureChecker, ITransientDependency
{
public abstract Task<string> GetOrNullAsync(string name);
public virtual async Task<bool> IsEnabledAsync(string name)
{
var value = await GetOrNullAsync(name);
if (value == null)
{
return false;
}
try
{
return bool.Parse(value);
}
catch (Exception ex)
{
throw new AbpException(
$"The value '{value}' for the feature '{name}' should be a boolean, but was not!",
ex
);
}
}
}
}

143
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs

@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Authorization;
using Volo.Abp.Threading;
namespace Volo.Abp.Features
{
public static class FeatureCheckerExtensions
{
public static async Task<T> GetAsync<T>(
[NotNull] this IFeatureChecker featureChecker,
[NotNull] string name,
T defaultValue = default)
where T : struct
{
Check.NotNull(featureChecker, nameof(featureChecker));
Check.NotNull(name, nameof(name));
var value = await featureChecker.GetOrNullAsync(name);
return value?.To<T>() ?? defaultValue;
}
public static string GetOrNull(
[NotNull] this IFeatureChecker featureChecker,
[NotNull] string name)
{
Check.NotNull(featureChecker, nameof(featureChecker));
return AsyncHelper.RunSync(() => featureChecker.GetOrNullAsync(name));
}
public static T Get<T>(
[NotNull] this IFeatureChecker featureChecker,
[NotNull] string name,
T defaultValue = default)
where T : struct
{
return AsyncHelper.RunSync(() => featureChecker.GetAsync(name, defaultValue));
}
public static bool IsEnabled(
[NotNull] this IFeatureChecker featureChecker,
[NotNull] string name)
{
return AsyncHelper.RunSync(() => featureChecker.IsEnabledAsync(name));
}
public static async Task<bool> IsEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames)
{
if (featureNames.IsNullOrEmpty())
{
return true;
}
if (requiresAll)
{
foreach (var featureName in featureNames)
{
if (!(await featureChecker.IsEnabledAsync(featureName)))
{
return false;
}
}
return true;
}
foreach (var featureName in featureNames)
{
if (await featureChecker.IsEnabledAsync(featureName))
{
return true;
}
}
return false;
}
public static bool IsEnabled(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames)
{
return AsyncHelper.RunSync(() => featureChecker.IsEnabledAsync(requiresAll, featureNames));
}
public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, string featureName)
{
if (!(await featureChecker.IsEnabledAsync(featureName)))
{
throw new AbpAuthorizationException("Feature is not enabled: " + featureName);
}
}
public static void CheckEnabled(this IFeatureChecker featureChecker, string featureName)
{
if (!featureChecker.IsEnabled(featureName))
{
throw new AbpAuthorizationException("Feature is not enabled: " + featureName);
}
}
public static async Task CheckEnabledAsync(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames)
{
if (featureNames.IsNullOrEmpty())
{
return;
}
if (requiresAll)
{
foreach (var featureName in featureNames)
{
if (!(await featureChecker.IsEnabledAsync(featureName)))
{
throw new AbpAuthorizationException(
"Required features are not enabled. All of these features must be enabled: " +
string.Join(", ", featureNames)
);
}
}
}
else
{
foreach (var featureName in featureNames)
{
if (await featureChecker.IsEnabledAsync(featureName))
{
return;
}
}
throw new AbpAuthorizationException(
"Required features are not enabled. At least one of these features must be enabled: " +
string.Join(", ", featureNames)
);
}
}
public static void CheckEnabled(this IFeatureChecker featureChecker, bool requiresAll, params string[] featureNames)
{
AsyncHelper.RunSync(() => featureChecker.CheckEnabledAsync(requiresAll, featureNames));
}
}
}

178
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs

@ -0,0 +1,178 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.Features
{
public class FeatureDefinition
{
/// <summary>
/// Unique name of the feature.
/// </summary>
[NotNull]
public string Name { get; }
[NotNull]
public ILocalizableString DisplayName
{
get => _displayName;
set => _displayName = Check.NotNull(value, nameof(value));
}
private ILocalizableString _displayName;
[CanBeNull]
public ILocalizableString Description { get; set; }
/// <summary>
/// Parent of this feature, if one exists.
/// If set, this feature can be enabled only if the parent is enabled.
/// </summary>
[CanBeNull]
public FeatureDefinition Parent { get; private set; }
/// <summary>
/// List of child features.
/// </summary>
public IReadOnlyList<FeatureDefinition> Children => _children.ToImmutableList();
private readonly List<FeatureDefinition> _children;
/// <summary>
/// Default value of the feature.
/// </summary>
[CanBeNull]
public string DefaultValue { get; set; }
/// <summary>
/// Can clients see this feature and it's value.
/// Default: true.
/// </summary>
public bool IsVisibleToClients { get; set; }
/// <summary>
/// A list of allowed providers to get/set value of this feature.
/// An empty list indicates that all providers are allowed.
/// </summary>
[NotNull]
public List<string> AllowedProviders { get; }
/// <summary>
/// Gets/sets a key-value on the <see cref="Properties"/>.
/// </summary>
/// <param name="name">Name of the property</param>
/// <returns>
/// Returns the value in the <see cref="Properties"/> dictionary by given <see cref="name"/>.
/// Returns null if given <see cref="name"/> is not present in the <see cref="Properties"/> dictionary.
/// </returns>
[CanBeNull]
public object this[string name]
{
get => Properties.GetOrDefault(name);
set => Properties[name] = value;
}
/// <summary>
/// Can be used to get/set custom properties for this feature.
/// </summary>
[NotNull]
public Dictionary<string, object> Properties { get; }
/// <summary>
/// Input type.
/// This can be used to prepare an input for changing this feature's value.
/// Default: <see cref="ToggleStringValueType"/>.
/// </summary>
[CanBeNull]
public IStringValueType ValueType { get; set; }
public FeatureDefinition(
string name,
string defaultValue = null,
ILocalizableString displayName = null,
ILocalizableString description = null,
IStringValueType valueType = null,
bool isVisibleToClients = true)
{
Name = name;
DefaultValue = defaultValue;
DisplayName = displayName ?? new FixedLocalizableString(name);
Description = description;
ValueType = valueType;
IsVisibleToClients = isVisibleToClients;
Properties = new Dictionary<string, object>();
AllowedProviders = new List<string>();
_children = new List<FeatureDefinition>();
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.
/// This is a shortcut for nested calls on this object.
/// </summary>
public virtual FeatureDefinition WithProperty(string key, object value)
{
Properties[key] = value;
return this;
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.
/// This is a shortcut for nested calls on this object.
/// </summary>
public virtual FeatureDefinition WithProviders(params string[] providers)
{
if (!providers.IsNullOrEmpty())
{
AllowedProviders.AddRange(providers);
}
return this;
}
/// <summary>
/// Adds a child feature.
/// </summary>
/// <returns>Returns a newly created child feature</returns>
public FeatureDefinition CreateChild(
string name,
string defaultValue = null,
ILocalizableString displayName = null,
ILocalizableString description = null,
IStringValueType valueType = null,
bool isVisibleToClients = true)
{
var feature = new FeatureDefinition(
name,
defaultValue,
displayName,
description,
valueType,
isVisibleToClients)
{
Parent = this
};
_children.Add(feature);
return feature;
}
public void RemoveChild(string name)
{
var featureToRemove = _children.FirstOrDefault(f => f.Name == name);
if (featureToRemove == null)
{
throw new AbpException($"Could not find a feature named '{name}' in the Children of this feature '{Name}'.");
}
featureToRemove.Parent = null;
_children.Remove(featureToRemove);
}
public override string ToString()
{
return $"[{nameof(FeatureDefinition)}: {Name}]";
}
}
}

51
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionContext.cs

@ -0,0 +1,51 @@
using System.Collections.Generic;
using Volo.Abp.Localization;
namespace Volo.Abp.Features
{
public class FeatureDefinitionContext : IFeatureDefinitionContext
{
internal Dictionary<string, FeatureGroupDefinition> Groups { get; }
public FeatureDefinitionContext()
{
Groups = new Dictionary<string, FeatureGroupDefinition>();
}
public FeatureGroupDefinition AddGroup(string name, ILocalizableString displayName = null)
{
Check.NotNull(name, nameof(name));
if (Groups.ContainsKey(name))
{
throw new AbpException($"There is already an existing permission group with name: {name}");
}
return Groups[name] = new FeatureGroupDefinition(name, displayName);
}
public FeatureGroupDefinition GetGroupOrNull(string name)
{
Check.NotNull(name, nameof(name));
if (!Groups.ContainsKey(name))
{
return null;
}
return Groups[name];
}
public void RemoveGroup(string name)
{
Check.NotNull(name, nameof(name));
if (!Groups.ContainsKey(name))
{
throw new AbpException($"Undefined feature group: '{name}'.");
}
Groups.Remove(name);
}
}
}

117
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public class FeatureDefinitionManager : IFeatureDefinitionManager, ISingletonDependency
{
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureGroupDefinition>> _lazyFeatureGroupDefinitions;
protected IDictionary<string, FeatureDefinition> FeatureDefinitions => _lazyFeatureDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureDefinition>> _lazyFeatureDefinitions;
protected FeatureOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
public FeatureDefinitionManager(
IOptions<FeatureOptions> options,
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
Options = options.Value;
_lazyFeatureDefinitions = new Lazy<Dictionary<string, FeatureDefinition>>(
CreateFeatureDefinitions,
isThreadSafe: true
);
_lazyFeatureGroupDefinitions = new Lazy<Dictionary<string, FeatureGroupDefinition>>(
CreateFeatureGroupDefinitions,
isThreadSafe:true
);
}
public virtual FeatureDefinition Get(string name)
{
Check.NotNull(name, nameof(name));
var feature = GetOrNull(name);
if (feature == null)
{
throw new AbpException("Undefined feature: " + name);
}
return feature;
}
public virtual IReadOnlyList<FeatureDefinition> GetAll()
{
return FeatureDefinitions.Values.ToImmutableList();
}
public virtual FeatureDefinition GetOrNull(string name)
{
return FeatureDefinitions.GetOrDefault(name);
}
protected virtual Dictionary<string, FeatureDefinition> CreateFeatureDefinitions()
{
var features = new Dictionary<string, FeatureDefinition>();
foreach (var groupDefinition in FeatureGroupDefinitions.Values)
{
foreach (var feature in groupDefinition.Features)
{
AddFeatureToDictionaryRecursively(features, feature);
}
}
return features;
}
protected virtual void AddFeatureToDictionaryRecursively(
Dictionary<string, FeatureDefinition> features,
FeatureDefinition feature)
{
if (features.ContainsKey(feature.Name))
{
throw new AbpException("Duplicate feature name: " + feature.Name);
}
features[feature.Name] = feature;
foreach (var child in feature.Children)
{
AddFeatureToDictionaryRecursively(features, child);
}
}
protected virtual Dictionary<string, FeatureGroupDefinition> CreateFeatureGroupDefinitions()
{
var context = new FeatureDefinitionContext();
using (var scope = _serviceProvider.CreateScope())
{
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IFeatureDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.Define(context);
}
}
return context.Groups;
}
}
}

9
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionProvider.cs

@ -0,0 +1,9 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public abstract class FeatureDefinitionProvider : IFeatureDefinitionProvider, ISingletonDependency
{
public abstract void Define(IFeatureDefinitionContext context);
}
}

111
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs

@ -0,0 +1,111 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.Features
{
public class FeatureGroupDefinition
{
/// <summary>
/// Unique name of the group.
/// </summary>
public string Name { get; }
public Dictionary<string, object> Properties { get; }
public ILocalizableString DisplayName
{
get => _displayName;
set => _displayName = Check.NotNull(value, nameof(value));
}
private ILocalizableString _displayName;
public IReadOnlyList<FeatureDefinition> Features => _features.ToImmutableList();
private readonly List<FeatureDefinition> _features;
/// <summary>
/// Gets/sets a key-value on the <see cref="Properties"/>.
/// </summary>
/// <param name="name">Name of the property</param>
/// <returns>
/// Returns the value in the <see cref="Properties"/> dictionary by given <see cref="name"/>.
/// Returns null if given <see cref="name"/> is not present in the <see cref="Properties"/> dictionary.
/// </returns>
public object this[string name]
{
get => Properties.GetOrDefault(name);
set => Properties[name] = value;
}
protected internal FeatureGroupDefinition(
string name,
ILocalizableString displayName = null)
{
Name = name;
DisplayName = displayName ?? new FixedLocalizableString(Name);
Properties = new Dictionary<string, object>();
_features = new List<FeatureDefinition>();
}
public virtual FeatureDefinition AddFeature(
string name,
string defaultValue = null,
ILocalizableString displayName = null,
ILocalizableString description = null,
IStringValueType valueType = null,
bool isVisibleToClients = true)
{
var feature = new FeatureDefinition(
name,
defaultValue,
displayName,
description,
valueType,
isVisibleToClients
);
_features.Add(feature);
return feature;
}
public virtual List<FeatureDefinition> GetFeaturesWithChildren()
{
var features = new List<FeatureDefinition>();
foreach (var feature in _features)
{
AddFeatureToListRecursively(features, feature);
}
return features;
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.
/// This is a shortcut for nested calls on this object.
/// </summary>
public virtual FeatureGroupDefinition WithProperty(string key, object value)
{
Properties[key] = value;
return this;
}
private void AddFeatureToListRecursively(List<FeatureDefinition> features, FeatureDefinition feature)
{
features.Add(feature);
foreach (var child in feature.Children)
{
AddFeatureToListRecursively(features, child);
}
}
public override string ToString()
{
return $"[{nameof(FeatureGroupDefinition)} {Name}]";
}
}
}

56
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptor.cs

@ -0,0 +1,56 @@
using System.Threading.Tasks;
using Volo.Abp.Aspects;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Threading;
namespace Volo.Abp.Features
{
public class FeatureInterceptor : AbpInterceptor, ITransientDependency
{
private readonly IMethodInvocationFeatureCheckerService _methodInvocationFeatureCheckerService;
public FeatureInterceptor(
IMethodInvocationFeatureCheckerService methodInvocationFeatureCheckerService)
{
_methodInvocationFeatureCheckerService = methodInvocationFeatureCheckerService;
}
public override void Intercept(IAbpMethodInvocation invocation)
{
if (AbpCrossCuttingConcerns.IsApplied(
invocation.TargetObject,
AbpCrossCuttingConcerns.FeatureChecking))
{
invocation.Proceed();
return;
}
AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation));
invocation.Proceed();
}
public override async Task InterceptAsync(IAbpMethodInvocation invocation)
{
if (AbpCrossCuttingConcerns.IsApplied(
invocation.TargetObject,
AbpCrossCuttingConcerns.FeatureChecking))
{
await invocation.ProceedAsync();
return;
}
AsyncHelper.RunSync(() => CheckFeaturesAsync(invocation));
await invocation.ProceedAsync();
}
protected virtual Task CheckFeaturesAsync(IAbpMethodInvocation invocation)
{
return _methodInvocationFeatureCheckerService.CheckAsync(
new MethodInvocationFeatureCheckerContext(
invocation.Method
)
);
}
}
}

36
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs

@ -0,0 +1,36 @@
using System;
using System.Linq;
using System.Reflection;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public static class FeatureInterceptorRegistrar
{
public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
if (ShouldIntercept(context.ImplementationType))
{
context.Interceptors.TryAdd<FeatureInterceptor>();
}
}
private static bool ShouldIntercept(Type type)
{
return type.IsDefined(typeof(RequiresFeatureAttribute), true) ||
AnyMethodHasRequiresFeatureAttribute(type);
}
private static bool AnyMethodHasRequiresFeatureAttribute(Type implementationType)
{
return implementationType
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Any(HasRequiresFeatureAttribute);
}
private static bool HasRequiresFeatureAttribute(MemberInfo methodInfo)
{
return methodInfo.IsDefined(typeof(RequiresFeatureAttribute), true);
}
}
}

17
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureOptions.cs

@ -0,0 +1,17 @@
using Volo.Abp.Collections;
namespace Volo.Abp.Features
{
public class FeatureOptions
{
public ITypeList<IFeatureDefinitionProvider> DefinitionProviders { get; }
public ITypeList<IFeatureValueProvider> ValueProviders { get; }
public FeatureOptions()
{
DefinitionProviders = new TypeList<IFeatureDefinitionProvider>();
ValueProviders = new TypeList<IFeatureValueProvider>();
}
}
}

19
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValue.cs

@ -0,0 +1,19 @@
using System;
namespace Volo.Abp.Features
{
[Serializable]
public class FeatureValue : NameValue
{
public FeatureValue()
{
}
public FeatureValue(string name, string value)
{
Name = name;
Value = value;
}
}
}

19
framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProvider.cs

@ -0,0 +1,19 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public abstract class FeatureValueProvider : IFeatureValueProvider, ISingletonDependency
{
public abstract string Name { get; }
protected IFeatureStore FeatureStore { get; }
protected FeatureValueProvider(IFeatureStore featureStore)
{
FeatureStore = featureStore;
}
public abstract Task<string> GetOrNullAsync(FeatureDefinition feature);
}
}

12
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureChecker.cs

@ -0,0 +1,12 @@
using JetBrains.Annotations;
using System.Threading.Tasks;
namespace Volo.Abp.Features
{
public interface IFeatureChecker
{
Task<string> GetOrNullAsync([NotNull] string name);
Task<bool> IsEnabledAsync(string name);
}
}

14
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionContext.cs

@ -0,0 +1,14 @@
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace Volo.Abp.Features
{
public interface IFeatureDefinitionContext
{
FeatureGroupDefinition AddGroup([NotNull] string name, ILocalizableString displayName = null);
FeatureGroupDefinition GetGroupOrNull(string name);
void RemoveGroup(string name);
}
}

15
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionManager.cs

@ -0,0 +1,15 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Features
{
public interface IFeatureDefinitionManager
{
[NotNull]
FeatureDefinition Get([NotNull] string name);
IReadOnlyList<FeatureDefinition> GetAll();
FeatureDefinition GetOrNull(string name);
}
}

7
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionProvider.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Features
{
public interface IFeatureDefinitionProvider
{
void Define(IFeatureDefinitionContext context);
}
}

14
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureStore.cs

@ -0,0 +1,14 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Features
{
public interface IFeatureStore
{
Task<string> GetOrNullAsync(
[NotNull] string name,
[CanBeNull] string providerName,
[CanBeNull] string providerKey
);
}
}

12
framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureValueProvider.cs

@ -0,0 +1,12 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.Features
{
public interface IFeatureValueProvider
{
string Name { get; }
Task<string> GetOrNullAsync([NotNull] FeatureDefinition feature);
}
}

11
framework/src/Volo.Abp.Features/Volo/Abp/Features/IMethodInvocationFeatureCheckerService.cs

@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace Volo.Abp.Features
{
public interface IMethodInvocationFeatureCheckerService
{
Task CheckAsync(
MethodInvocationFeatureCheckerContext context
);
}
}

14
framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerContext.cs

@ -0,0 +1,14 @@
using System.Reflection;
namespace Volo.Abp.Features
{
public class MethodInvocationFeatureCheckerContext
{
public MethodInfo Method { get; }
public MethodInvocationFeatureCheckerContext(MethodInfo method)
{
Method = method;
}
}
}

59
framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerService.cs

@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public class MethodInvocationFeatureCheckerService : IMethodInvocationFeatureCheckerService, ITransientDependency
{
private readonly IFeatureChecker _featureChecker;
public MethodInvocationFeatureCheckerService(
IFeatureChecker featureChecker)
{
_featureChecker = featureChecker;
}
public async Task CheckAsync(MethodInvocationFeatureCheckerContext context)
{
if (IsFeatureCheckDisabled(context))
{
return;
}
foreach (var requiresFeatureAttribute in GetRequiredFeatureAttributes(context.Method))
{
await _featureChecker.CheckEnabledAsync(requiresFeatureAttribute.RequiresAll, requiresFeatureAttribute.Features);
}
}
protected virtual bool IsFeatureCheckDisabled(MethodInvocationFeatureCheckerContext context)
{
return context.Method
.GetCustomAttributes(true)
.OfType<DisableFeatureCheckAttribute>()
.Any();
}
protected virtual IEnumerable<RequiresFeatureAttribute> GetRequiredFeatureAttributes(MethodInfo methodInfo)
{
var attributes = methodInfo
.GetCustomAttributes(true)
.OfType<RequiresFeatureAttribute>();
if (methodInfo.IsPublic)
{
attributes = attributes
.Union(
methodInfo.DeclaringType
.GetCustomAttributes(true)
.OfType<RequiresFeatureAttribute>()
);
}
return attributes;
}
}
}

23
framework/src/Volo.Abp.Features/Volo/Abp/Features/NullFeatureStore.cs

@ -0,0 +1,23 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
[Dependency(TryRegister = true)]
public class NullFeatureStore : IFeatureStore, ISingletonDependency
{
public ILogger<NullFeatureStore> Logger { get; set; }
public NullFeatureStore()
{
Logger = NullLogger<NullFeatureStore>.Instance;
}
public Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{
return Task.FromResult((string) null);
}
}
}

33
framework/src/Volo.Abp.Features/Volo/Abp/Features/RequiresFeatureAttribute.cs

@ -0,0 +1,33 @@
using System;
namespace Volo.Abp.Features
{
/// <summary>
/// This attribute can be used on a class/method to declare that given class/method is available
/// only if required feature(s) are enabled.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequiresFeatureAttribute : Attribute
{
/// <summary>
/// A list of features to be checked if they are enabled.
/// </summary>
public string[] Features { get; }
/// <summary>
/// If this property is set to true, all of the <see cref="Features"/> must be enabled.
/// If it's false, at least one of the <see cref="Features"/> must be enabled.
/// Default: false.
/// </summary>
public bool RequiresAll { get; set; }
/// <summary>
/// Creates a new instance of <see cref="RequiresFeatureAttribute"/> class.
/// </summary>
/// <param name="features">A list of features to be checked if they are enabled</param>
public RequiresFeatureAttribute(params string[] features)
{
Features = features ?? Array.Empty<string>();
}
}
}

25
framework/src/Volo.Abp.Features/Volo/Abp/Features/TenantFeatureValueProvider.cs

@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Features
{
public class TenantFeatureValueProvider : FeatureValueProvider
{
public const string ProviderName = "Tenant";
public override string Name => ProviderName;
protected ICurrentTenant CurrentTenant { get; }
public TenantFeatureValueProvider(IFeatureStore featureStore, ICurrentTenant currentTenant)
: base(featureStore)
{
CurrentTenant = currentTenant;
}
public override async Task<string> GetOrNullAsync(FeatureDefinition feature)
{
return await FeatureStore.GetOrNullAsync(feature.Name, Name, CurrentTenant.Id?.ToString());
}
}
}

5
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs

@ -27,11 +27,6 @@ namespace Volo.Abp.Localization
.Add<AbpValidationResource>("en") .Add<AbpValidationResource>("en")
.AddVirtualJson("/Localization/Resources/AbpValidation"); .AddVirtualJson("/Localization/Resources/AbpValidation");
}); });
Configure<SettingOptions>(options =>
{
options.DefinitionProviders.Add<LocalizationSettingProvider>();
});
} }
} }
} }

1
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo.Abp.MultiTenancy.Abstractions.csproj

@ -14,7 +14,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Data\Volo.Abp.Data.csproj" /> <ProjectReference Include="..\Volo.Abp.Data\Volo.Abp.Data.csproj" />
<ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" /> <ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" />
</ItemGroup> </ItemGroup>

4
framework/src/Volo.Abp.MultiTenancy.Abstractions/Volo/Abp/MultiTenancy/AbpMultiTenancyAbstractionsModule.cs

@ -1,10 +1,12 @@
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Security;
namespace Volo.Abp.MultiTenancy namespace Volo.Abp.MultiTenancy
{ {
[DependsOn( [DependsOn(
typeof(AbpDataModule) typeof(AbpDataModule),
typeof(AbpSecurityModule)
)] )]
public class AbpMultiTenancyAbstractionsModule : AbpModule //TODO: Rename to AbpMultiTenancyModule? public class AbpMultiTenancyAbstractionsModule : AbpModule //TODO: Rename to AbpMultiTenancyModule?
{ {

0
framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationException.cs → framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs

29
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs

@ -1,8 +1,10 @@
using Volo.Abp.Localization; using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Security; using Volo.Abp.Security;
using Volo.Abp.Users;
namespace Volo.Abp.Settings namespace Volo.Abp.Settings
{ {
@ -13,6 +15,11 @@ namespace Volo.Abp.Settings
)] )]
public class AbpSettingsModule : AbpModule public class AbpSettingsModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
Configure<SettingOptions>(options => Configure<SettingOptions>(options =>
@ -23,5 +30,23 @@ namespace Volo.Abp.Settings
options.ValueProviders.Add<UserSettingValueProvider>(); options.ValueProviders.Add<UserSettingValueProvider>();
}); });
} }
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
{
if (typeof(ISettingDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
services.Configure<SettingOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
} }
} }

20
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs

@ -1,11 +1,11 @@
using System.Collections.Generic; using System.Threading.Tasks;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Settings namespace Volo.Abp.Settings
{ {
[Dependency(TryRegister = true)]
public class NullSettingStore : ISettingStore, ISingletonDependency public class NullSettingStore : ISettingStore, ISingletonDependency
{ {
public ILogger<NullSettingStore> Logger { get; set; } public ILogger<NullSettingStore> Logger { get; set; }
@ -19,21 +19,5 @@ namespace Volo.Abp.Settings
{ {
return Task.FromResult((string) null); return Task.FromResult((string) null);
} }
public Task SetAsync(string name, string value, string providerName, string providerKey)
{
Logger.LogWarning($"Setting the value for {name} is not possible because current setting store is {nameof(NullSettingStore)}");
return Task.CompletedTask;
}
public Task<List<SettingValue>> GetListAsync(string providerName, string providerKey)
{
return Task.FromResult(new List<SettingValue>());
}
public Task DeleteAsync(string name, string providerName, string providerKey)
{
return Task.CompletedTask;
}
} }
} }

2
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinition.cs

@ -40,7 +40,7 @@ namespace Volo.Abp.Settings
/// A list of allowed providers to get/set value of this setting. /// A list of allowed providers to get/set value of this setting.
/// An empty list indicates that all providers are allowed. /// An empty list indicates that all providers are allowed.
/// </summary> /// </summary>
public List<string> Providers { get; } public List<string> Providers { get; } //TODO: Rename to AllowedProviders
/// <summary> /// <summary>
/// Is this setting inherited from parent scopes. /// Is this setting inherited from parent scopes.

27
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs

@ -10,22 +10,19 @@ namespace Volo.Abp.Settings
{ {
public class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency public class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency
{ {
protected Lazy<List<ISettingDefinitionProvider>> Providers { get; }
protected Lazy<IDictionary<string, SettingDefinition>> SettingDefinitions { get; } protected Lazy<IDictionary<string, SettingDefinition>> SettingDefinitions { get; }
protected SettingOptions Options { get; } protected SettingOptions Options { get; }
private readonly IServiceProvider _serviceProvider; protected IServiceProvider ServiceProvider { get; }
public SettingDefinitionManager( public SettingDefinitionManager(
IOptions<SettingOptions> options, IOptions<SettingOptions> options,
IServiceProvider serviceProvider) IServiceProvider serviceProvider)
{ {
_serviceProvider = serviceProvider; ServiceProvider = serviceProvider;
Options = options.Value; Options = options.Value;
Providers = new Lazy<List<ISettingDefinitionProvider>>(CreateSettingProviders, true);
SettingDefinitions = new Lazy<IDictionary<string, SettingDefinition>>(CreateSettingDefinitions, true); SettingDefinitions = new Lazy<IDictionary<string, SettingDefinition>>(CreateSettingDefinitions, true);
} }
@ -53,21 +50,21 @@ namespace Volo.Abp.Settings
return SettingDefinitions.Value.GetOrDefault(name); return SettingDefinitions.Value.GetOrDefault(name);
} }
protected virtual List<ISettingDefinitionProvider> CreateSettingProviders()
{
return Options
.DefinitionProviders
.Select(p => _serviceProvider.GetRequiredService(p) as ISettingDefinitionProvider)
.ToList();
}
protected virtual IDictionary<string, SettingDefinition> CreateSettingDefinitions() protected virtual IDictionary<string, SettingDefinition> CreateSettingDefinitions()
{ {
var settings = new Dictionary<string, SettingDefinition>(); var settings = new Dictionary<string, SettingDefinition>();
foreach (var provider in Providers.Value) using (var scope = ServiceProvider.CreateScope())
{ {
provider.Define(new SettingDefinitionContext(settings)); var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as ISettingDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.Define(new SettingDefinitionContext(settings));
}
} }
return settings; return settings;

2
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs

@ -2,7 +2,7 @@
namespace Volo.Abp.Settings namespace Volo.Abp.Settings
{ {
public abstract class SettingDefinitionProvider : ISettingDefinitionProvider, ISingletonDependency public abstract class SettingDefinitionProvider : ISettingDefinitionProvider, ITransientDependency
{ {
public abstract void Define(ISettingDefinitionContext context); public abstract void Define(ISettingDefinitionContext context);
} }

2
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingProvider.cs

@ -29,7 +29,7 @@ namespace Volo.Abp.Settings
Providers = new Lazy<List<ISettingValueProvider>>( Providers = new Lazy<List<ISettingValueProvider>>(
() => Options () => Options
.ValueProviders .ValueProviders
.Select(c => serviceProvider.GetRequiredService(c) as ISettingValueProvider) .Select(type => serviceProvider.GetRequiredService(type) as ISettingValueProvider)
.ToList(), .ToList(),
true true
); );

14
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/AlwaysValidValueValidator.cs

@ -0,0 +1,14 @@
using System;
namespace Volo.Abp.Validation.StringValues
{
[Serializable]
[ValueValidator("NULL")]
public class AlwaysValidValueValidator : ValueValidatorBase
{
public override bool IsValid(object value)
{
return true;
}
}
}

24
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/BooleanValueValidator.cs

@ -0,0 +1,24 @@
using System;
namespace Volo.Abp.Validation.StringValues
{
[Serializable]
[ValueValidator("BOOLEAN")]
public class BooleanValueValidator : ValueValidatorBase
{
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
if (value is bool)
{
return true;
}
return bool.TryParse(value.ToString(), out _);
}
}
}

19
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/StringValues/FreeTextStringValueType.cs

@ -0,0 +1,19 @@
using System;
namespace Volo.Abp.Validation.StringValues
{
[Serializable]
[StringValueType("FREE_TEXT")]
public class FreeTextStringValueType : StringValueTypeBase
{
public FreeTextStringValueType()
{
}
public FreeTextStringValueType(IValueValidator validator)
: base(validator)
{
}
}
}

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

Loading…
Cancel
Save