Browse Source

Merge branch 'Translate'

pull/4228/head
maliming 8 years ago
parent
commit
05f6bdcdd6
  1. 1
      abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs
  2. 4
      abp_io/src/Volo.AbpWebSite.Web/Pages/Templates.cshtml
  3. 1
      abp_io/src/Volo.AbpWebSite.Web/Templates/MvcApplicationTemplate.cs
  4. 20
      abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildContextExtensions.cs
  5. 17
      abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildPipelineStep.cs
  6. 2
      abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/RemoveProjectFromSolutionStep.cs
  7. 8
      abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs
  8. 2
      docs/en/Dependency-Injection.md
  9. 10
      docs/en/Getting-Started-AspNetCore-MVC-Template.md
  10. 6
      docs/en/Tutorials/AspNetCore-Mvc/Part-I.md
  11. 2
      docs/en/Tutorials/AspNetCore-Mvc/Part-III.md
  12. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png
  13. BIN
      docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png
  14. BIN
      docs/en/images/bookstore-visual-studio-solution-v2.png
  15. BIN
      docs/en/images/pcm-update-database-v2.png
  16. 10
      docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md
  17. 705
      docs/zh-Hans/Samples/Microservice-Demo.md
  18. 4
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md
  19. 1
      docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md
  20. BIN
      docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png
  21. BIN
      docs/zh-Hans/images/pcm-update-database-v2.png
  22. 26
      framework/Volo.Abp.sln
  23. 24
      framework/src/Volo.Abp.AspNetCore.Mvc.Client/Volo/Abp/AspNetCore/Mvc/Client/RemoteFeatureChecker.cs
  24. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationConfigurationDto.cs
  25. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ApplicationFeatureConfigurationDto.cs
  26. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs
  27. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs
  28. 17
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
  29. 0
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Layout/IPageLayout.cs
  30. 15
      framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs
  31. 8
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs
  32. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs
  33. 27
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/AbpApplicationConfigurationAppService.cs
  34. 42
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeatureActionFilter.cs
  35. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowActionFilter.cs
  36. 23
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AbpAuthorizationModule.cs
  37. 26
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs
  38. 7
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/IAuthorizationEnabled.cs
  39. 25
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs
  40. 6
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionDefinitionProvider.cs
  41. 3
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/IPermissionValueProvider.cs
  42. 39
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionManager.cs
  43. 6
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionDefinitionProvider.cs
  44. 3
      framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/Permissions/PermissionValueProvider.cs
  45. 1
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs
  46. 23
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs
  47. 1
      framework/src/Volo.Abp.Core/Volo/Abp/Aspects/AbpCrossCuttingConcerns.cs
  48. 1
      framework/src/Volo.Abp.Ddd.Application/Volo.Abp.Ddd.Application.csproj
  49. 7
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs
  50. 4
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs
  51. 5
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs
  52. 4
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo.Abp.EntityFrameworkCore.MySQL.csproj
  53. 8
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextConfigurationContextMySQLExtensions.cs
  54. 6
      framework/src/Volo.Abp.EntityFrameworkCore.MySQL/Volo/Abp/EntityFrameworkCore/AbpDbContextOptionsMySQLExtensions.cs
  55. 8
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs
  56. 13
      framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs
  57. 1
      framework/src/Volo.Abp.Features/Volo.Abp.Features.csproj
  58. 40
      framework/src/Volo.Abp.Features/Volo/Abp/Features/AbpFeaturesModule.cs
  59. 22
      framework/src/Volo.Abp.Features/Volo/Abp/Features/DefaultValueSettingValueProvider.cs
  60. 10
      framework/src/Volo.Abp.Features/Volo/Abp/Features/DisableFeatureCheckAttribute.cs
  61. 32
      framework/src/Volo.Abp.Features/Volo/Abp/Features/EditionFeatureValueProvider.cs
  62. 42
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureChecker.cs
  63. 32
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerBase.cs
  64. 114
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureCheckerExtensions.cs
  65. 34
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinition.cs
  66. 43
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionContext.cs
  67. 73
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureDefinitionManager.cs
  68. 111
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureGroupDefinition.cs
  69. 56
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptor.cs
  70. 36
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs
  71. 2
      framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureValueProvider.cs
  72. 11
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IFeatureDefinitionContext.cs
  73. 11
      framework/src/Volo.Abp.Features/Volo/Abp/Features/IMethodInvocationFeatureCheckerService.cs
  74. 14
      framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerContext.cs
  75. 59
      framework/src/Volo.Abp.Features/Volo/Abp/Features/MethodInvocationFeatureCheckerService.cs
  76. 1
      framework/src/Volo.Abp.Features/Volo/Abp/Features/NullFeatureStore.cs
  77. 33
      framework/src/Volo.Abp.Features/Volo/Abp/Features/RequiresFeatureAttribute.cs
  78. 2
      framework/src/Volo.Abp.Features/Volo/Abp/Features/TenantFeatureValueProvider.cs
  79. 1
      framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs
  80. 50
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs
  81. 8
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpLocalizationModule.cs
  82. 58
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs
  83. 10
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs
  84. 28
      framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs
  85. 0
      framework/src/Volo.Abp.Security/Volo/Abp/Authorization/AbpAuthorizationException.cs
  86. 8
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs
  87. 28
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/AbpSettingsModule.cs
  88. 1
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/NullSettingStore.cs
  89. 27
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionManager.cs
  90. 2
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingDefinitionProvider.cs
  91. 6
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs
  92. 2
      framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValueProvider.cs
  93. 7
      framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj
  94. 24
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs
  95. 13
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs
  96. 59
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs
  97. 10
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs
  98. 46
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItemList.cs
  99. 34
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs
  100. 13
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuNames.cs

1
abp_io/src/Volo.AbpWebSite.Web/AbpWebSiteWebModule.cs

@ -69,7 +69,6 @@ namespace Volo.AbpWebSite
Configure<BlogFileOptions>(options =>
{
options.FileUploadLocalFolder = Path.Combine(hostingEnvironment.WebRootPath, "files");
options.FileUploadUrlRoot = "/files/";
});
}

4
abp_io/src/Volo.AbpWebSite.Web/Pages/Templates.cshtml

@ -14,7 +14,7 @@
@using (Html.BeginForm(FormMethod.Post))
{
<div class="row border">
<div class="col p-5 ">
<div class="col-lg-7 p-5 ">
<h2>
<span class="text-primary">Create</span>
<span class="text-success">New Project</span>
@ -64,7 +64,7 @@
</div>
</div>
<div class="col-md-5 bg-light p-5">
<div class="col-lg-5 bg-light p-5">
<h4>The Startup Project</h4>
<p>See <a href="/documents/abp/latest/Getting-Started-AspNetCore-MVC-Template" target="_blank">the documentation</a> to understand how to run the downloaded project.</p>

1
abp_io/src/Volo.AbpWebSite.Web/Templates/MvcApplicationTemplate.cs

@ -37,6 +37,7 @@ namespace Volo.AbpWebSite.Templates
if (context.Request.DatabaseProvider != DatabaseProvider.EntityFrameworkCore)
{
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.EntityFrameworkCore.DbMigrations"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Application.Tests", projectFolderPath: "test/MyCompanyName.MyProjectName.Application.Tests"));
steps.Add(new RemoveProjectFromSolutionStep("MyCompanyName.MyProjectName.Web.Tests", projectFolderPath: "test/MyCompanyName.MyProjectName.Web.Tests"));
}

20
abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildContextExtensions.cs

@ -0,0 +1,20 @@
using System;
using System.Linq;
using Volo.Utils.SolutionTemplating.Files;
namespace Volo.Utils.SolutionTemplating.Building
{
public static class ProjectBuildContextExtensions
{
public static FileEntry GetFile(this ProjectBuildContext context, string filePath)
{
var file = context.Files.FirstOrDefault(f => f.Name == filePath);
if (file == null)
{
throw new ApplicationException("Could not find file: " + filePath);
}
return file;
}
}
}

17
abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/ProjectBuildPipelineStep.cs

@ -1,22 +1,7 @@
using System;
using System.Linq;
using Volo.Utils.SolutionTemplating.Files;
namespace Volo.Utils.SolutionTemplating.Building
namespace Volo.Utils.SolutionTemplating.Building
{
public abstract class ProjectBuildPipelineStep
{
public abstract void Execute(ProjectBuildContext context);
protected FileEntry GetFile(ProjectBuildContext context, string filePath)
{
var file = context.Files.FirstOrDefault(f => f.Name == filePath);
if (file == null)
{
throw new ApplicationException("Could not find file: " + filePath);
}
return file;
}
}
}

2
abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/RemoveProjectFromSolutionStep.cs

@ -23,7 +23,7 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
public override void Execute(ProjectBuildContext context)
{
context.Files.RemoveAll(file => file.Name.StartsWith(_projectFolderPath));
var solutionFile = GetFile(context, _solutionFilePath);
var solutionFile = context.GetFile(_solutionFilePath);
solutionFile.NormalizeLineEndings();
solutionFile.SetLines(RemoveProject(solutionFile.GetLines().ToList()));
}

8
abp_io/src/Volo.Utils.SolutionTemplating/Volo/Utils/SolutionTemplating/Building/Steps/SwitchEntityFrameworkCoreToMongoDbStep.cs

@ -14,7 +14,7 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
private void ChangeProjectReference(ProjectBuildContext context)
{
var file = GetFile(context, "/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj");
var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/MyCompanyName.MyProjectName.Web.csproj");
file.NormalizeLineEndings();
@ -29,12 +29,12 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
}
}
throw new ApplicationException("Could not find the 'Default' connection string in appsettings.json file!");
throw new ApplicationException("Could not find the EntityFrameworkCore reference in the MyCompanyName.MyProjectName.Web.csproj!");
}
private void ChangeWebModuleUsage(ProjectBuildContext context)
{
var file = GetFile(context, "/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs");
var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/MyProjectNameWebModule.cs");
file.NormalizeLineEndings();
@ -59,7 +59,7 @@ namespace Volo.Utils.SolutionTemplating.Building.Steps
private void ChangeConnectionString(ProjectBuildContext context)
{
var file = GetFile(context, "/src/MyCompanyName.MyProjectName.Web/appsettings.json");
var file = context.GetFile("/src/MyCompanyName.MyProjectName.Web/appsettings.json");
file.NormalizeLineEndings();

2
docs/en/Dependency-Injection.md

@ -170,7 +170,7 @@ public class BlogModule : AbpModule
There are three common ways of using a service that has already been registered.
### Contructor Injection
### Constructor Injection
This is the most common way of injecting a service into a class. For example:

10
docs/en/Getting-Started-AspNetCore-MVC-Template.md

@ -19,9 +19,9 @@ The downloaded project requires;
### The Solution Structure
Extract the zip file downloaded and open in **Visual Studio 2017 (15.7.0+)**:
Extract the zip file downloaded and open in **Visual Studio 2017 (15.9.0+)**:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png)
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
The solution has a layered structure (based on Domain Driven Design) where;
@ -30,6 +30,8 @@ The solution has a layered structure (based on Domain Driven Design) where;
* ``.Web`` is the presentation layer.
* ``.EntityFrameworkCore`` is the EF Core integration package.
EF Core database migrations are separated to a project named `.EntityFrameworkCore.DbMigrations`.
The solution also contains unit & integration test projects properly configured to work with **EF Core** & **SQLite in-memory** database.
### Creating the Database
@ -50,9 +52,9 @@ Right click to the `.Web` project and select **Set as StartUp Project**:
![set-as-startup-project](images/set-as-startup-project.png)
Open the **Package Manager Console**, select `.EntityFrameworkCore` project as the **Default Project** and run the `Update-Database` command:
Open the **Package Manager Console**, select `.EntityFrameworkCore.DbMigrations` project as the **Default Project** and run the `Update-Database` command:
![pcm-update-database](images/pcm-update-database.png)
![pcm-update-database](images/pcm-update-database-v2.png)
This will create a new database based on the configured connection string.

6
docs/en/Tutorials/AspNetCore-Mvc/Part-I.md

@ -20,7 +20,7 @@ Go to the [startup template page](https://abp.io/Templates) and download a new p
This is the how the layered solution structure looks after it's created from the startup template:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png)
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
### Create the Book Entity
@ -91,9 +91,9 @@ EF Core requires you to relate entities with your DbContext. The easiest way to
#### Add New Migration & Update the Database
The Startup template uses [EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) to create and maintain the database schema. Open the **Package Manager Console (PMC)** (under the *Tools/Nuget Package Manager* menu), select the `Acme.BookStore.EntityFrameworkCore` as the **default project** and execute the following command:
The Startup template uses [EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) to create and maintain the database schema. Open the **Package Manager Console (PMC)** (under the *Tools/Nuget Package Manager* menu), select the `Acme.BookStore.EntityFrameworkCore.DbMigrations` as the **default project** and execute the following command:
![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration.png)
![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration-v2.png)
This will create a new migration class inside the `Migrations` folder. Then execute the `Update-Database` command to update the database schema:

2
docs/en/Tutorials/AspNetCore-Mvc/Part-III.md

@ -14,8 +14,6 @@ You can download the **source code** of the application [from here](https://gith
There are two test projects in the solution:
![bookstore-test-projects](images/bookstore-test-projects.png)
* `Acme.BookStore.Application.Tests` is for unit & integration tests. You can write tests for application service methods. It uses **EF Core SQLite in-memory** database.
* `Acme.BookStore.Web.Tests` is for full stack integration tests including the web layer. So, you can write tests for UI pages too.

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-pmc-add-book-migration-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
docs/en/Tutorials/AspNetCore-Mvc/images/bookstore-visual-studio-solution-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/en/images/bookstore-visual-studio-solution-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/en/images/pcm-update-database-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

10
docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md

@ -12,14 +12,14 @@
下载的项目需要:
* [Visual Studio 2017 (v15.7.0+)](https://visualstudio.microsoft.com/tr/downloads/)
* [Visual Studio 2017 (v15.9.0+)](https://visualstudio.microsoft.com/tr/downloads/)
* [.NET Core 2.1.1+](https://www.microsoft.com/net/download/dotnet-core/)
### 解决方案结构
下载后解压文件并在 **Visual Studio 2017(15.7.0 +)** 中打开:
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png)
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
该解决方案具有分层结构(基于域驱动设计), 其中:
@ -28,6 +28,8 @@
* ``.Web`` 为是表示层.
* ``.EntityFrameworkCore`` 是EF Core集成.
EF Core 数据库迁移被分离到名为`.EntityFrameworkCore.DbMigrations`项目中.
解决方案还包含配置好的的单元&集成测试项目, 以便与 **EF Core** 和 **SQLite内存中** 数据库配合使用.
### 创建数据库
@ -48,9 +50,9 @@
![set-as-startup-project](images/set-as-startup-project.png)
打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore`项目作为**默认项目**并运行`Update-Database`命令:
打开**包管理器控制台(Package Manager Console)**, 选择`.EntityFrameworkCore.DbMigrations`项目作为**默认项目**并运行`Update-Database`命令:
![pcm-update-database](images/pcm-update-database.png)
![pcm-update-database](images/pcm-update-database-v2.png)
这将基于配置的连接字符串创建新数据库.

705
docs/zh-Hans/Samples/Microservice-Demo.md

File diff suppressed because it is too large

4
docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-I.md

@ -20,7 +20,7 @@
下面的图片展示了从启动模板创建的项目是如何分层的.
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution.png)
![bookstore-visual-studio-solution](images/bookstore-visual-studio-solution-v2.png)
### 创建Book实体
@ -93,7 +93,7 @@ public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
这个启动模板使用了[EF Core Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/)来创建并维护数据库结构.打开 **Package Manager Console (PMC)** (工具/Nuget包管理器菜单),选择 `Acme.BookStore.EntityFrameworkCore`作为默认的项目然后执行下面的命令:
![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration.png)
![bookstore-pmc-add-book-migration](images/bookstore-pmc-add-book-migration-v2.png)
这样就会在`Migrations`文件夹中创建一个新的migration类.然后执行`Update-Database`命令更新数据库结构.

1
docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-III.md

@ -14,7 +14,6 @@
本解决方案中有两个测试项目:
![bookstore-test-projects](images/bookstore-test-projects.png)
* `Acme.BookStore.Application.Tests` 项目用于单元测试和集成测试.你可以在这个项目中为Application Service方法写测试代码.这个项目使用了 **EF Core SQLite in-memory** 数据库.
* `Acme.BookStore.Web.Tests` 项目用于包含Web层的完整集成测试.所以,你也可以在这里写关于UI页面的测试.

BIN
docs/zh-Hans/images/bookstore-visual-studio-solution-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
docs/zh-Hans/images/pcm-update-database-v2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

26
framework/Volo.Abp.sln

@ -5,23 +5,8 @@ VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CBCC288A-53C3-402F-99F7-E468738560F5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{447C8A77-E5F0-4538-8687-7383196D04EA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker-files", "docker-files", "{0BEA55D6-E0B8-40DD-A256-B34C4DD990A5}"
ProjectSection(SolutionItems) = preProject
..\docker\docker-compose.yml = ..\docker\docker-compose.yml
..\docker\down.ps1 = ..\docker\down.ps1
..\docker\haproxy.cfg = ..\docker\haproxy.cfg
..\docker\up.ps1 = ..\docker\up.ps1
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6D893E48-0739-401A-9AD1-286BCB6E5517}"
ProjectSection(SolutionItems) = preProject
..\build\build.ps1 = ..\build\build.ps1
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AbpTestBase", "test\AbpTestBase\AbpTestBase.csproj", "{1020F5FD-6A97-40C2-AFCA-EBDF641DF111}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore", "src\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj", "{02BE03BA-3411-448C-AB61-CB36407CC49A}"
@ -226,7 +211,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.Cli
EndProject
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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Features", "src\Volo.Abp.Features\Volo.Abp.Features.csproj", "{01E3D389-8872-4EB1-9D3D-13B6ED54DE0E}"
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
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -646,13 +633,15 @@ Global
{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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0BEA55D6-E0B8-40DD-A256-B34C4DD990A5} = {CBCC288A-53C3-402F-99F7-E468738560F5}
{6D893E48-0739-401A-9AD1-286BCB6E5517} = {CBCC288A-53C3-402F-99F7-E468738560F5}
{1020F5FD-6A97-40C2-AFCA-EBDF641DF111} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{02BE03BA-3411-448C-AB61-CB36407CC49A} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{B1D860BB-6EC6-4BAE-ADAA-C2AEC2FFB510} = {447C8A77-E5F0-4538-8687-7383196D04EA}
@ -756,6 +745,7 @@ Global
{E803DDB8-81EA-454B-9A66-9C2941100B67} = {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
GlobalSection(ExtensibilityGlobals) = postSolution
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);
}
}
}

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 CurrentUserDto CurrentUser { get; set; }
public ApplicationFeatureConfigurationDto Features { get; set; }
}
}

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

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
{
[Serializable]
public class ApplicationFeatureConfigurationDto
{
public Dictionary<string, string> Values { get; set; }
public ApplicationFeatureConfigurationDto()
{
Values = new Dictionary<string, string>();
}
}
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/AbpAspNetCoreMvcUiBootstrapModule.cs

@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/AbpAspNetCoreMvcUiThemeSharedModule.cs

@ -1,5 +1,4 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;

17
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js

@ -338,6 +338,23 @@
}
}
configuration.language = {
info: localize("PagerInfo"),
infoFiltered: localize("PagerInfoFiltered"),
infoEmpty: localize("PagerInfoEmpty"),
search: localize("PagerSearch"),
processing: localize("ProcessingWithThreeDot"),
loadingRecords: localize("LoadingWithThreeDot"),
lengthMenu: localize("PagerShowMenuEntries"),
emptyTable: localize("NoDataAvailableInDatatable"),
paginate: {
first: localize("PagerFirst"),
last: localize("PagerLast"),
previous: localize("PagerPrevious"),
next: localize("PagerNext")
}
};
configuration.dom = '<"dataTable_filters"f>rt<"row dataTable_footer"<"col-auto"l><"col-auto"i><"col"p>>';
return configuration;

0
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Layout/ILayoutManager.cs → framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/Layout/IPageLayout.cs

15
framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Http/AbpFormFileExtensions.cs

@ -0,0 +1,15 @@
using System.IO;
namespace Microsoft.AspNetCore.Http
{
public static class AbpFormFileExtensions
{
public static byte[] GetAllBytes(this IFormFile file)
{
using (var stream = file.OpenReadStream())
{
return stream.GetAllBytes();
}
}
}
}

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

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Aspects;
using Volo.Abp.AspNetCore.Mvc.Validation;
using Volo.Abp.Features;
using Volo.Abp.Guids;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping;
@ -28,12 +30,16 @@ namespace Volo.Abp.AspNetCore.Mvc
public ICurrentTenant CurrentTenant { get; set; }
public IAuthorizationService AuthorizationService { get; set; }
protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current;
public IClock Clock { get; set; }
public IModelStateValidator ModelValidator { get; set; }
public IFeatureChecker FeatureChecker { get; set; }
public List<string> AppliedCrossCuttingConcerns { get; } = new List<string>();
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.Conventions;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.Features;
using Volo.Abp.AspNetCore.Mvc.Uow;
using Volo.Abp.AspNetCore.Mvc.Validation;
@ -26,6 +27,7 @@ namespace Volo.Abp.AspNetCore.Mvc
private static void AddFilters(MvcOptions options)
{
options.Filters.AddService(typeof(AbpAuditActionFilter));
options.Filters.AddService(typeof(AbpFeatureActionFilter));
options.Filters.AddService(typeof(AbpValidationActionFilter));
options.Filters.AddService(typeof(AbpUowActionFilter));
options.Filters.AddService(typeof(AbpExceptionFilter));

27
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 System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Authorization;
using Volo.Abp.Features;
using Volo.Abp.Localization;
using Volo.Abp.Settings;
using Volo.Abp.Users;
@ -23,6 +23,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
private readonly ICurrentUser _currentUser;
private readonly ISettingProvider _settingProvider;
private readonly ISettingDefinitionManager _settingDefinitionManager;
private readonly IFeatureDefinitionManager _featureDefinitionManager;
public AbpApplicationConfigurationAppService(
IOptions<AbpLocalizationOptions> localizationOptions,
@ -31,7 +32,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
IAuthorizationService authorizationService,
ICurrentUser currentUser,
ISettingProvider settingProvider,
SettingDefinitionManager settingDefinitionManager)
SettingDefinitionManager settingDefinitionManager,
IFeatureDefinitionManager featureDefinitionManager)
{
_serviceProvider = serviceProvider;
_abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider;
@ -39,16 +41,18 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
_currentUser = currentUser;
_settingProvider = settingProvider;
_settingDefinitionManager = settingDefinitionManager;
_featureDefinitionManager = featureDefinitionManager;
_localizationOptions = localizationOptions.Value;
}
public async Task<ApplicationConfigurationDto> GetAsync()
public virtual async Task<ApplicationConfigurationDto> GetAsync()
{
//TODO: Optimize & cache..?
return new ApplicationConfigurationDto
{
Auth = await GetAuthConfigAsync(),
Features = await GetFeaturesConfigAsync(),
Localization = GetLocalizationConfig(),
CurrentUser = GetCurrentUser(),
Setting = await GetSettingConfigAsync()
@ -126,5 +130,22 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations
return result;
}
protected virtual async Task<ApplicationFeatureConfigurationDto> GetFeaturesConfigAsync()
{
var result = new ApplicationFeatureConfigurationDto();
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
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
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 Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
@ -16,6 +18,7 @@ namespace Volo.Abp.Authorization
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded);
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
@ -31,5 +34,23 @@ namespace Volo.Abp.Authorization
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
{
@ -6,10 +10,28 @@ namespace Volo.Abp.Authorization
{
public static void RegisterIfNeeded(IOnServiceRegistredContext context)
{
if (typeof(IAuthorizationEnabled).IsAssignableFrom(context.ImplementationType))
if (ShouldIntercept(context.ImplementationType))
{
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 Microsoft.AspNetCore.Authorization;
using Volo.Abp.Clients;
@ -30,8 +32,7 @@ namespace Volo.Abp.Authorization
return;
}
var authorizationAttributes = GetAuthorizationDataAttributes(context);
foreach (var authorizationAttribute in authorizationAttributes)
foreach (var authorizationAttribute in GetAuthorizationDataAttributes(context.Method))
{
await CheckAsync(authorizationAttribute);
}
@ -42,17 +43,23 @@ namespace Volo.Abp.Authorization
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)
.OfType<IAuthorizeData>();
var methodAttributes = context.Method
.GetCustomAttributes(true)
.OfType<IAuthorizeData>();
if (methodInfo.IsPublic)
{
attributes = attributes
.Union(
methodInfo.DeclaringType
.GetCustomAttributes(true)
.OfType<IAuthorizeData>()
);
}
return classAttributes.Union(methodAttributes).ToArray();
return attributes;
}
protected async Task CheckAsync(IAuthorizeData authorizationAttribute)

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

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

@ -1,9 +1,8 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions
{
public interface IPermissionValueProvider : ISingletonDependency
public interface IPermissionValueProvider
{
string Name { get; }

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
{
protected List<IPermissionDefinitionProvider> Providers => _lazyProviders.Value;
private readonly Lazy<List<IPermissionDefinitionProvider>> _lazyProviders;
protected IDictionary<string, PermissionGroupDefinition> PermissionGroupDefinitions => _lazyPermissionGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, PermissionGroupDefinition>> _lazyPermissionGroupDefinitions;
@ -30,9 +27,15 @@ namespace Volo.Abp.Authorization.Permissions
_serviceProvider = serviceProvider;
Options = options.Value;
_lazyProviders = new Lazy<List<IPermissionDefinitionProvider>>(CreatePermissionProviders, true);
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(CreatePermissionDefinitions, true);
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(CreatePermissionGroupDefinitions, true);
_lazyPermissionDefinitions = new Lazy<Dictionary<string, PermissionDefinition>>(
CreatePermissionDefinitions,
isThreadSafe: true
);
_lazyPermissionGroupDefinitions = new Lazy<Dictionary<string, PermissionGroupDefinition>>(
CreatePermissionGroupDefinitions,
isThreadSafe: true
);
}
public virtual PermissionDefinition Get(string name)
@ -64,14 +67,6 @@ namespace Volo.Abp.Authorization.Permissions
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()
{
var permissions = new Dictionary<string, PermissionDefinition>();
@ -87,7 +82,9 @@ namespace Volo.Abp.Authorization.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))
{
@ -106,9 +103,17 @@ namespace Volo.Abp.Authorization.Permissions
{
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;

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

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

@ -1,8 +1,9 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions
{
public abstract class PermissionValueProvider : IPermissionValueProvider
public abstract class PermissionValueProvider : IPermissionValueProvider, ISingletonDependency //TODO: to transient?
{
public abstract string Name { get; }

1
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs

@ -10,7 +10,6 @@ namespace Volo.Abp.BackgroundWorkers
)]
public class AbpBackgroundWorkersModule : AbpModule
{
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<BackgroundWorkerOptions>>().Value;

23
framework/src/Volo.Abp.Core/System/Collections/Generic/AbpListExtensions.cs

@ -32,6 +32,18 @@ namespace System.Collections.Generic
source.Insert(source.Count, item);
}
public static void InsertAfter<T>(this IList<T> source, T existingItem, T item)
{
var index = source.IndexOf(existingItem);
if (index < 0)
{
source.AddFirst(item);
return;
}
source.Insert(index + 1, item);
}
public static void InsertAfter<T>(this IList<T> source, Predicate<T> selector, T item)
{
var index = source.FindIndex(selector);
@ -44,6 +56,17 @@ namespace System.Collections.Generic
source.Insert(index + 1, item);
}
public static void InsertBefore<T>(this IList<T> source, T existingItem, T item)
{
var index = source.IndexOf(existingItem);
if (index < 0)
{
source.AddLast(item);
return;
}
source.Insert(index, item);
}
public static void InsertBefore<T>(this IList<T> source, Predicate<T> selector, T item)
{

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 UnitOfWork = "AbpUnitOfWork";
public const string Authorization = "AbpAuthorization";
public const string FeatureChecking = "AbpFeatureChecking";
public static void AddApplied(object obj, params string[] concerns)
{

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

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

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)
{
Configure<SettingOptions>(options =>
{
options.DefinitionProviders.Add<EmailSettingProvider>();
});
Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpEmailingModule>();

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

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

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

@ -1,7 +1,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using MySql.Data.EntityFrameworkCore.Infraestructure;
using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
namespace Volo.Abp.EntityFrameworkCore
@ -10,15 +10,15 @@ namespace Volo.Abp.EntityFrameworkCore
{
public static DbContextOptionsBuilder UseMySQL(
[NotNull] this AbpDbContextConfigurationContext context,
[CanBeNull] Action<MySQLDbContextOptionsBuilder> mySQLOptionsAction = null)
[CanBeNull] Action<MySqlDbContextOptionsBuilder> mySQLOptionsAction = null)
{
if (context.ExistingConnection != null)
{
return context.DbContextOptions.UseMySQL(context.ExistingConnection, mySQLOptionsAction);
return context.DbContextOptions.UseMySql(context.ExistingConnection, mySQLOptionsAction);
}
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 MySql.Data.EntityFrameworkCore.Infraestructure;
using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace Volo.Abp.EntityFrameworkCore
{
@ -8,7 +8,7 @@ namespace Volo.Abp.EntityFrameworkCore
{
public static void UseMySQL(
[NotNull] this AbpDbContextOptions options,
[CanBeNull] Action<MySQLDbContextOptionsBuilder> mySQLOptionsAction = null)
[CanBeNull] Action<MySqlDbContextOptionsBuilder> mySQLOptionsAction = null)
{
options.Configure(context =>
{
@ -18,7 +18,7 @@ namespace Volo.Abp.EntityFrameworkCore
public static void UseMySQL<TDbContext>(
[NotNull] this AbpDbContextOptions options,
[CanBeNull] Action<MySQLDbContextOptionsBuilder> mySQLOptionsAction = null)
[CanBeNull] Action<MySqlDbContextOptionsBuilder> mySQLOptionsAction = null)
where TDbContext : AbpDbContext<TDbContext>
{
options.Configure<TDbContext>(context =>

8
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/AbpEventBusRabbitMqModule.cs

@ -15,5 +15,13 @@ namespace Volo.Abp.EventBus.RabbitMq
Configure<RabbitMqEventBusOptions>(configuration.GetSection("RabbitMQ:EventBus"));
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
context
.ServiceProvider
.GetRequiredService<RabbitMqDistributedEventBus>()
.Initialize();
}
}
}

13
framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.EventBus.RabbitMq
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; }
protected ConcurrentDictionary<string, Type> EventTypes { get; }
protected IRabbitMqMessageConsumerFactory MessageConsumerFactory { get; }
protected IRabbitMqMessageConsumer Consumer { get; }
protected IRabbitMqMessageConsumer Consumer { get; private set; }
public RabbitMqDistributedEventBus(
IOptions<RabbitMqEventBusOptions> options,
@ -50,18 +50,23 @@ namespace Volo.Abp.EventBus.RabbitMq
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>();
Initialize();
}
public void Initialize()
{
Consumer = MessageConsumerFactory.Create(
new ExchangeDeclareConfiguration(
RabbitMqEventBusOptions.ExchangeName,
RabbitMqEventBusOptions.ExchangeName,
type: "direct",
durable: true
),
),
new QueueDeclareConfiguration(
RabbitMqEventBusOptions.ClientName,
durable: true,
exclusive: false,
autoDelete: false
),
),
RabbitMqEventBusOptions.ConnectionName
);

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

@ -16,6 +16,7 @@
<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>

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

@ -1,18 +1,52 @@
using Volo.Abp.Localization;
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(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<EditionFeatureValueProvider>();
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 //TODO: Directly implement IFeatureValueProvider
{
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
{
}
}

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

@ -0,0 +1,32 @@
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.Features
{
public class EditionFeatureValueProvider : FeatureValueProvider
{
public const string ProviderName = "Edition";
public override string Name => ProviderName;
protected ICurrentPrincipalAccessor PrincipalAccessor;
public EditionFeatureValueProvider(IFeatureStore featureStore, ICurrentPrincipalAccessor principalAccessor)
: base(featureStore)
{
PrincipalAccessor = principalAccessor;
}
public override async Task<string> GetOrNullAsync(FeatureDefinition feature)
{
var editionId = PrincipalAccessor.Principal?.FindEditionId();
if (editionId == null)
{
return null;
}
return await FeatureStore.GetOrNullAsync(feature.Name, Name, editionId.Value.ToString("N"));
}
}
}

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

@ -4,39 +4,42 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public class FeatureChecker : IFeatureChecker, ITransientDependency
public class FeatureChecker : FeatureCheckerBase
{
protected IFeatureDefinitionManager FeatureDefinitionManager { get; }
protected Lazy<List<IFeatureValueProvider>> Providers { get; }
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>>(
_providers = new Lazy<List<IFeatureValueProvider>>(
() => Options
.ValueProviders
.Select(type => serviceProvider.GetRequiredService(type) as IFeatureValueProvider)
.Select(type => ServiceProvider.GetRequiredService(type) as IFeatureValueProvider)
.ToList(),
true
);
}
public virtual async Task<string> GetOrNullAsync(string name)
public override async Task<string> GetOrNullAsync(string name)
{
var featureDefinition = FeatureDefinitionManager.Get(name);
var providers = Enumerable
.Reverse(Providers.Value);
.Reverse(Providers);
if (featureDefinition.AllowedProviders.Any())
{
@ -46,27 +49,6 @@ namespace Volo.Abp.Features
return await GetOrNullValueFromProvidersAsync(providers, featureDefinition);
}
public 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
);
}
}
protected virtual async Task<string> GetOrNullValueFromProvidersAsync(
IEnumerable<IFeatureValueProvider> providers,
FeatureDefinition feature)

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

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

@ -1,13 +1,18 @@
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)
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));
@ -17,21 +22,122 @@ namespace Volo.Abp.Features
return value?.To<T>() ?? defaultValue;
}
public static string GetOrNull([NotNull] this IFeatureChecker featureChecker, [NotNull] string name)
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)
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)
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));
}
}
}

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

@ -3,6 +3,7 @@ using System.Collections.Immutable;
using System.Linq;
using JetBrains.Annotations;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.Features
{
@ -29,6 +30,7 @@ namespace Volo.Abp.Features
/// 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>
@ -53,28 +55,52 @@ namespace Volo.Abp.Features
/// 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; }
//TODO: Implement input type like old ABP!
/// <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;
IsVisibleToClients = isVisibleToClients;
DisplayName = displayName ?? new FixedLocalizableString(name);
Description = description;
ValueType = valueType;
IsVisibleToClients = isVisibleToClients;
Properties = new Dictionary<string, object>();
AllowedProviders = new List<string>();
@ -114,13 +140,15 @@ namespace Volo.Abp.Features
string defaultValue = null,
ILocalizableString displayName = null,
ILocalizableString description = null,
IStringValueType valueType = null,
bool isVisibleToClients = true)
{
var feature = new FeatureDefinition(
name,
defaultValue,
displayName,
description,
description,
valueType,
isVisibleToClients)
{
Parent = this

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

@ -1,38 +1,51 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Volo.Abp.Localization;
namespace Volo.Abp.Features
{
public class FeatureDefinitionContext : IFeatureDefinitionContext
{
protected Dictionary<string, FeatureDefinition> Features { get; }
internal Dictionary<string, FeatureGroupDefinition> Groups { get; }
public FeatureDefinitionContext(Dictionary<string, FeatureDefinition> features)
public FeatureDefinitionContext()
{
Features = features;
Groups = new Dictionary<string, FeatureGroupDefinition>();
}
public virtual FeatureDefinition GetOrNull(string name)
public FeatureGroupDefinition AddGroup(string name, ILocalizableString displayName = null)
{
return Features.GetOrDefault(name);
}
Check.NotNull(name, nameof(name));
public virtual IReadOnlyList<FeatureDefinition> GetAll()
{
return Features.Values.ToImmutableList();
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 virtual void Add(params FeatureDefinition[] definitions)
public FeatureGroupDefinition GetGroupOrNull(string name)
{
if (definitions.IsNullOrEmpty())
Check.NotNull(name, nameof(name));
if (!Groups.ContainsKey(name))
{
return;
return null;
}
foreach (var definition in definitions)
return Groups[name];
}
public void RemoveGroup(string name)
{
Check.NotNull(name, nameof(name));
if (!Groups.ContainsKey(name))
{
Features[definition.Name] = definition;
throw new AbpException($"Undefined feature group: '{name}'.");
}
Groups.Remove(name);
}
}
}

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

@ -10,9 +10,11 @@ namespace Volo.Abp.Features
{
public class FeatureDefinitionManager : IFeatureDefinitionManager, ISingletonDependency
{
protected Lazy<List<IFeatureDefinitionProvider>> Providers { get; }
protected IDictionary<string, FeatureGroupDefinition> FeatureGroupDefinitions => _lazyFeatureGroupDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureGroupDefinition>> _lazyFeatureGroupDefinitions;
protected Lazy<IDictionary<string, FeatureDefinition>> FeatureDefinitions { get; }
protected IDictionary<string, FeatureDefinition> FeatureDefinitions => _lazyFeatureDefinitions.Value;
private readonly Lazy<Dictionary<string, FeatureDefinition>> _lazyFeatureDefinitions;
protected FeatureOptions Options { get; }
@ -25,8 +27,15 @@ namespace Volo.Abp.Features
_serviceProvider = serviceProvider;
Options = options.Value;
Providers = new Lazy<List<IFeatureDefinitionProvider>>(CreateFeatureProviders, true);
FeatureDefinitions = new Lazy<IDictionary<string, FeatureDefinition>>(CreateFeatureDefinitions, true);
_lazyFeatureDefinitions = new Lazy<Dictionary<string, FeatureDefinition>>(
CreateFeatureDefinitions,
isThreadSafe: true
);
_lazyFeatureGroupDefinitions = new Lazy<Dictionary<string, FeatureGroupDefinition>>(
CreateFeatureGroupDefinitions,
isThreadSafe:true
);
}
public virtual FeatureDefinition Get(string name)
@ -45,32 +54,64 @@ namespace Volo.Abp.Features
public virtual IReadOnlyList<FeatureDefinition> GetAll()
{
return FeatureDefinitions.Value.Values.ToImmutableList();
return FeatureDefinitions.Values.ToImmutableList();
}
public virtual FeatureDefinition GetOrNull(string name)
{
return FeatureDefinitions.Value.GetOrDefault(name);
return FeatureDefinitions.GetOrDefault(name);
}
protected virtual List<IFeatureDefinitionProvider> CreateFeatureProviders()
protected virtual Dictionary<string, FeatureDefinition> CreateFeatureDefinitions()
{
return Options
.DefinitionProviders
.Select(p => _serviceProvider.GetRequiredService(p) as IFeatureDefinitionProvider)
.ToList();
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 IDictionary<string, FeatureDefinition> CreateFeatureDefinitions()
protected virtual void AddFeatureToDictionaryRecursively(
Dictionary<string, FeatureDefinition> features,
FeatureDefinition feature)
{
var features = new Dictionary<string, FeatureDefinition>();
if (features.ContainsKey(feature.Name))
{
throw new AbpException("Duplicate feature name: " + feature.Name);
}
features[feature.Name] = feature;
foreach (var provider in Providers.Value)
foreach (var child in feature.Children)
{
provider.Define(new FeatureDefinitionContext(features));
AddFeatureToDictionaryRecursively(features, child);
}
}
return features;
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;
}
}
}

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

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

@ -3,7 +3,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
public abstract class FeatureValueProvider : IFeatureValueProvider, ISingletonDependency
public abstract class FeatureValueProvider : IFeatureValueProvider, ISingletonDependency //TODO: to transient?
{
public abstract string Name { get; }

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

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

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

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

@ -5,6 +5,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Features
{
[Dependency(TryRegister = true)]
public class NullFeatureStore : IFeatureStore, ISingletonDependency
{
public ILogger<NullFeatureStore> Logger { get; set; }

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

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

@ -19,7 +19,7 @@ namespace Volo.Abp.Features
public override async Task<string> GetOrNullAsync(FeatureDefinition feature)
{
return await FeatureStore.GetOrNullAsync(feature.Name, Name, CurrentTenant.Id?.ToString());
return await FeatureStore.GetOrNullAsync(feature.Name, Name, CurrentTenant.Id?.ToString("N"));
}
}
}

1
framework/src/Volo.Abp.Http.Client/Volo/Abp/Http/Client/DynamicProxying/DynamicHttpProxyInterceptor.cs

@ -35,7 +35,6 @@ namespace Volo.Abp.Http.Client.DynamicProxying
protected IJsonSerializer JsonSerializer { get; }
protected IRemoteServiceHttpClientAuthenticator ClientAuthenticator { get; }
public ILogger<DynamicHttpProxyInterceptor<TService>> Logger { get; set; }
static DynamicHttpProxyInterceptor()

50
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs

@ -8,7 +8,7 @@ using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization
{
public class AbpDictionaryBasedStringLocalizer : IStringLocalizer
public class AbpDictionaryBasedStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance
{
public LocalizationResource Resource { get; }
@ -26,7 +26,20 @@ namespace Volo.Abp.Localization
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
{
return GetAllStrings(CultureInfo.CurrentUICulture.Name, includeParentCultures);
return GetAllStrings(
CultureInfo.CurrentUICulture.Name,
includeParentCultures
);
}
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers)
{
return GetAllStrings(
CultureInfo.CurrentUICulture.Name,
includeParentCultures,
includeBaseLocalizers
);
}
public IStringLocalizer WithCulture(CultureInfo culture)
@ -109,26 +122,32 @@ namespace Volo.Abp.Localization
return null;
}
protected virtual IReadOnlyList<LocalizedString> GetAllStrings(string cultureName, bool includeParentCultures = true)
protected virtual IReadOnlyList<LocalizedString> GetAllStrings(
string cultureName,
bool includeParentCultures = true,
bool includeBaseLocalizers = true)
{
//TODO: Can be optimized (example: if it's already default dictionary, skip overriding)
var allStrings = new Dictionary<string, LocalizedString>();
foreach (var baseLocalizer in BaseLocalizers.Select(l => l.WithCulture(CultureInfo.GetCultureInfo(cultureName))))
if (includeBaseLocalizers)
{
//TODO: Try/catch is a workaround here!
try
foreach (var baseLocalizer in BaseLocalizers.Select(l => l.WithCulture(CultureInfo.GetCultureInfo(cultureName))))
{
var baseLocalizedString = baseLocalizer.GetAllStrings(includeParentCultures);
foreach (var localizedString in baseLocalizedString)
//TODO: Try/catch is a workaround here!
try
{
allStrings[localizedString.Name] = localizedString;
var baseLocalizedString = baseLocalizer.GetAllStrings(includeParentCultures);
foreach (var localizedString in baseLocalizedString)
{
allStrings[localizedString.Name] = localizedString;
}
}
}
catch (MissingManifestResourceException)
{
catch (MissingManifestResourceException)
{
}
}
}
@ -160,7 +179,7 @@ namespace Volo.Abp.Localization
: cultureName;
}
public class CultureWrapperStringLocalizer : IStringLocalizer
public class CultureWrapperStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance
{
private readonly string _cultureName;
private readonly AbpDictionaryBasedStringLocalizer _innerLocalizer;
@ -184,6 +203,11 @@ namespace Volo.Abp.Localization
{
return new CultureWrapperStringLocalizer(culture.Name, _innerLocalizer);
}
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers)
{
return _innerLocalizer.GetAllStrings(_cultureName, includeParentCultures, includeBaseLocalizers);
}
}
}
}

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

@ -1,4 +1,5 @@
using Volo.Abp.Localization.Resources.AbpValidation;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Localization.Resources.AbpValidation;
using Volo.Abp.Modularity;
using Volo.Abp.Settings;
using Volo.Abp.VirtualFileSystem;
@ -27,11 +28,6 @@ namespace Volo.Abp.Localization
.Add<AbpValidationResource>("en")
.AddVirtualJson("/Localization/Resources/AbpValidation");
});
Configure<SettingOptions>(options =>
{
options.DefinitionProviders.Add<LocalizationSettingProvider>();
});
}
}
}

58
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpStringLocalizerExtensions.cs

@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.Extensions.Localization;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Reflection;
namespace Volo.Abp.Localization
{
public static class AbpStringLocalizerExtensions
{
[NotNull]
public static IStringLocalizer GetInternalLocalizer(
[NotNull] this IStringLocalizer stringLocalizer)
{
Check.NotNull(stringLocalizer, nameof(stringLocalizer));
var localizerType = stringLocalizer.GetType();
if (!ReflectionHelper.IsAssignableToGenericType(localizerType, typeof(StringLocalizer<>)))
{
return stringLocalizer;
}
var localizerField = localizerType
.GetField(
"_localizer",
BindingFlags.Instance |
BindingFlags.NonPublic
);
if (localizerField == null)
{
throw new AbpException($"Could not find the _localizer field inside the {typeof(StringLocalizer<>).FullName} class. Probably its name has changed. Please report this issue to the ABP framework.");
}
return localizerField.GetValue(stringLocalizer) as IStringLocalizer;
}
public static IEnumerable<LocalizedString> GetAllStrings(
this IStringLocalizer stringLocalizer,
bool includeParentCultures,
bool includeBaseLocalizers)
{
var internalLocalizer = (ProxyHelper.UnProxy(stringLocalizer) as IStringLocalizer).GetInternalLocalizer();
if (internalLocalizer is IStringLocalizerSupportsInheritance stringLocalizerSupportsInheritance)
{
return stringLocalizerSupportsInheritance.GetAllStrings(
includeParentCultures,
includeBaseLocalizers
);
}
return stringLocalizer.GetAllStrings(
includeParentCultures
);
}
}
}

10
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/IStringLocalizerSupportsInheritance.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization
{
public interface IStringLocalizerSupportsInheritance
{
IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures, bool includeBaseLocalizers);
}
}

28
framework/src/Volo.Abp.Security/System/Security/Principal/AbpClaimsIdentityExtensions.cs

@ -91,5 +91,33 @@ namespace System.Security.Principal
return clientIdOrNull.Value;
}
public static Guid? FindEditionId([NotNull] this ClaimsPrincipal principal)
{
Check.NotNull(principal, nameof(principal));
var editionIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.EditionId);
if (editionIdOrNull == null || editionIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
return Guid.Parse(editionIdOrNull.Value);
}
public static Guid? FindEditionId([NotNull] this IIdentity identity)
{
Check.NotNull(identity, nameof(identity));
var claimsIdentity = identity as ClaimsIdentity;
var editionIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.EditionId);
if (editionIdOrNull == null || editionIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
return Guid.Parse(editionIdOrNull.Value);
}
}
}

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

8
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs

@ -44,10 +44,16 @@ namespace Volo.Abp.Security.Claims
public static string PhoneNumberVerified { get; set; } = "phone_number_verified";
/// <summary>
/// Default: "phone_number_verified".
/// Default: "tenantid".
/// </summary>
public static string TenantId { get; set; } = "tenantid";
/// <summary>
/// Default: "editionid".
/// </summary>
public static string EditionId { get; set; } = "editionid";
/// <summary>
/// Default: "client_id".
/// </summary>

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

@ -1,4 +1,7 @@
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.MultiTenancy;
using Volo.Abp.Security;
@ -12,6 +15,11 @@ namespace Volo.Abp.Settings
)]
public class AbpSettingsModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<SettingOptions>(options =>
@ -22,5 +30,23 @@ namespace Volo.Abp.Settings
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);
});
}
}
}

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

@ -5,6 +5,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Settings
{
[Dependency(TryRegister = true)]
public class NullSettingStore : ISettingStore, ISingletonDependency
{
public ILogger<NullSettingStore> Logger { get; set; }

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

@ -10,22 +10,19 @@ namespace Volo.Abp.Settings
{
public class SettingDefinitionManager : ISettingDefinitionManager, ISingletonDependency
{
protected Lazy<List<ISettingDefinitionProvider>> Providers { get; }
protected Lazy<IDictionary<string, SettingDefinition>> SettingDefinitions { get; }
protected SettingOptions Options { get; }
private readonly IServiceProvider _serviceProvider;
protected IServiceProvider ServiceProvider { get; }
public SettingDefinitionManager(
IOptions<SettingOptions> options,
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
ServiceProvider = serviceProvider;
Options = options.Value;
Providers = new Lazy<List<ISettingDefinitionProvider>>(CreateSettingProviders, true);
SettingDefinitions = new Lazy<IDictionary<string, SettingDefinition>>(CreateSettingDefinitions, true);
}
@ -53,21 +50,21 @@ namespace Volo.Abp.Settings
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()
{
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;

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

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

6
framework/src/Volo.Abp.Settings/Volo/Abp/Settings/SettingValue.cs

@ -5,17 +5,11 @@ namespace Volo.Abp.Settings
[Serializable]
public class SettingValue : NameValue
{
/// <summary>
/// Creates a new <see cref="NameValue"/>.
/// </summary>
public SettingValue()
{
}
/// <summary>
/// Creates a new <see cref="NameValue"/>.
/// </summary>
public SettingValue(string name, string value)
{
Name = name;

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

@ -3,7 +3,7 @@ using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Settings
{
public abstract class SettingValueProvider : ISettingValueProvider, ISingletonDependency
public abstract class SettingValueProvider : ISettingValueProvider, ISingletonDependency //TODO: to transient?
{
public abstract string Name { get; }

7
framework/src/Volo.Abp.UI.Navigation/Volo.Abp.UI.Navigation.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.props" />
@ -13,6 +13,11 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\Ui\Navigation\Localization\Resource\*.json" />
<Content Remove="Volo\Abp\Ui\Navigation\Localization\Resource\*.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.UI\Volo.Abp.UI.csproj" />
</ItemGroup>

24
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/AbpUiNavigationModule.cs

@ -1,10 +1,32 @@
using Volo.Abp.Modularity;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Ui.Navigation;
using Volo.Abp.Ui.Navigation.Localization.Resource;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.UI.Navigation
{
[DependsOn(typeof(AbpUiModule))]
public class AbpUiNavigationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpUiNavigationModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<AbpUiNavigationResource>("en")
.AddVirtualJson("/Volo/Abp/Ui/Navigation/Localization/Resource");
});
Configure<NavigationOptions>(options =>
{
options.MenuContributors.Add(new DefaultMenuContributor());
});
}
}
}

13
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenu.cs

@ -1,5 +1,5 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Ui.Navigation;
namespace Volo.Abp.UI.Navigation
{
@ -29,11 +29,11 @@ namespace Volo.Abp.UI.Navigation
/// <inheritdoc cref="IHasMenuItems.Items"/>
[NotNull]
public IList<ApplicationMenuItem> Items { get; } //TODO: Create a specialized collection (that can contain AddAfter for example)
public ApplicationMenuItemList Items { get; }
/// <summary>
/// Can be used to store a custom object related to this menu.
/// TODO: Conver to dictionary!
/// TODO: Convert to dictionary!
/// </summary>
[CanBeNull]
public object CustomData { get; set; }
@ -47,7 +47,7 @@ namespace Volo.Abp.UI.Navigation
Name = name;
DisplayName = displayName ?? Name;
Items = new List<ApplicationMenuItem>();
Items = new ApplicationMenuItemList();
}
/// <summary>
@ -60,5 +60,10 @@ namespace Volo.Abp.UI.Navigation
Items.Add(menuItem);
return this;
}
public override string ToString()
{
return $"[ApplicationMenu] Name = {Name}";
}
}
}

59
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuExtensions.cs

@ -0,0 +1,59 @@
using JetBrains.Annotations;
using System.Linq;
using Volo.Abp.Ui.Navigation;
namespace Volo.Abp.UI.Navigation
{
public static class ApplicationMenuExtensions
{
[NotNull]
public static ApplicationMenuItem GetAdministration(
[NotNull] this ApplicationMenu applicationMenu)
{
return applicationMenu.GetMenuItem(
DefaultMenuNames.Application.Main.Administration
);
}
[NotNull]
public static ApplicationMenuItem GetMenuItem(
[NotNull] this IHasMenuItems menuWithItems,
string menuItemName)
{
var menuItem = menuWithItems.GetMenuItemOrNull(menuItemName);
if (menuItem == null)
{
throw new AbpException($"Could not find a menu item with given name: {menuItemName}");
}
return menuItem;
}
[CanBeNull]
public static ApplicationMenuItem GetMenuItemOrNull(
[NotNull] this IHasMenuItems menuWithItems,
string menuItemName)
{
Check.NotNull(menuWithItems, nameof(menuWithItems));
return menuWithItems.Items.FirstOrDefault(mi => mi.Name == menuItemName);
}
[NotNull]
public static IHasMenuItems SetSubItemOrder(
[NotNull] this IHasMenuItems menuWithItems,
string menuItemName,
int order)
{
Check.NotNull(menuWithItems, nameof(menuWithItems));
var menuItem = menuWithItems.GetMenuItemOrNull(menuItemName);
if (menuItem != null)
{
menuItem.Order = order;
}
return menuWithItems;
}
}
}

10
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs

@ -1,5 +1,6 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Ui.Navigation;
namespace Volo.Abp.UI.Navigation
{
@ -68,7 +69,7 @@ namespace Volo.Abp.UI.Navigation
/// <inheritdoc cref="IHasMenuItems.Items"/>
[NotNull]
public IList<ApplicationMenuItem> Items { get; }
public ApplicationMenuItemList Items { get; }
/// <summary>
/// Can be used to store a custom object related to this menu item. Optional.
@ -109,7 +110,7 @@ namespace Volo.Abp.UI.Navigation
ElementId = elementId ?? GetDefaultElementId();
CssClass = cssClass;
Items = new List<ApplicationMenuItem>();
Items = new ApplicationMenuItemList();
}
/// <summary>
@ -127,5 +128,10 @@ namespace Volo.Abp.UI.Navigation
{
return "MenuItem_" + Name;
}
public override string ToString()
{
return $"[ApplicationMenuItem] Name = {Name}";
}
}
}

46
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItemList.cs

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Volo.Abp.UI.Navigation;
namespace Volo.Abp.Ui.Navigation
{
public class ApplicationMenuItemList : List<ApplicationMenuItem>
{
public ApplicationMenuItemList()
{
}
public ApplicationMenuItemList(int capacity)
: base(capacity)
{
}
public ApplicationMenuItemList(IEnumerable<ApplicationMenuItem> collection)
: base(collection)
{
}
public void Normalize()
{
RemoveEmptyItems();
Order();
}
private void RemoveEmptyItems()
{
RemoveAll(item => item.IsLeaf && item.Url.IsNullOrEmpty());
}
private void Order()
{
//TODO: Is there any way that is more performant?
var orderedItems = this.OrderBy(item => item.Order).ToArray();
Clear();
AddRange(orderedItems);
}
}
}

34
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuContributor.cs

@ -0,0 +1,34 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using System.Threading.Tasks;
using Volo.Abp.Ui.Navigation.Localization.Resource;
using Volo.Abp.UI.Navigation;
namespace Volo.Abp.Ui.Navigation
{
public class DefaultMenuContributor : IMenuContributor
{
public virtual Task ConfigureMenuAsync(MenuConfigurationContext context)
{
Configure(context);
return Task.CompletedTask;
}
protected virtual void Configure(MenuConfigurationContext context)
{
var l = context.ServiceProvider
.GetRequiredService<IStringLocalizer<AbpUiNavigationResource>>();
if (context.Menu.Name == StandardMenus.Main)
{
context.Menu.AddItem(
new ApplicationMenuItem(
DefaultMenuNames.Application.Main.Administration,
l["Menu:Administration"],
icon: "fa fa-wrench"
)
);
}
}
}
}

13
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/DefaultMenuNames.cs

@ -0,0 +1,13 @@
namespace Volo.Abp.Ui.Navigation
{
public static class DefaultMenuNames
{
public static class Application
{
public static class Main
{
public const string Administration = "Abp.Application.Main.Administration";
}
}
}
}

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

Loading…
Cancel
Save