Browse Source

Merge branch 'dev' into maliming/map-claims

pull/3985/head
maliming 6 years ago
parent
commit
24134170e8
  1. 6
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 10
      docs/en/CLI.md
  3. 21
      docs/en/Dapper.md
  4. 8
      docs/en/Object-Extensions.md
  5. 14
      docs/en/SignalR-Integration.md
  6. 8
      docs/en/Text-Templating.md
  7. 4
      docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
  8. 10
      docs/zh-Hans/Dapper.md
  9. 38
      docs/zh-Hans/Object-Extensions.md
  10. 3
      docs/zh-Hans/Samples/Index.md
  11. 455
      docs/zh-Hans/Text-Templating.md
  12. 8
      docs/zh-Hans/UI/Angular/Confirmation-Service.md
  13. 4
      docs/zh-Hans/UI/Angular/Track-By-Service.md
  14. 4
      docs/zh-Hans/docs-nav.json
  15. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs
  16. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs
  17. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs
  18. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs
  19. 21
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs
  20. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs
  21. 56
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  22. 95
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  23. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs
  24. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerStyleContributor.cs
  25. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs
  26. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
  27. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs
  28. 30
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
  29. 100
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js
  30. 150
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs
  31. 11
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs
  32. 69
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs
  33. 18
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
  34. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs
  35. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs
  36. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs
  37. 113
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs
  38. 17
      framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs
  39. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs
  40. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  41. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs
  42. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs
  43. 16
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs
  44. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs
  45. 46
      framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs
  46. 27
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs
  47. 22
      framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs
  48. 54
      framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs
  49. 6
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs
  50. 0
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs
  51. 0
      framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs
  52. 50
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/StringLocalizerHelper.cs
  53. 38
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/IBasicObjectExtensionPropertyInfo.cs
  54. 2
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs
  55. 32
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs
  56. 1
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs
  57. 2
      framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs
  58. 5
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs
  59. 29
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs
  60. 50
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs
  61. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs
  62. 4
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json
  63. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs
  64. 27
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs
  65. 64
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs
  66. 6
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs
  67. 8
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs
  68. 7
      framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeHelper_Tests.cs
  69. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json
  70. 20
      framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs
  71. 50
      framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs
  72. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json
  73. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json
  74. 2
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl
  75. 21
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs
  76. 12
      modules/blogging/src/Volo.Blogging.Web/BloggingTwitterOptions.cs
  77. 8
      modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml
  78. 7
      modules/blogging/src/Volo.Blogging.Web/SocialMedia/BloggingTwitterOptions.cs
  79. 7
      modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminRemoteServiceConsts.cs
  80. 2
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj
  81. 12
      modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs
  82. 7
      modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsRemoteServiceConsts.cs
  83. 1
      modules/docs/src/Volo.Docs.HttpApi.Client/Volo.Docs.HttpApi.Client.csproj
  84. 13
      modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs
  85. 2
      modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityUserLookupAppService.cs
  86. 7
      modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupCountInputDto.cs
  87. 4
      modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupSearchInputDto.cs
  88. 8
      modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserLookupAppService.cs
  89. 2
      modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json
  90. 15
      modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRepositoryExternalUserLookupServiceProvider.cs
  91. 7
      modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs
  92. 24
      modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientIdentityUserLookupService.cs
  93. 7
      modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs
  94. 28
      modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs
  95. 12
      modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs
  96. 21
      modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs
  97. 4
      modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs
  98. 50
      modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs
  99. 22
      modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs
  100. 24
      modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs

6
abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

@ -148,6 +148,10 @@
"SuccessfullySent": "Successfully Sent",
"SuccessfullyDeleted": "Successfully Deleted",
"DiscountRequestDeletionWarningMessage": "Discount request will be deleted" ,
"BusinessType": "Business Type"
"BusinessType": "Business Type",
"TotalQuestionCount": "Total question count",
"RemainingQuestionCount": "Remaining question count",
"TotalQuestionMustBeGreaterWarningMessage": "TotalQuestionCount must be greater than RemainingQuestionCount !",
"QuestionCountsMustBeGreaterThanZero": "TotalQuestionCount and RemainingQuestionCount must be zero or greater than zero !"
}
}

10
docs/en/CLI.md

@ -40,7 +40,7 @@ abp new Acme.BookStore
#### Options
* `--template` or `-t`: Specifies the template name. Default template name is `app`, which generates a web application. Available templates:
* `app` (default): [Application template](Startup-Templates/Application.md). Additional options:
* **`app`** (default): [Application template](Startup-Templates/Application.md). Additional options:
* `--ui` or `-u`: Specifies the UI framework. Default framework is `mvc`. Available frameworks:
* `mvc`: ASP.NET Core MVC. There are some additional options for this template:
* `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios.
@ -51,10 +51,10 @@ abp new Acme.BookStore
* `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks:
* `none`: no mobile application.
* `react-native`: React Native.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `module`: [Module template](Startup-Templates/Module.md). Additional options:
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* **`module`**: [Module template](Startup-Templates/Module.md). Additional options:
* `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI).
* `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory.
* `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version.

21
docs/en/Dapper.md

@ -1,22 +1,24 @@
# Dapper Integration
Because Dapper's idea is that the sql statement takes precedence, and mainly provides some extension methods for the `IDbConnection` interface.
Dapper is a light-weight and simple database provider. The major benefit of using Dapper is writing T-SQL queries. It provides some extension methods for `IDbConnection` interface.
Abp does not encapsulate too many functions for Dapper. Abp Dapper provides a `DapperRepository<TDbContext>` base class based on Abp EntityFrameworkCore, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper.
These two properties can work well with [Unit-Of-Work](Unit-Of-Work.md).
ABP does not encapsulate many functions for Dapper. ABP Dapper library provides a `DapperRepository<TDbContext>` base class based on ABP EntityFrameworkCore module, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. `IDbConnection` and `IDbTransaction` works well with the [ABP Unit-Of-Work](Unit-Of-Work.md).
## Installation
Please install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md).
Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md).
`Volo.Abp.Dapper` is the library for the Dapper integration.
You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper
`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. Install it to your project (for a layered application, to your data/infrastructure layer):
Install it to your project (for a layered application, to your data/infrastructure layer):
```shell
Install-Package Volo.Abp.Dapper
```
Then add `AbpDapperModule` module dependency (`DependsOn` attribute) to your [module](Module-Development-Basics.md):
Then add `AbpDapperModule` module dependency (with `DependsOn` attribute) to your [module](Module-Development-Basics.md):
````C#
using Volo.Abp.Dapper;
@ -34,9 +36,10 @@ namespace MyCompany.MyProject
## Implement Dapper Repository
The following code implements the `Person` repository, which requires EF Core's `DbContext` (MyAppDbContext). You can inject `PersonDapperRepository` to call its methods.
The following code creates the `PersonRepository`, which requires EF Core's `DbContext` (MyAppDbContext).
You can inject `PersonDapperRepository` to your services for your database operations.
`DbConnection` and `DbTransaction` are from the `DapperRepository` base class.
`DbConnection` and `DbTransaction` comes from the `DapperRepository` base class.
```C#
public class PersonDapperRepository : DapperRepository<MyAppDbContext>, ITransientDependency

8
docs/en/Object-Extensions.md

@ -200,17 +200,17 @@ ObjectExtensionManager.Instance
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, int>(
"MyIntProperty",
.AddOrUpdateProperty<IdentityUser, DateTime>(
"MyDateTimeProperty",
options =>
{
options.DefaultValueFactory = () => 42;
options.DefaultValueFactory = () => DateTime.Now;
});
````
`options.DefaultValueFactory` has a higher priority than the `options.DefaultValue` .
> Tip: Use `DefaultValueFactory` option only if the default value may change over the time. If it is a constant value, then use the `DefaultValue` option.
> Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option.
#### CheckPairDefinitionOnMapping

14
docs/en/SignalR-Integration.md

@ -1,6 +1,6 @@
# SignalR Integration
> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages that simplify the integration and usage.
> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages those simplify the integration and usage.
## Installation
@ -224,4 +224,14 @@ ABP implements SignalR's `IUserIdProvider` interface to provide the current user
See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messages between (authenticated) users.
![signalr-demo-chat](images/signalr-demo-chat.png)
![signalr-demo-chat](images/signalr-demo-chat.png)
## Remarks
ABP Framework doesn't change the SignalR. It works in your ABP Framework based application just like any other ASP.NET Core application.
Refer to the Microsoft's documentation to [host and scale](https://docs.microsoft.com/en-us/aspnet/core/signalr/scale) your application, integrate to [Azure](https://docs.microsoft.com/en-us/aspnet/core/signalr/publish-to-azure-web-app) or [Redis backplane](https://docs.microsoft.com/en-us/aspnet/core/signalr/redis-backplane)... etc.
## See Also
* [Microsoft SignalR documentation](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction)

8
docs/en/Text-Templating.md

@ -204,13 +204,14 @@ Inline localization uses the [localization system](Localization.md) to localize
Assuming you need to send an email to a user to reset her/his password. Here, the template content:
````
<a href="{%{{{model.link}}}%}">{%{{{L "ResetMyPassword"}}}%}</a>
<a title="{%{{{L "ResetMyPasswordTitle"}}}%}" href="{%{{{model.link}}}%}">{%{{{L "ResetMyPassword" model.name}}}%}</a>
````
`L` function is used to localize the given key based on the current user culture. You need to define the `ResetMyPassword` key inside your localization file:
````json
"ResetMyPassword": "Click here to reset your password"
"ResetMyPasswordTitle": "Reset my password",
"ResetMyPassword": "Hi {0}, Click here to reset your password"
````
You also need to declare the localization resource to be used with this template, inside your template definition provider class:
@ -234,6 +235,7 @@ var result = await _templateRenderer.RenderAsync(
"PasswordReset", //the template name
new PasswordResetModel
{
Name = "john",
Link = "https://abp.io/example-link?userId=123&token=ABC"
}
);
@ -242,7 +244,7 @@ var result = await _templateRenderer.RenderAsync(
You will see the localized result:
````csharp
<a href="https://abp.io/example-link?userId=123&token=ABC">Click here to reset your password</a>
<a title="Reset my password" href="https://abp.io/example-link?userId=123&token=ABC">Hi john, Click here to reset your password</a>
````
> If you define the [default localization resource](Localization.md) for your application, then no need to declare the resource type for the template definition.

4
docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md

@ -88,6 +88,8 @@ You can set some of the attributes on your c# property, or directly on html tag.
* `label`: Sets the label for input.
* `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`.
`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-input-tag-helper) are also valid for `abp-input` tag helper.
### Label & Localization
You can set label of your input in different ways:
@ -258,4 +260,4 @@ You can set some of the attributes on your c# property, or directly on html tag.
#### Tag Attributes
- `asp-items`: Sets the select data. This Should be a list of SelectListItem.
- `Inline`: If true, radio buttons will be in single line, next to each other. If false, they will be under each other.
- `Inline`: If true, radio buttons will be in single line, next to each other. If false, they will be under each other.

10
docs/zh-Hans/Dapper.md

@ -1,16 +1,16 @@
# Dapper 集成
由于Dapper的思想是sql语句优先, 且主要为`IDbConnection`接口提供了一些扩展方法.
Dapper 是轻量化的数据库提供者,使用Dapper主要的好处是编写T-SQL查询,它为 `IDbConnection` 接口提供了一些扩展方法.
Abp并没有为Dapper封装太多功能. Abp Dapper在Abp EntityFrameworkCore的基础上提供了`DapperRepository<TDbContext>`基类, 在其中提供了Dapper需要的`IDbConnection`和`IDbTransaction`属性.
这两个属性可以和[工作单元](Unit-Of-Work.md)很好的配合.
ABP没有为Dapper封装许多功能, ABP Dapper库在ABP EntityFrameworkCore的基础上提供了 `DapperRepository<TDbContext>` 基类,在其中提供了Dapper需要的`IDbConnection`和`IDbTransaction`属性,这两个属性可以和[工作单元](Unit-Of-Work.md)很好的配合.
## 安装
请先根据[EF Core的集成文档](Entity-Framework-Core.md)安装并配置好EF Core.
`Volo.Abp.Dapper`是Dapper集成的主要nuget包. 将其安装到你的项目中(在分层应用程序中适用于 数据访问/基础设施层):
`Volo.Abp.Dapper`是Dapper集成的主要[nuget包](https://www.nuget.org/packages/Volo.Abp.Dapper).
将其安装到你的项目中(在分层应用程序中适用于 数据访问/基础设施层):
```shell
Install-Package Volo.Abp.Dapper

38
docs/zh-Hans/Object-Extensions.md

@ -176,6 +176,44 @@ ObjectExtensionManager.Instance
`options` 有一个名为 `Configuration` 的字典,该字典存储对象扩展定义甚至可以扩展. EF Core使用它来将其他属性映射到数据库中的表字段. 请参阅[扩展实体文档](Customizing-Application-Modules-Extending-Entities.md).
#### 默认值
自动为新属性设置默认值,默认值是属性类型的自然默认值,例如: `string`: `null` , `bool`: `false` 或 `int`: `0`.
有两种方法可以覆盖默认值:
##### DefaultValue 选项
`DefaultValue` 选项可以设置任何值:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, int>(
"MyIntProperty",
options =>
{
options.DefaultValue = 42;
});
````
##### DefaultValueFactory 选项
`DefaultValueFactory` 可以设置返回默认值的函数:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, DateTime>(
"MyDateTimeProperty",
options =>
{
options.DefaultValueFactory = () => DateTime.Now;
});
````
`options.DefaultValueFactory` 比 `options.DefaultValue` 优先级要高.
> 提示: 只有在默认值可能发生变化时(如示例中的`DateTime.Now;`) 才使用 `DefaultValueFactory`,如果是一个常量请使用 `DefaultValue` 选项.
#### CheckPairDefinitionOnMapping
控制在映射两个可扩展对象时如何检查属性定义. 请参阅*对象到对象映射*部分,了解 `CheckPairDefinitionOnMapping` 选项.

3
docs/zh-Hans/Samples/Index.md

@ -45,6 +45,9 @@
* [源码](https://github.com/abpframework/abp-samples/tree/master/RabbitMqEventBus)
* [分布式事件总线文档](../Distributed-Event-Bus.md)
* [RabbitMQ 分布式事件总线集成文档](../Distributed-Event-Bus-RabbitMQ-Integration.md)
* **文本模板 Demo**: 文本模板系统的不同用例.
* [源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo)
* [文本模板文档](../Text-Templating.md)
* **自定义认证**: 如何为ASP.NET Core MVC / Razor Pages应用程序自定义身份验证的解决方案.
* [源码](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization)
* 相关 "[How To](../How-To/Index.md)" 文档:

455
docs/zh-Hans/Text-Templating.md

@ -0,0 +1,455 @@
# 文本模板
## 介绍
ABP框架提供了一个简单有效的文本模板系统,文本模板用于动态渲染基于模板和模型(数据对象)内容:
***TEMPLATE + MODEL ==render==> RENDERED CONTENT***
它非常类似于 ASP.NET Core Razor View (或 Page):
*RAZOR VIEW (or PAGE) + MODEL ==render==> HTML CONTENT*
你可以将渲染的输出用于任何目的,例如发送电子邮件或准备一些报告.
### 示例
Here, a simple template:
````
Hello {%{{{model.name}}}%} :)
````
你可以定义一个含有 `Name` 属性的类来渲染这个模板:
````csharp
public class HelloModel
{
public string Name { get; set; }
}
````
如果你使用 `Name` 为 `John` 的 `HelloModel` 渲染模板,输出为:
````
Hello John :)
````
模板渲染引擎非常强大;
* 它基于 [Scriban](https://github.com/lunet-io/scriban) 库, 所以它支持 **条件逻辑**, **循环** 等.
* 模板内容 **可以本地化**.
* 你可以定义 **布局模板** 在渲染其他模板中用做布局.
* 对于高级场景,你可以传递任何对象到模板上下文.
### 源码
这里是本文开发和引用的[示例应用程序源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
## 安装
推荐使用 [ABP CLI](CLI.md) 安装包.
### 使用 ABP CLI
在项目目录(.csproj file)打开命令行窗口运行以下命令:
````bash
abp add-package Volo.Abp.TextTemplating
````
### 手动安装
如果你想要手动安装;
1. 添加 [Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating) NuGet包到你的项目:
````
Install-Package Volo.Abp.TextTemplating
````
2. 添加 `AbpTextTemplatingModule` 到你的模块依赖列表:
````csharp
[DependsOn(
//...other dependencies
typeof(AbpTextTemplatingModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
}
````
## 定义模板
在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类:
````csharp
public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition("Hello") //template name: "Hello"
.WithVirtualFilePath(
"/Demos/Hello/Hello.tpl", //template content path
isInlineLocalized: true
)
);
}
}
````
* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板.
* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用).
* `/Demos/Hello/Hello.tpl` 是模板文件的路径.
* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分.
### 模板内容
`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.tpl` 文件,并在属性窗口中将其标记为"**嵌入式资源**":
![hello-template](images/hello-template.png)
示例 `Hello.tpl` 内容如下所示:
````
Hello {%{{{model.name}}}%} :)
````
[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件:
````csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<TextTemplateDemoModule>("TextTemplateDemo");
});
````
* `TextTemplateDemoModule`是模块类.
* `TextTemplateDemo` 是你的项目的根命名空间.
## 渲染模板
`ITemplateRenderer` 服务用于渲染模板内容.
### 示例: 渲染一个简单的模板
````csharp
public class HelloDemo : ITransientDependency
{
private readonly ITemplateRenderer _templateRenderer;
public HelloDemo(ITemplateRenderer templateRenderer)
{
_templateRenderer = templateRenderer;
}
public async Task RunAsync()
{
var result = await _templateRenderer.RenderAsync(
"Hello", //the template name
new HelloModel
{
Name = "John"
}
);
Console.WriteLine(result);
}
}
````
* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它.
* `RenderAsync` 有两个基本参数:
* `templateName`: 要渲染的模板名称 (本示例中是 `Hello`).
* `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象).
示例会返回以下结果:
````csharp
Hello John :)
````
### 匿名模型
虽然建议为模板创建模型类,但在简单情况下使用匿名对象也是可行的:
````csharp
var result = await _templateRenderer.RenderAsync(
"Hello",
new
{
Name = "John"
}
);
````
示例中我们并没有创建模型类,但是创建了一个匿名对象模型.
### 大驼峰 与 小驼峰
PascalCase 属性名(如 `UserName`) 在模板中用做小驼峰(如 `userName`).
## 本地化
可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项.
### 内联本地化
内联本地化使用[本地化系统](Localization.md)本地化模板内的文本.
#### 示例: 重置密码链接
假设你需要向用户发送电子邮件重置密码. 模板内容:
````
<a href="{%{{{model.link}}}%}">{%{{{L "ResetMyPassword"}}}%}</a>
````
`L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键:
````json
"ResetMyPassword": "Click here to reset your password"
````
你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源:
````csharp
context.Add(
new TemplateDefinition(
"PasswordReset", //Template name
typeof(DemoResource) //LOCALIZATION RESOURCE
).WithVirtualFilePath(
"/Demos/PasswordReset/PasswordReset.tpl", //template content path
isInlineLocalized: true
)
);
````
当你这样渲染模板时:
````csharp
var result = await _templateRenderer.RenderAsync(
"PasswordReset", //the template name
new PasswordResetModel
{
Link = "https://abp.io/example-link?userId=123&token=ABC"
}
);
````
你可以看到以下本地化结果:
````csharp
<a href="https://abp.io/example-link?userId=123&token=ABC">Click here to reset your password</a>
````
> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型.
### 多个内容本地化
你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它.
#### 示例: 欢迎电子邮件模板
假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板.
首先创建一个文件夹,将模板放在里面,像 `en.tpl`, `tr.tpl` 每一个你支持的文化:
![multiple-file-template](images/multiple-file-template.png)
然后在模板定义提供程序类中添加模板定义:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en"
)
.WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates", //template content folder
isInlineLocalized: false
)
);
````
* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化.
* 指定 **模板文件夹** 而不是单个模板文件.
* 设置 `isInlineLocalized` 为 `false`.
就这些,你可以渲染当前文化的模板:
````csharp
var result = await _templateRenderer.RenderAsync("WelcomeEmail");
````
> 为了简单我们跳过了模型,但是你可以使用前面所述的模型.
### 指定文化
`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化.
````csharp
var result = await _templateRenderer.RenderAsync(
"WelcomeEmail",
cultureName: "en"
);
````
## 布局模板
布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统.
### 示例: 邮件HTML布局模板
例如,你想为所有电子邮件模板创建一个布局.
首先像之前一样创建一个模板文件:
````xml
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
{%{{{content}}}%}
</body>
</html>
````
* 布局模板必须具有 **{%{{{content}}}%}** 部分作为渲染的子内容的占位符.
在模板定义提供程序中注册模板:
````csharp
context.Add(
new TemplateDefinition(
"EmailLayout",
isLayout: true //SET isLayout!
).WithVirtualFilePath(
"/Demos/EmailLayout/EmailLayout.tpl",
isInlineLocalized: true
)
);
````
现在你可以将此模板用作任何其他模板的布局:
````csharp
context.Add(
new TemplateDefinition(
name: "WelcomeEmail",
defaultCultureName: "en",
layout: "EmailLayout" //Set the LAYOUT
).WithVirtualFilePath(
"/Demos/WelcomeEmail/Templates",
isInlineLocalized: false
)
);
````
## 全局上下文
ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量.
示例模板内容:
````
A global object value: {%{{{myGlobalObject}}}%}
````
模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它:
````csharp
var result = await _templateRenderer.RenderAsync(
"GlobalContextUsage",
globalContext: new Dictionary<string, object>
{
{"myGlobalObject", "TEST VALUE"}
}
);
````
渲染的结果将是:
````
A global object value: TEST VALUE
````
## 高级功能
本节介绍文本模板系统的一些内部知识和高级用法.
### 模板内容Provider
`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容.
> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容.
示例:
````csharp
public class TemplateContentDemo : ITransientDependency
{
private readonly ITemplateContentProvider _templateContentProvider;
public TemplateContentDemo(ITemplateContentProvider templateContentProvider)
{
_templateContentProvider = templateContentProvider;
}
public async Task RunAsync()
{
var result = await _templateContentProvider
.GetContentOrNullAsync("Hello");
Console.WriteLine(result);
}
}
````
结果是原始模板内容:
````
Hello {%{{{model.name}}}%} :)
````
* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`.
* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分).
### 模板内容贡献者
`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容.
你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容.
示例:
````csharp
public class MyTemplateContentProvider
: ITemplateContentContributor, ITransientDependency
{
public async Task<string> GetOrNullAsync(TemplateContentContributorContext context)
{
var templateName = context.TemplateDefinition.Name;
//TODO: Try to find content from another source
return null;
}
}
````
如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者.
### Template Definition Manager
`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建).
## 另请参阅
* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo).
* [本地化系统](Localization.md).
* [虚拟文件系统](Virtual-File-System.md).

8
docs/zh-Hans/UI/Angular/Confirmation-Service.md

@ -49,7 +49,7 @@ this.confirmation
- `message` 和 `title` 参数接收字符串,本地化Key或本地化对象. 参阅[本地化文档](./Localization.md)
- `Confirmation.Status` 是一个枚举,具有三个属性;
- `Confirmation.Status.confirm` 是一个关闭事件值,当通过确认按钮关闭弹出窗口时触发此事件.
- `Confirmation.Status.reject` 是一个关闭事件值,当通过“取消”按钮关闭弹出窗口时触发此事件.
- `Confirmation.Status.reject` 是一个关闭事件值,当通过取消按钮关闭弹出窗口时触发此事件.
- `Confirmation.Status.dismiss` 是一个关闭事件值,当通过按Escape键关闭弹出窗口时触发此事件.
如果你对确认状态不感兴趣,则不必订阅返回的observable:
@ -66,6 +66,7 @@ this.confirmation.error('You are not authorized.', 'Error');
const options: Partial<Confirmation.Options> = {
hideCancelBtn: false,
hideYesBtn: false,
dismissible: false,
cancelText: 'Close',
yesText: 'Confirm',
messageLocalizationParams: ['Demo'],
@ -81,8 +82,9 @@ this.confirmation.warn(
- `hideCancelBtn` 选项为 `true` 时隐藏取消按钮. 默认值为 `false`.
- `hideYesBtn` 选项为 `true` 时隐藏确认按钮. 默认值为 `false`.
- `cancelText` 是取消按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Cancel`.
- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Yes`.
- `dismissible`选项允许通过按Escape键或单击背景来取消确认弹出窗口. 默认值为 `true`.
- `cancelText` 是取消按钮的文本,可以传递本地化键或本地化对象. 默认值为 `AbpUi::Cancel`.
- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值为 `AbpUi::Yes`.
- `messageLocalizationParams`是用于消息本地化的插值参数.
- `titleLocalizationParams` 是标题本地化的插值参数.

4
docs/zh-Hans/UI/Angular/Track-By-Service.md

@ -96,3 +96,7 @@ class DemoComponent {
trackByTenantAccountId = trackByDeep<Item>('tenant', 'account', 'id');
}
```
## 下一步是什么?
- [ListService](./List-Service.md)

4
docs/zh-Hans/docs-nav.json

@ -181,6 +181,10 @@
"path": "Object-To-Object-Mapping.md"
},
{
"text": "文本模板",
"path": "Text-Templating.md"
},
{
"text": "JSON序列化"
},

1
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs

@ -9,6 +9,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
public Dictionary<string, ExtensionPropertyDto> Properties { get; set; }
public Dictionary<string, object> Configuration { get; set; }
}
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionEnumDto
{
public List<ExtensionEnumFieldDto> Fields { get; set; }
public string LocalizationResource { get; set; }
}
}

12
framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs

@ -0,0 +1,12 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
[Serializable]
public class ExtensionEnumFieldDto
{
public string Name { get; set; }
public object Value { get; set; }
}
}

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

@ -7,5 +7,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
public class ObjectExtensionsDto
{
public Dictionary<string, ModuleExtensionDto> Modules { get; set; }
public Dictionary<string, ExtensionEnumDto> Enums { get; set; }
}
}

21
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs

@ -2,20 +2,31 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Text.Encodings.Web;
using Volo.Abp.Threading;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions
{
public static class TagHelperExtensions
{
public static async Task<TagHelperOutput> ProcessAndGetOutputAsync(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, string tagName = "div", TagMode tagMode = TagMode.SelfClosing)
public static async Task<TagHelperOutput> ProcessAndGetOutputAsync(
this TagHelper tagHelper,
TagHelperAttributeList attributeList,
TagHelperContext context,
string tagName = "div",
TagMode tagMode = TagMode.SelfClosing)
{
var innerOutput = new TagHelperOutput(tagName, attributeList, (useCachedResult, encoder) => Task.Run<TagHelperContent>(() => new DefaultTagHelperContent()))
var innerOutput = new TagHelperOutput(
tagName,
attributeList,
(useCachedResult, encoder) => Task.Run<TagHelperContent>(() => new DefaultTagHelperContent()))
{
TagMode = tagMode
};
var innerContext = new TagHelperContext(attributeList, context.Items, Guid.NewGuid().ToString());
var innerContext = new TagHelperContext(
attributeList,
context.Items,
Guid.NewGuid().ToString()
);
tagHelper.Init(context);

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

@ -33,6 +33,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
[ViewContext]
public ViewContext ViewContext { get; set; }
[HtmlAttributeName("asp-format")]
public string Format { get; set; }
public string Name { get; set; }
public string Value { get; set; }
public AbpInputTagHelper(AbpInputTagHelperService tagHelperService)
: base(tagHelperService)
{

56
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -91,7 +91,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
inputHtml + label :
label + inputHtml;
return innerContent + infoHtml + validation;
return innerContent + infoHtml + validation;
}
protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml, bool isCheckbox)
@ -103,30 +103,56 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
protected virtual TagHelper GetInputTagHelper(TagHelperContext context, TagHelperOutput output)
{
var textAreaAttribute = TagHelper.AspFor.ModelExplorer.GetAttribute<TextArea>();
if (textAreaAttribute != null)
if (TagHelper.AspFor.ModelExplorer.GetAttribute<TextArea>() != null)
{
return new TextAreaTagHelper(_generator)
var textAreaTagHelper = new TextAreaTagHelper(_generator)
{
For = TagHelper.AspFor,
ViewContext = TagHelper.ViewContext
};
if (!TagHelper.Name.IsNullOrEmpty())
{
textAreaTagHelper.Name = TagHelper.Name;
}
return textAreaTagHelper;
}
return new InputTagHelper(_generator)
var inputTagHelper = new InputTagHelper(_generator)
{
For = TagHelper.AspFor,
InputTypeName = TagHelper.InputTypeName,
ViewContext = TagHelper.ViewContext
};
if (!TagHelper.Format.IsNullOrEmpty())
{
inputTagHelper.Format = TagHelper.Format;
}
if (!TagHelper.Name.IsNullOrEmpty())
{
inputTagHelper.Name = TagHelper.Name;
}
if (!TagHelper.Value.IsNullOrEmpty())
{
inputTagHelper.Value = TagHelper.Value;
}
return inputTagHelper;
}
protected virtual async Task<(TagHelperOutput, bool)> GetInputTagHelperOutputAsync(TagHelperContext context, TagHelperOutput output)
{
var tagHelper = GetInputTagHelper(context, output);
var inputTagHelperOutput = await tagHelper.ProcessAndGetOutputAsync(GetInputAttributes(context, output), context, "input");
var inputTagHelperOutput = await tagHelper.ProcessAndGetOutputAsync(
GetInputAttributes(context, output),
context,
"input"
);
ConvertToTextAreaIfTextArea(inputTagHelperOutput);
AddDisabledAttribute(inputTagHelperOutput);
@ -245,7 +271,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
return "";
}
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null ? "<span> * </span>":"";
return TagHelper.AspFor.ModelExplorer.GetAttribute<RequiredAttribute>() != null ? "<span> * </span>" : "";
}
protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag, bool isCheckbox)
@ -282,7 +308,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
var idAttr = inputTag.Attributes.FirstOrDefault(a => a.Name == "id");
var localizedText = _tagHelperLocalizer.GetLocalizedText(text, TagHelper.AspFor.ModelExplorer);
return "<small id=\""+ idAttr?.Value + "InfoText\" class=\"form-text text-muted\">" +
return "<small id=\"" + idAttr?.Value + "InfoText\" class=\"form-text text-muted\">" +
localizedText +
"</small>";
}
@ -332,7 +358,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
var groupPrefix = "group-";
var tagHelperAttributes = output.Attributes.Where(a => !a.Name.StartsWith(groupPrefix)).ToList();
var attrList = new TagHelperAttributeList();
foreach (var tagHelperAttribute in tagHelperAttributes)
@ -345,6 +371,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
attrList.Add("type", TagHelper.InputTypeName);
}
if (!TagHelper.Name.IsNullOrEmpty() && !attrList.ContainsName("name"))
{
attrList.Add("name", TagHelper.Name);
}
if (!TagHelper.Value.IsNullOrEmpty() && !attrList.ContainsName("value"))
{
attrList.Add("value", TagHelper.Value);
}
return attrList;
}

95
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
@ -12,6 +13,7 @@ using Microsoft.Extensions.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
using Volo.Abp.DynamicProxy;
using Volo.Abp.Localization;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
@ -21,12 +23,18 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
private readonly IHtmlGenerator _generator;
private readonly HtmlEncoder _encoder;
private readonly IAbpTagHelperLocalizer _tagHelperLocalizer;
private readonly IStringLocalizerFactory _stringLocalizerFactory;
public AbpSelectTagHelperService(IHtmlGenerator generator, HtmlEncoder encoder, IAbpTagHelperLocalizer tagHelperLocalizer)
public AbpSelectTagHelperService(
IHtmlGenerator generator,
HtmlEncoder encoder,
IAbpTagHelperLocalizer tagHelperLocalizer,
IStringLocalizerFactory stringLocalizerFactory)
{
_generator = generator;
_encoder = encoder;
_tagHelperLocalizer = tagHelperLocalizer;
_stringLocalizerFactory = stringLocalizerFactory;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
@ -209,74 +217,33 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form
}
var containerLocalizer = _tagHelperLocalizer.GetLocalizerOrNull(explorer.Container.ModelType.Assembly);
IStringLocalizer modelObjectLocalizer = null;
if (explorer.Model != null)
{
modelObjectLocalizer = _tagHelperLocalizer.GetLocalizerOrNull(ProxyHelper.UnProxy(explorer.Model).GetType().Assembly);
}
selectItems.AddRange(enumType.GetEnumNames()
.Select(enumName => new SelectListItem
{
Value = Convert.ToUInt64(Enum.Parse(enumType, enumName)).ToString(),
Text = GetLocalizedEnumFieldName(containerLocalizer, modelObjectLocalizer, enumType, enumName)
}));
return selectItems;
}
protected virtual string GetLocalizedEnumFieldName(
IStringLocalizer containerLocalizer,
IStringLocalizer modelObjectLocalizer,
Type enumType,
string fieldName)
{
LocalizedString localizedString;
//Look for the enum name + enum field name
var localizationKey = enumType.Name + "." + fieldName;
if (containerLocalizer != null)
{
localizedString = containerLocalizer[localizationKey];
if (!localizedString.ResourceNotFound)
{
return localizedString.Value;
}
}
if (modelObjectLocalizer != null)
foreach (var enumValue in enumType.GetEnumValues())
{
localizedString = modelObjectLocalizer[localizationKey];
if (!localizedString.ResourceNotFound)
var memberName = enumType.GetEnumName(enumValue);
var localizedMemberName = AbpInternalLocalizationHelper.LocalizeWithFallback(
new[]
{
containerLocalizer,
_stringLocalizerFactory.CreateDefaultOrNull()
},
new[]
{
$"Enum:{enumType.Name}.{memberName}",
$"{enumType.Name}.{memberName}",
memberName
},
memberName
);
selectItems.Add(new SelectListItem
{
return localizedString.Value;
}
}
//Look for the enum field name
localizationKey = fieldName;
if (containerLocalizer != null)
{
localizedString = containerLocalizer[localizationKey];
if (!localizedString.ResourceNotFound)
{
return localizedString.Value;
}
}
if (modelObjectLocalizer != null)
{
localizedString = modelObjectLocalizer[localizationKey];
if (!localizedString.ResourceNotFound)
{
return localizedString.Value;
}
Value = enumValue.ToString(),
Text = localizedMemberName
});
}
return fieldName;
return selectItems;
}
protected virtual List<SelectListItem> GetSelectItemsFromAttribute(

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerScriptContributor.cs

@ -4,7 +4,7 @@ using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Timeago
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker
{
[DependsOn(typeof(JQueryScriptContributor))]
public class BootstrapDatepickerScriptContributor : BundleContributor

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/BootstrapDatepicker/BootstrapDatepickerStyleContributor.cs

@ -1,7 +1,7 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Select2
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker
{
public class BootstrapDatepickerStyleContributor : BundleContributor
{

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Luxon

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs

@ -1,5 +1,6 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.DatatablesNetBs4;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQueryForm;

1
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs

@ -1,5 +1,6 @@
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.BootstrapDatepicker;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Core;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.DatatablesNetBs4;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.FontAwesome;

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

@ -8,7 +8,7 @@
};
/************************************************************************
* RECORD-ACTIONS extension for datatables *
* RECORD-ACTIONS extension for datatables *
*************************************************************************/
(function () {
if (!$.fn.dataTableExt) {
@ -328,4 +328,32 @@
})();
/************************************************************************
* Default Renderers *
*************************************************************************/
datatables.defaultRenderers = datatables.defaultRenderers || {};
datatables.defaultRenderers['boolean'] = function(value) {
if (value) {
return '<i class="fa fa-check"></i>';
} else {
return '<i class="fa fa-times"></i>';
}
};
datatables.defaultRenderers['date'] = function (value) {
return luxon
.DateTime
.fromISO(value, { locale: abp.localization.currentCulture.name })
.toLocaleString();
};
datatables.defaultRenderers['datetime'] = function (value) {
return luxon
.DateTime
.fromISO(value, { locale: abp.localization.currentCulture.name })
.toLocaleString(luxon.DateTime.DATETIME_SHORT);
};
})(jQuery);

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

@ -96,9 +96,29 @@
get: _get
};
})();
function initializeObjectExtensions() {
var getShortEnumTypeName = function (enumType) {
var lastDotIndex = enumType.lastIndexOf('.');
if (lastDotIndex < 0) {
return enumType;
}
return enumType.substr(lastDotIndex + 1);
};
var getEnumMemberName = function (enumInfo, enumMemberValue) {
for (var i = 0; i < enumInfo.fields.length; i++) {
var enumField = enumInfo.fields[i];
if (enumField.value == enumMemberValue) {
return enumField.name;
}
}
return null;
};
function localizeDisplayName(propertyName, displayName) {
if (displayName && displayName.name) {
return abp.localization.localize(displayName.name, displayName.resource);
@ -111,6 +131,47 @@
return abp.localization.localize(propertyName);
}
function localizeWithFallback(localizationResources, keys, defaultValue) {
for (var i = 0; i < localizationResources.length; i++) {
var localizationResource = localizationResources[i];
if (!localizationResource) {
continue;
}
for (var j = 0; j < keys.length; j++) {
var key = keys[j];
if (abp.localization.isLocalized(key, localizationResource)) {
return abp.localization.localize(key, localizationResource);
}
}
}
return defaultValue;
}
function localizeEnumMember(property, enumMemberValue) {
var enumType = property.config.type;
var enumInfo = abp.objectExtensions.enums[enumType];
var enumMemberName = getEnumMemberName(enumInfo, enumMemberValue);
if (!enumMemberName) {
return enumMemberValue;
}
var shortEnumType = getShortEnumTypeName(enumType);
return localizeWithFallback(
[enumInfo.localizationResource, abp.localization.defaultResourceName],
[
'Enum:' + shortEnumType + '.' + enumMemberName,
shortEnumType + '.' + enumMemberName,
enumMemberName
],
enumMemberName
);
}
function configureTableColumns(tableName, columnConfigs) {
abp.ui.extensions.tableColumns.get(tableName)
.addContributor(
@ -137,16 +198,41 @@
return tableProperties;
}
function getValueFromRow(property, row) {
return row.extraProperties[property.name];;
}
function convertPropertyToColumnConfig(property) {
var columnConfig = {
title: localizeDisplayName(property.name, property.config.displayName),
data: "extraProperties." + property.name,
orderable: false
};
if (property.config.typeSimple === 'enum') {
columnConfig.render = function(data, type, row) {
var value = getValueFromRow(property, row);
return localizeEnumMember(property, value);
}
} else {
var defaultRenderer = abp.libs.datatables.defaultRenderers[property.config.typeSimple];
if (defaultRenderer) {
columnConfig.render = function (data, type, row) {
var value = getValueFromRow(property, row);
return defaultRenderer(value);
}
}
}
return columnConfig;
}
function convertPropertiesToColumnConfigs(properties) {
var columnConfigs = [];
for (var i = 0; i < properties.length; i++) {
var tableProperty = properties[i];
columnConfigs.push({
title: localizeDisplayName(tableProperty.name, tableProperty.config.displayName),
data: "extraProperties." + tableProperty.name,
orderable: false
});
columnConfigs.push(convertPropertyToColumnConfig(properties[i]));
}
return columnConfigs;

150
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/ObjectExtending/MvcUiObjectExtensionPropertyInfoExtensions.cs

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Reflection;
namespace Volo.Abp.ObjectExtending
{
public static class MvcUiObjectExtensionPropertyInfoExtensions
{
private static readonly HashSet<Type> NumberTypes = new HashSet<Type> {
typeof(int),
typeof(long),
typeof(byte),
typeof(sbyte),
typeof(short),
typeof(ushort),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(decimal),
typeof(int?),
typeof(long?),
typeof(byte?),
typeof(sbyte?),
typeof(short?),
typeof(ushort?),
typeof(uint?),
typeof(long?),
typeof(ulong?),
typeof(float?),
typeof(double?),
typeof(decimal?)
};
public static string GetInputFormatOrNull(this IBasicObjectExtensionPropertyInfo property)
{
if (property.IsDate())
{
return "{0:yyyy-MM-dd}";
}
if (property.IsDateTime())
{
return "{0:yyyy-MM-ddTHH:mm}";
}
return null;
}
public static string GetInputValueOrNull(this IBasicObjectExtensionPropertyInfo property, object value)
{
if (value == null)
{
return null;
}
if (TypeHelper.IsFloatingType(property.Type))
{
return value.ToString()?.Replace(',', '.');
}
/* Let the ASP.NET Core handle it! */
return null;
}
public static string GetInputType(this ObjectExtensionPropertyInfo propertyInfo)
{
foreach (var attribute in propertyInfo.Attributes)
{
var inputTypeByAttribute = GetInputTypeFromAttributeOrNull(attribute);
if (inputTypeByAttribute != null)
{
return inputTypeByAttribute;
}
}
return GetInputTypeFromTypeOrNull(propertyInfo.Type)
?? "text"; //default
}
private static string GetInputTypeFromAttributeOrNull(Attribute attribute)
{
if (attribute is EmailAddressAttribute)
{
return "email";
}
if (attribute is UrlAttribute)
{
return "url";
}
if (attribute is HiddenInputAttribute)
{
return "hidden";
}
if (attribute is PhoneAttribute)
{
return "tel";
}
if (attribute is DataTypeAttribute dataTypeAttribute)
{
switch (dataTypeAttribute.DataType)
{
case DataType.Password:
return "password";
case DataType.Date:
return "date";
case DataType.Time:
return "time";
case DataType.EmailAddress:
return "email";
case DataType.Url:
return "url";
case DataType.PhoneNumber:
return "tel";
case DataType.DateTime:
return "datetime-local";
}
}
return null;
}
private static string GetInputTypeFromTypeOrNull(Type type)
{
if (type == typeof(bool))
{
return "checkbox";
}
if (type == typeof(DateTime))
{
return "datetime-local";
}
if (NumberTypes.Contains(type))
{
return "number";
}
return null;
}
}
}

11
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc
{
public static class ActionResultHelper
{
public static List<Type> ObjectResultTypes { get; }
public static List<Type> ObjectResultTypes { get; }
static ActionResultHelper()
{
@ -20,10 +20,15 @@ namespace Volo.Abp.AspNetCore.Mvc
};
}
public static bool IsObjectResult(Type returnType)
public static bool IsObjectResult(Type returnType, params Type[] excludeTypes)
{
returnType = AsyncHelper.UnwrapTask(returnType);
if (!excludeTypes.IsNullOrEmpty() && excludeTypes.Any(t => t.IsAssignableFrom(returnType)))
{
return false;
}
if (!typeof(IActionResult).IsAssignableFrom(returnType))
{
return true;
@ -32,4 +37,4 @@ namespace Volo.Abp.AspNetCore.Mvc
return ObjectResultTypes.Any(t => t.IsAssignableFrom(returnType));
}
}
}
}

69
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/CachedObjectExtensionsDtoService.cs

@ -39,7 +39,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
{
var objectExtensionsDto = new ObjectExtensionsDto
{
Modules = new Dictionary<string, ModuleExtensionDto>()
Modules = new Dictionary<string, ModuleExtensionDto>(),
Enums = new Dictionary<string, ExtensionEnumDto>()
};
foreach (var moduleConfig in ObjectExtensionManager.Instance.Modules())
@ -47,6 +48,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
objectExtensionsDto.Modules[moduleConfig.Key] = CreateModuleExtensionDto(moduleConfig.Value);
}
FillEnums(objectExtensionsDto);
return objectExtensionsDto;
}
@ -104,9 +107,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
var extensionPropertyDto = new ExtensionPropertyDto
{
Type = TypeHelper.GetFullNameHandlingNullableAndGenerics(propertyConfig.Type),
TypeSimple = propertyConfig.Type.IsEnum
? "enum"
: TypeHelper.GetSimplifiedName(propertyConfig.Type),
TypeSimple = GetSimpleTypeName(propertyConfig),
Attributes = new List<ExtensionPropertyAttributeDto>(),
DisplayName = CreateDisplayNameDto(propertyConfig),
Configuration = new Dictionary<string, object>(),
@ -158,6 +159,26 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
return extensionPropertyDto;
}
protected virtual string GetSimpleTypeName(ExtensionPropertyConfiguration propertyConfig)
{
if (propertyConfig.Type.IsEnum)
{
return "enum";
}
if (propertyConfig.IsDate())
{
return "date";
}
if (propertyConfig.IsDateTime())
{
return "datetime";
}
return TypeHelper.GetSimplifiedName(propertyConfig.Type);
}
protected virtual LocalizableStringDto CreateDisplayNameDto(ExtensionPropertyConfiguration propertyConfig)
{
if (propertyConfig.DisplayName == null)
@ -183,5 +204,45 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending
return null;
}
protected virtual void FillEnums(ObjectExtensionsDto objectExtensionsDto)
{
var enumProperties = ObjectExtensionManager.Instance.Modules().Values
.SelectMany(
m => m.Entities.Values.SelectMany(
e => e.GetProperties()
)
)
.Where(p => p.Type.IsEnum)
.ToList();
foreach (var enumProperty in enumProperties)
{
// ReSharper disable once AssignNullToNotNullAttribute (enumProperty.Type.FullName can not be null for this case)
objectExtensionsDto.Enums[enumProperty.Type.FullName] = CreateExtensionEnumDto(enumProperty);
}
}
protected virtual ExtensionEnumDto CreateExtensionEnumDto(ExtensionPropertyConfiguration enumProperty)
{
var extensionEnumDto = new ExtensionEnumDto
{
Fields = new List<ExtensionEnumFieldDto>(),
LocalizationResource = enumProperty.GetLocalizationResourceNameOrNull()
};
foreach (var enumValue in enumProperty.Type.GetEnumValues())
{
extensionEnumDto.Fields.Add(
new ExtensionEnumFieldDto
{
Name = enumProperty.Type.GetEnumName(enumValue),
Value = enumValue
}
);
}
return extensionEnumDto;
}
}
}

18
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -25,7 +25,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
public AbpExceptionPageFilter(
IExceptionToErrorInfoConverter errorInfoConverter,
IHttpExceptionStatusCodeFinder statusCodeFinder,
IHttpExceptionStatusCodeFinder statusCodeFinder,
IJsonSerializer jsonSerializer)
{
_errorInfoConverter = errorInfoConverter;
@ -34,13 +34,12 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
Logger = NullLogger<AbpExceptionPageFilter>.Instance;
}
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{
return Task.CompletedTask;
}
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
if (context.HandlerMethod == null || !ShouldHandleException(context))
@ -54,20 +53,20 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
return;;
}
await HandleAndWrapException(pageHandlerExecutedContext);
}
protected virtual bool ShouldHandleException(PageHandlerExecutingContext context)
{
//TODO: Create DontWrap attribute to control wrapping..?
if (context.ActionDescriptor.IsPageAction() &&
ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType))
ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType, typeof(void)))
{
return true;
}
if (context.HttpContext.Request.CanAccept(MimeTypes.Application.Json))
{
return true;
@ -107,6 +106,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
context.Exception = null; //Handled!
}
}
}
}

1
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertiesDictionaryModelBinderProvider.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;

6
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ModelBinding/AbpExtraPropertyModelBinder.cs

@ -1,5 +1,4 @@
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Volo.Abp.ObjectExtending;
@ -61,7 +60,10 @@ namespace Volo.Abp.AspNetCore.Mvc.ModelBinding
return value;
}
return TypeHelper.ConvertFromString(propertyInfo.Type, value);
return TypeHelper.ConvertFromString(
propertyInfo.Type,
value
);
}
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs

@ -38,7 +38,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
context.HttpContext.Items["_AbpActionInfo"] = new AbpActionInfoInHttpContext
{
IsObjectResult = ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType)
IsObjectResult = ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType, typeof(void))
};
if (unitOfWorkAttr?.IsDisabled == true)
@ -71,7 +71,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
}
}
}
private AbpUnitOfWorkOptions CreateOptions(PageHandlerExecutingContext context, UnitOfWorkAttribute unitOfWorkAttribute)
{
var options = new AbpUnitOfWorkOptions();
@ -102,4 +102,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
return result.Exception == null || result.ExceptionHandled;
}
}
}
}

113
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs

@ -1,116 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
namespace Volo.Abp.ObjectExtending
{
public static class ObjectExtensionPropertyInfoAspNetCoreMvcExtensions
{
private static readonly HashSet<Type> NumberTypes = new HashSet<Type> {
typeof(int),
typeof(long),
typeof(byte),
typeof(sbyte),
typeof(short),
typeof(ushort),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(float),
typeof(double),
typeof(int?),
typeof(long?),
typeof(byte?),
typeof(sbyte?),
typeof(short?),
typeof(ushort?),
typeof(uint?),
typeof(long?),
typeof(ulong?),
typeof(float?),
typeof(double?),
private static readonly Type[] DateTimeTypes =
{
typeof(DateTime),
typeof(DateTimeOffset)
};
public static string GetInputType(this ObjectExtensionPropertyInfo propertyInfo)
public static bool IsDate(this IBasicObjectExtensionPropertyInfo property)
{
foreach (var attribute in propertyInfo.Attributes)
{
var inputTypeByAttribute = GetInputTypeFromAttributeOrNull(attribute);
if (inputTypeByAttribute != null)
{
return inputTypeByAttribute;
}
}
return GetInputTypeFromTypeOrNull(propertyInfo.Type)
?? "text"; //default
return DateTimeTypes.Contains(property.Type) &&
property.GetDataTypeOrNull() == DataType.Date;
}
private static string GetInputTypeFromAttributeOrNull(Attribute attribute)
public static bool IsDateTime(this IBasicObjectExtensionPropertyInfo property)
{
if (attribute is EmailAddressAttribute)
{
return "email";
}
if (attribute is UrlAttribute)
{
return "url";
}
if (attribute is HiddenInputAttribute)
{
return "hidden";
}
if (attribute is PhoneAttribute)
{
return "tel";
}
if (attribute is DataTypeAttribute dataTypeAttribute)
{
switch (dataTypeAttribute.DataType)
{
case DataType.Password:
return "password";
case DataType.Date:
return "date";
case DataType.Time:
return "time";
case DataType.EmailAddress:
return "email";
case DataType.Url:
return "url";
case DataType.PhoneNumber:
return "tel";
case DataType.DateTime:
return "datetime-local";
}
}
return null;
return DateTimeTypes.Contains(property.Type) &&
!property.IsDate();
}
private static string GetInputTypeFromTypeOrNull(Type type)
public static DataType? GetDataTypeOrNull(this IBasicObjectExtensionPropertyInfo property)
{
if (type == typeof(bool))
{
return "checkbox";
}
if (type == typeof(DateTime))
{
return "datetime-local";
}
if (NumberTypes.Contains(type))
{
return "number";
}
return null;
return property
.Attributes
.OfType<DataTypeAttribute>()
.FirstOrDefault()?.DataType;
}
}
}

17
framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs

@ -1,21 +1,28 @@
using Microsoft.AspNetCore.SignalR;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
namespace Volo.Abp.AspNetCore.SignalR
{
public class AbpSignalRUserIdProvider : IUserIdProvider, ITransientDependency
{
public ICurrentUser CurrentUser { get; }
public AbpSignalRUserIdProvider(ICurrentUser currentUser)
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
private readonly ICurrentUser _currentUser;
public AbpSignalRUserIdProvider(ICurrentPrincipalAccessor currentPrincipalAccessor, ICurrentUser currentUser)
{
CurrentUser = currentUser;
_currentPrincipalAccessor = currentPrincipalAccessor;
_currentUser = currentUser;
}
public virtual string GetUserId(HubConnectionContext connection)
{
return CurrentUser.Id?.ToString();
using (_currentPrincipalAccessor.Change(connection.User))
{
return _currentUser.Id?.ToString();
}
}
}
}

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs

@ -6,13 +6,16 @@ namespace Volo.Abp.AspNetCore.Security.Claims
{
public class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor
{
public override ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal;
private readonly IHttpContextAccessor _httpContextAccessor;
public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public override ClaimsPrincipal GetClaimsPrincipal()
{
return _httpContextAccessor.HttpContext?.User ?? base.GetClaimsPrincipal();
}
}
}

4
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs

@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.Cli.Args;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Templates.Console;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
@ -266,6 +267,7 @@ namespace Volo.Abp.Cli.Commands
protected virtual MobileApp GetMobilePreference(CommandLineArgs commandLineArgs)
{
var optionValue = commandLineArgs.Options.GetOrNull(Options.Mobile.Short, Options.Mobile.Long);
var template = commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long);
switch (optionValue)
{
case "none":
@ -273,7 +275,7 @@ namespace Volo.Abp.Cli.Commands
case "react-native":
return MobileApp.ReactNative;
default:
return MobileApp.ReactNative;
return ConsoleTemplate.TemplateName == template ? MobileApp.None : MobileApp.ReactNative;
}
}

9
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs

@ -12,6 +12,7 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.Cli.ProjectBuilding.Templates.Console;
using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
@ -63,12 +64,12 @@ namespace Volo.Abp.Cli.ProjectBuilding
Logger.LogWarning("The remote service is currently unavailable, please specify the version.");
Logger.LogWarning(string.Empty);
Logger.LogWarning("Find the following template in your cache directory: ");
Logger.LogWarning("\t Template Name\tVersion");
Logger.LogWarning("\tTemplate Name\tVersion");
var templateList = GetLocalTemplates();
foreach (var cacheFile in templateList)
{
Logger.LogWarning($"\t {cacheFile.TemplateName}\t\t{cacheFile.Version}");
Logger.LogWarning($"\t{cacheFile.TemplateName}\t\t{cacheFile.Version}");
}
Logger.LogWarning(string.Empty);
@ -241,7 +242,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
stringBuilder.AppendLine(cacheFile);
}
var matches = Regex.Matches(stringBuilder.ToString(), $"({AppTemplate.TemplateName}|{AppProTemplate.TemplateName}|{ModuleTemplate.TemplateName}|{ModuleProTemplate.TemplateName})-(.+).zip");
var matches = Regex.Matches(stringBuilder.ToString(), $"({AppTemplate.TemplateName}|{AppProTemplate.TemplateName}|{ModuleTemplate.TemplateName}|{ModuleProTemplate.TemplateName}|{ConsoleTemplate.TemplateName})-(.+).zip");
foreach (Match match in matches)
{
templateList.Add((match.Groups[1].Value, match.Groups[2].Value));
@ -278,4 +279,4 @@ namespace Volo.Abp.Cli.ProjectBuilding
public string Version { get; set; }
}
}
}
}

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs

@ -1,6 +1,7 @@
using System;
using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.Cli.ProjectBuilding.Templates.Console;
using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule;
using Volo.Abp.DependencyInjection;
@ -25,9 +26,11 @@ namespace Volo.Abp.Cli.ProjectBuilding
return new ModuleTemplate();
case ModuleProTemplate.TemplateName:
return new ModuleProTemplate();
case ConsoleTemplate.TemplateName:
return new ConsoleTemplate();
default:
throw new Exception("There is no template found with given name: " + name);
}
}
}
}
}

16
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs

@ -0,0 +1,16 @@
namespace Volo.Abp.Cli.ProjectBuilding.Templates.Console
{
public class ConsoleTemplate : ConsoleTemplateBase
{
/// <summary>
/// "console".
/// </summary>
public const string TemplateName = "console";
public ConsoleTemplate()
: base(TemplateName)
{
DocumentUrl = CliConsts.DocsLink + "/en/abp/latest/Getting-Started-Console-Application";
}
}
}

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs

@ -0,0 +1,13 @@
using JetBrains.Annotations;
using Volo.Abp.Cli.ProjectBuilding.Building;
namespace Volo.Abp.Cli.ProjectBuilding.Templates.Console
{
public abstract class ConsoleTemplateBase : TemplateInfo
{
protected ConsoleTemplateBase([NotNull] string name) :
base(name)
{
}
}
}

46
framework/src/Volo.Abp.Core/Volo/Abp/AbpInitializationException.cs

@ -0,0 +1,46 @@
using System;
using System.Runtime.Serialization;
namespace Volo.Abp
{
public class AbpInitializationException : AbpException
{
/// <summary>
/// Creates a new <see cref="AbpException"/> object.
/// </summary>
public AbpInitializationException()
{
}
/// <summary>
/// Creates a new <see cref="AbpException"/> object.
/// </summary>
/// <param name="message">Exception message</param>
public AbpInitializationException(string message)
: base(message)
{
}
/// <summary>
/// Creates a new <see cref="AbpException"/> object.
/// </summary>
/// <param name="message">Exception message</param>
/// <param name="innerException">Inner exception</param>
public AbpInitializationException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// Constructor for serializing.
/// </summary>
public AbpInitializationException(SerializationInfo serializationInfo, StreamingContext context)
: base(serializationInfo, context)
{
}
}
}

27
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleLoader.cs

@ -105,7 +105,14 @@ namespace Volo.Abp.Modularity
//PreConfigureServices
foreach (var module in modules.Where(m => m.Instance is IPreConfigureServices))
{
((IPreConfigureServices)module.Instance).PreConfigureServices(context);
try
{
((IPreConfigureServices)module.Instance).PreConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IPreConfigureServices.PreConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
//ConfigureServices
@ -119,13 +126,27 @@ namespace Volo.Abp.Modularity
}
}
module.Instance.ConfigureServices(context);
try
{
module.Instance.ConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IAbpModule.ConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
//PostConfigureServices
foreach (var module in modules.Where(m => m.Instance is IPostConfigureServices))
{
((IPostConfigureServices)module.Instance).PostConfigureServices(context);
try
{
((IPostConfigureServices)module.Instance).PostConfigureServices(context);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during {nameof(IPostConfigureServices.PostConfigureServices)} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
foreach (var module in modules)

22
framework/src/Volo.Abp.Core/Volo/Abp/Modularity/ModuleManager.cs

@ -34,11 +34,18 @@ namespace Volo.Abp.Modularity
{
LogListOfModules();
foreach (var Contributor in _lifecycleContributors)
foreach (var contributor in _lifecycleContributors)
{
foreach (var module in _moduleContainer.Modules)
{
Contributor.Initialize(context, module.Instance);
try
{
contributor.Initialize(context, module.Instance);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during the initialize {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
}
@ -59,11 +66,18 @@ namespace Volo.Abp.Modularity
{
var modules = _moduleContainer.Modules.Reverse().ToList();
foreach (var Contributor in _lifecycleContributors)
foreach (var contributor in _lifecycleContributors)
{
foreach (var module in modules)
{
Contributor.Shutdown(context, module.Instance);
try
{
contributor.Shutdown(context, module.Instance);
}
catch (Exception ex)
{
throw new AbpInitializationException($"An error occurred during the shutdown {contributor.GetType().FullName} phase of the module {module.Type.AssemblyQualifiedName}. See the inner exception for details.", ex);
}
}
}
}

54
framework/src/Volo.Abp.Core/Volo/Abp/Reflection/TypeHelper.cs

@ -2,14 +2,23 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace Volo.Abp.Reflection
{
public static class TypeHelper
{
private static readonly HashSet<Type> FloatingTypes = new HashSet<Type>
{
typeof(float),
typeof(double),
typeof(decimal)
};
private static readonly HashSet<Type> NonNullablePrimitiveTypes = new HashSet<Type>
{
typeof(byte),
@ -40,7 +49,7 @@ namespace Volo.Abp.Reflection
{
return false;
}
var type = obj.GetType();
if (!type.GetTypeInfo().IsGenericType)
{
@ -297,9 +306,39 @@ namespace Volo.Abp.Reflection
public static object ConvertFromString(Type targetType, string value)
{
return TypeDescriptor
.GetConverter(targetType)
.ConvertFromString(value);
if (value == null)
{
return null;
}
var converter = TypeDescriptor.GetConverter(targetType);
if (IsFloatingType(targetType))
{
using (CultureHelper.Use(CultureInfo.InvariantCulture))
{
return converter.ConvertFromString(value.Replace(',', '.'));
}
}
return converter.ConvertFromString(value);
}
public static bool IsFloatingType(Type type, bool includeNullable = true)
{
if (FloatingTypes.Contains(type))
{
return true;
}
if (includeNullable &&
IsNullable(type) &&
FloatingTypes.Contains(type.GenericTypeArguments[0]))
{
return true;
}
return false;
}
public static object ConvertFrom<TTargetType>(object value)
@ -313,5 +352,12 @@ namespace Volo.Abp.Reflection
.GetConverter(targetType)
.ConvertFrom(value);
}
public static Type StripNullable(Type type)
{
return IsNullable(type)
? type.GenericTypeArguments[0]
: type;
}
}
}

6
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/ValueConverters/ExtraPropertiesValueConverter.cs

@ -58,12 +58,14 @@ namespace Volo.Abp.EntityFrameworkCore.ValueConverters
return dictionary;
}
private static object GetNormalizedValue(Dictionary<string, object> dictionary, ObjectExtensionPropertyInfo property)
private static object GetNormalizedValue(
Dictionary<string, object> dictionary,
ObjectExtensionPropertyInfo property)
{
var value = dictionary.GetOrDefault(property.Name);
if (value == null)
{
return null;
return property.GetDefaultValue();
}
try

0
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizableString.cs → framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizableString.cs

0
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs → framework/src/Volo.Abp.Localization.Abstractions/Volo/Abp/Localization/LocalizationResourceNameAttribute.cs

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

@ -0,0 +1,50 @@
using Microsoft.Extensions.Localization;
namespace Volo.Abp.Localization
{
/// <summary>
/// This class is designed to be used internal by the framework.
/// </summary>
public static class AbpInternalLocalizationHelper
{
/// <summary>
/// Searches an array of keys in an array of localizers.
/// </summary>
/// <param name="localizers">
/// An array of localizers. Search the keys on the localizers.
/// Can contain null items in the array.
/// </param>
/// <param name="keys">
/// An array of keys. Search the keys on the localizers.
/// Should not contain null items in the array.
/// </param>
/// <param name="defaultValue">
/// Return value if none of the localizers has none of the keys.
/// </param>
/// <returns></returns>
public static string LocalizeWithFallback(
IStringLocalizer[] localizers,
string[] keys,
string defaultValue)
{
foreach (var key in keys)
{
foreach (var localizer in localizers)
{
if (localizer == null)
{
continue;
}
var localizedString = localizer[key];
if (!localizedString.ResourceNotFound)
{
return localizedString.Value;
}
}
}
return defaultValue;
}
}
}

38
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/IBasicObjectExtensionPropertyInfo.cs

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace Volo.Abp.ObjectExtending
{
public interface IBasicObjectExtensionPropertyInfo
{
[NotNull]
public string Name { get; }
[NotNull]
public Type Type { get; }
[NotNull]
public List<Attribute> Attributes { get; }
[NotNull]
public List<Action<ObjectExtensionPropertyValidationContext>> Validators { get; }
[CanBeNull]
public ILocalizableString DisplayName { get; }
/// <summary>
/// Uses as the default value if <see cref="DefaultValueFactory"/> was not set.
/// </summary>
[CanBeNull]
public object DefaultValue { get; set; }
/// <summary>
/// Used with the first priority to create the default value for the property.
/// Uses to the <see cref="DefaultValue"/> if this was not set.
/// </summary>
[CanBeNull]
public Func<object> DefaultValueFactory { get; set; }
}
}

2
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfiguration.cs

@ -6,7 +6,7 @@ using Volo.Abp.Reflection;
namespace Volo.Abp.ObjectExtending.Modularity
{
public class ExtensionPropertyConfiguration : IHasNameWithLocalizableDisplayName
public class ExtensionPropertyConfiguration : IHasNameWithLocalizableDisplayName, IBasicObjectExtensionPropertyInfo
{
[NotNull]
public EntityExtensionConfiguration EntityExtensionConfiguration { get; }

32
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ExtensionPropertyConfigurationExtensions.cs

@ -0,0 +1,32 @@
using System;
using Volo.Abp.Localization;
namespace Volo.Abp.ObjectExtending.Modularity
{
public static class ExtensionPropertyConfigurationExtensions
{
public static string GetLocalizationResourceNameOrNull(
this ExtensionPropertyConfiguration property)
{
var resourceType = property.GetLocalizationResourceTypeOrNull();
if (resourceType == null)
{
return null;
}
return LocalizationResourceNameAttribute.GetName(resourceType);
}
public static Type GetLocalizationResourceTypeOrNull(
this ExtensionPropertyConfiguration property)
{
if (property.DisplayName != null &&
property.DisplayName is LocalizableString localizableString)
{
return localizableString.ResourceType;
}
return null;
}
}
}

1
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/Modularity/ModuleExtensionConfigurationHelper.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Volo.Abp.ObjectExtending.Modularity

2
framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs

@ -7,7 +7,7 @@ using Volo.Abp.Reflection;
namespace Volo.Abp.ObjectExtending
{
public class ObjectExtensionPropertyInfo : IHasNameWithLocalizableDisplayName
public class ObjectExtensionPropertyInfo : IHasNameWithLocalizableDisplayName, IBasicObjectExtensionPropertyInfo
{
[NotNull]
public ObjectExtensionInfo ObjectExtension { get; }

5
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs

@ -1,9 +1,12 @@
using System.Security.Claims;
using System;
using System.Security.Claims;
namespace Volo.Abp.Security.Claims
{
public interface ICurrentPrincipalAccessor
{
ClaimsPrincipal Principal { get; }
IDisposable Change(ClaimsPrincipal principal);
}
}

29
framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs

@ -1,4 +1,5 @@
using System.Security.Claims;
using System;
using System.Security.Claims;
using System.Threading;
using Volo.Abp.DependencyInjection;
@ -6,6 +7,28 @@ namespace Volo.Abp.Security.Claims
{
public class ThreadCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency
{
public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal;
public ClaimsPrincipal Principal => _currentPrincipal.Value ?? GetClaimsPrincipal();
private readonly AsyncLocal<ClaimsPrincipal> _currentPrincipal = new AsyncLocal<ClaimsPrincipal>();
public virtual ClaimsPrincipal GetClaimsPrincipal()
{
return Thread.CurrentPrincipal as ClaimsPrincipal;
}
public virtual IDisposable Change(ClaimsPrincipal principal)
{
return SetCurrent(principal);
}
private IDisposable SetCurrent(ClaimsPrincipal principal)
{
var parent = Principal;
_currentPrincipal.Value = principal;
return new DisposeAction(() =>
{
_currentPrincipal.Value = parent;
});
}
}
}
}

50
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using Scriban;
using Scriban.Runtime;
using Scriban.Syntax;
namespace Volo.Abp.TextTemplating
{
public class TemplateLocalizer : IScriptCustomFunction
{
private readonly IStringLocalizer _localizer;
public TemplateLocalizer(IStringLocalizer localizer)
{
_localizer = localizer;
}
public object Invoke(TemplateContext context, ScriptNode callerContext, ScriptArray arguments,
ScriptBlockStatement blockStatement)
{
return GetString(arguments);
}
public ValueTask<object> InvokeAsync(TemplateContext context, ScriptNode callerContext, ScriptArray arguments,
ScriptBlockStatement blockStatement)
{
return new ValueTask<object>(GetString(arguments));
}
private string GetString(ScriptArray arguments)
{
if (arguments.IsNullOrEmpty())
{
return string.Empty;
}
var name = arguments[0];
if (name == null || name.ToString().IsNullOrWhiteSpace())
{
return string.Empty;
}
var args = arguments.Skip(1).Where(x => x != null && !x.ToString().IsNullOrWhiteSpace()).ToArray();
return args.Any() ? _localizer[name.ToString(), args] : _localizer[name.ToString()];
}
}
}

9
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs

@ -140,12 +140,7 @@ namespace Volo.Abp.TextTemplating
var localizer = GetLocalizerOrNull(templateDefinition);
if (localizer != null)
{
scriptObject.Import(
"L",
new Func<string, string>(
name => localizer[name]
)
);
scriptObject.SetValue("L", new TemplateLocalizer(localizer), true);
}
context.PushGlobal(scriptObject);
@ -163,4 +158,4 @@ namespace Volo.Abp.TextTemplating
return _stringLocalizerFactory.CreateDefaultOrNull();
}
}
}
}

4
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json

@ -12,7 +12,7 @@
"The field {0} must be between {1} and {2}.": "字段{0}值必须在{1}和{2}范围内.",
"The field {0} must match the regular expression '{1}'.": "字段{0}与请求的格式不匹配。",
"The {0} field is required.": "字段{0}不可为空.",
"The field {0} must be a string with a maximum length of {1}.": "字段{0}必须是长度为{1}的字符串.",
"The field {0} must be a string with a maximum length of {1}.": "字段{0}必须是最大长度为{1}的字符串.",
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "字段{0}必须是最小长度为{2}并且最大长度{1}的字符串.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "字段{0}不是有效的完全限定的http,https或ftp URL.",
"The field {0} is invalid.": "字段{0}是无效值.",
@ -31,4 +31,4 @@
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "字段{0}不是有效的完全限定的http,https或ftp URL.",
"ThisFieldIsInvalid.": "该字段无效."
}
}
}

2
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs

@ -29,4 +29,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Authorization
await next(context);
}
}
}
}

27
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs

@ -1,18 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{
public class ExceptionTestPage : AbpPageModel
{
public void OnGetUserFriendlyException1()
public void OnGetUserFriendlyException_void()
{
throw new UserFriendlyException("This is a sample exception!");
}
public IActionResult OnGetUserFriendlyException2()
public Task OnGetUserFriendlyException_Task()
{
throw new UserFriendlyException("This is a sample exception!");
}
public IActionResult OnGetUserFriendlyException_ActionResult()
{
throw new UserFriendlyException("This is a sample exception!");
}
public JsonResult OnGetUserFriendlyException_JsonResult()
{
throw new UserFriendlyException("This is a sample exception!");
}
public Task<JsonResult> OnGetUserFriendlyException_Task_JsonResult()
{
throw new UserFriendlyException("This is a sample exception!");
}
}
}
}

64
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs

@ -1,4 +1,4 @@
using System.Net;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@ -24,15 +24,33 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
}
[Fact]
public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Void_Return_Value()
public async Task Should_Not_Handle_Exceptions_For_Void_Return_Values()
{
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException1", HttpStatusCode.Forbidden);
result.Error.ShouldNotBeNull();
result.Error.Message.ShouldBe("This is a sample exception!");
await Assert.ThrowsAsync<UserFriendlyException>(
async () => await GetResponseAsStringAsync(
"/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_Void"
)
);
#pragma warning disable 4014
_fakeExceptionSubscriber
.Received()
.DidNotReceive()
.HandleAsync(Arg.Any<ExceptionNotificationContext>());
#pragma warning restore 4014
}
[Fact]
public async Task Should_Not_Handle_Exceptions_For_Task_Return_Values()
{
await Assert.ThrowsAsync<UserFriendlyException>(
async () => await GetResponseAsStringAsync(
"/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_Task"
)
);
#pragma warning disable 4014
_fakeExceptionSubscriber
.DidNotReceive()
.HandleAsync(Arg.Any<ExceptionNotificationContext>());
#pragma warning restore 4014
}
@ -41,8 +59,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
public async Task Should_Not_Handle_Exceptions_For_ActionResult_Return_Values()
{
await Assert.ThrowsAsync<UserFriendlyException>(
async () => await GetResponseAsObjectAsync<RemoteServiceErrorResponse>(
"/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException2"
async () => await GetResponseAsStringAsync(
"/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_ActionResult"
)
);
@ -50,7 +68,35 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
_fakeExceptionSubscriber
.DidNotReceive()
.HandleAsync(Arg.Any<ExceptionNotificationContext>());
#pragma warning restore 4014
}
[Fact]
public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Object_Return_Value()
{
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_JsonResult", HttpStatusCode.Forbidden);
result.Error.ShouldNotBeNull();
result.Error.Message.ShouldBe("This is a sample exception!");
#pragma warning disable 4014
_fakeExceptionSubscriber
.Received()
.HandleAsync(Arg.Any<ExceptionNotificationContext>());
#pragma warning restore 4014
}
[Fact]
public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Task_Object_Return_Value()
{
var result = await GetResponseAsObjectAsync<RemoteServiceErrorResponse>("/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_Task_JsonResult", HttpStatusCode.Forbidden);
result.Error.ShouldNotBeNull();
result.Error.Message.ShouldBe("This is a sample exception!");
#pragma warning disable 4014
_fakeExceptionSubscriber
.Received()
.HandleAsync(Arg.Any<ExceptionNotificationContext>());
#pragma warning restore 4014
}
}
}
}

6
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs

@ -14,9 +14,9 @@ namespace Volo.Abp.AspNetCore.Mvc.Features
}
[RequiresFeature("NotAllowedFeature")]
public void OnGetNotAllowedFeature()
public ObjectResult OnGetNotAllowedFeature()
{
return new ObjectResult(42);
}
public ObjectResult OnGetNoFeature()
@ -24,4 +24,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Features
return new ObjectResult(42);
}
}
}
}

8
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs

@ -32,23 +32,23 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
}
[UnitOfWork(isTransactional: true)]
public void OnGetHandledException()
public ObjectResult OnGetHandledException()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue();
throw new UserFriendlyException("This is a sample exception!");
}
public ObjectResult OnGetExceptionOnComplete()
{
CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse();
_testUnitOfWorkConfig.ThrowExceptionOnComplete = true;
//Prevent rendering of pages.
return new ObjectResult("");
}
}
}
}

7
framework/test/Volo.Abp.Core.Tests/Volo/Abp/Reflection/TypeHelper_Tests.cs

@ -80,6 +80,7 @@ namespace Volo.Abp.Reflection
TypeHelper.GetDefaultValue(typeof(byte)).ShouldBe(0);
TypeHelper.GetDefaultValue(typeof(int)).ShouldBe(0);
TypeHelper.GetDefaultValue(typeof(string)).ShouldBeNull();
TypeHelper.GetDefaultValue(typeof(MyEnum)).ShouldBe(MyEnum.EnumValue0);
}
[Fact]
@ -94,5 +95,11 @@ namespace Volo.Abp.Reflection
{
}
public enum MyEnum
{
EnumValue0,
EnumValue1,
}
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json

@ -0,0 +1,6 @@
{
"culture": "de",
"texts": {
"hello": "Hello"
}
}

20
framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ObjectExtensionManager_Tests.cs

@ -138,23 +138,27 @@ namespace Volo.Abp.ObjectExtending
.AddOrUpdateProperty<MyExtensibleObject, string>("StringPropWithCustomDefaultValue", property =>
{
property.DefaultValue = "custom-value";
})
.AddOrUpdateProperty<MyExtensibleObject, MyTestEnum>("EnumProp", property =>
{
property.DefaultValue = MyTestEnum.EnumValue2;
});
_objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("IntProp")
.DefaultValue.ShouldBe(0);
.GetDefaultValue().ShouldBe(0);
_objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("IntPropWithCustomDefaultValue")
.DefaultValue.ShouldBe(42);
.GetDefaultValue().ShouldBe(42);
_objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("BoolProp")
.DefaultValue.ShouldBe(false);
.GetDefaultValue().ShouldBe(false);
_objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("NullableIntProp")
.DefaultValue.ShouldBeNull();
.GetDefaultValue().ShouldBeNull();
var propWithDefaultValueFactory = _objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("NullableIntPropWithCustomDefaultValueFactory");
@ -162,11 +166,15 @@ namespace Volo.Abp.ObjectExtending
_objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("StringProp")
.DefaultValue.ShouldBeNull();
.GetDefaultValue().ShouldBeNull();
_objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("StringPropWithCustomDefaultValue")
.DefaultValue.ShouldBe("custom-value");
.GetDefaultValue().ShouldBe("custom-value");
_objectExtensionManager
.GetPropertyOrNull<MyExtensibleObject>("EnumProp")
.GetDefaultValue().ShouldBe(MyTestEnum.EnumValue2);
}
private class MyExtensibleObject : ExtensibleObject

50
framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs

@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Security.Claims;
using Shouldly;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Security.Claims
{
public class CurrentPrincipalAccessor_Test : AbpIntegratedTest<AbpSecurityTestModule>
{
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public CurrentPrincipalAccessor_Test()
{
_currentPrincipalAccessor = GetRequiredService<ICurrentPrincipalAccessor>();
}
[Fact]
public void Should_Get_Changed_Principal_If()
{
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.Name,"bob"),
new Claim(ClaimTypes.NameIdentifier,"123456")
}));
var claimsPrincipal2 = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.Name,"lee"),
new Claim(ClaimTypes.NameIdentifier,"654321")
}));
_currentPrincipalAccessor.Principal.ShouldBe(null);
using (_currentPrincipalAccessor.Change(claimsPrincipal))
{
_currentPrincipalAccessor.Principal.ShouldBe(claimsPrincipal);
using (_currentPrincipalAccessor.Change(claimsPrincipal2))
{
_currentPrincipalAccessor.Principal.ShouldBe(claimsPrincipal2);
}
_currentPrincipalAccessor.Principal.ShouldBe(claimsPrincipal);
}
_currentPrincipalAccessor.Principal.ShouldBeNull();
}
}
}

7
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json

@ -1,6 +1,7 @@
{
"culture": "en",
"texts": {
"HelloText": "Hello"
}
}
"HelloText": "Hello {0}",
"HowAreYou": "how are you?"
}
}

7
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json

@ -1,6 +1,7 @@
{
"culture": "tr",
"texts": {
"HelloText": "Merhaba"
}
}
"HelloText": "Merhaba {0}",
"HowAreYou": "nasılsın?"
}
}

2
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl

@ -1 +1 @@
{{L "HelloText"}}. Please click to the following link to get an email to reset your password!
{{L "HelloText" model.name}}, {{L "HowAreYou" }}. Please click to the following link to get an email to reset your password!

21
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs

@ -81,13 +81,15 @@ namespace Volo.Abp.TextTemplating
{
(await _templateRenderer.RenderAsync(
TestTemplates.ForgotPasswordEmail,
new ForgotPasswordEmailModel("John"),
cultureName: "en"
)).ShouldBe("*BEGIN*Hello. Please click to the following link to get an email to reset your password!*END*");
)).ShouldBe("*BEGIN*Hello John, how are you?. Please click to the following link to get an email to reset your password!*END*");
(await _templateRenderer.RenderAsync(
TestTemplates.ForgotPasswordEmail,
model: new Dictionary<string, object>() { { "name", "John" } },
cultureName: "tr"
)).ShouldBe("*BEGIN*Merhaba. Please click to the following link to get an email to reset your password!*END*");
)).ShouldBe("*BEGIN*Merhaba John, nasılsın?. Please click to the following link to get an email to reset your password!*END*");
}
private class WelcomeEmailModel
@ -104,5 +106,20 @@ namespace Volo.Abp.TextTemplating
Name = name;
}
}
private class ForgotPasswordEmailModel
{
public string Name { get; set; }
public ForgotPasswordEmailModel()
{
}
public ForgotPasswordEmailModel(string name)
{
Name = name;
}
}
}
}

12
modules/blogging/src/Volo.Blogging.Web/BloggingTwitterOptions.cs

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Volo.Blogging
{
public class BloggingTwitterOptions
{
public string Site { get; set; }
}
}

8
modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml

@ -8,6 +8,7 @@
@using Volo.Blogging.Pages.Blog.Posts
@using Volo.Blogging.Areas.Blog.Helpers.TagHelpers
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Prismjs
@using Volo.Blogging.SocialMedia
@inject IAuthorizationService Authorization
@inject IOptionsSnapshot<BloggingTwitterOptions> twitterOptions
@model DetailModel
@ -21,6 +22,11 @@
ViewBag.TwitterDescription = Model.Post.Description;
ViewBag.TwitterImage = $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Model.Post.CoverImage}";
ViewBag.LinkedInUrl = Request.GetEncodedUrl();
ViewBag.LinkedInTitle = Model.Post.Title;
ViewBag.LinkedInDescription = Model.Post.Description;
ViewBag.LinkedInImage = $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Model.Post.CoverImage}";
var hasCommentingPermission = CurrentUser.IsAuthenticated; //TODO: Apply real policy!
}
@section scripts {
@ -129,7 +135,7 @@
<h5>@L["TagsInThisArticle"]</h5>
@foreach (var tag in Model.Post.Tags)
{
<a asp-page="/Blog/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a>
<a asp-page="/Blogs/Posts/Index" asp-route-blogShortName="@Model.BlogShortName" asp-route-tagName="@tag.Name" class="tag">@tag.Name</a>
}
</div>
}

7
modules/blogging/src/Volo.Blogging.Web/SocialMedia/BloggingTwitterOptions.cs

@ -0,0 +1,7 @@
namespace Volo.Blogging.SocialMedia
{
public class BloggingTwitterOptions
{
public string Site { get; set; }
}
}

7
modules/docs/src/Volo.Docs.Admin.Application.Contracts/Volo/Docs/Admin/DocsAdminRemoteServiceConsts.cs

@ -0,0 +1,7 @@
namespace Volo.Docs.Admin
{
public static class DocsAdminRemoteServiceConsts
{
public const string RemoteServiceName = "AbpDocsAdmin";
}
}

2
modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo.Docs.Admin.HttpApi.Client.csproj

@ -12,6 +12,8 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Docs.Admin.Application.Contracts\Volo.Docs.Admin.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
</ItemGroup>
</Project>

12
modules/docs/src/Volo.Docs.Admin.HttpApi.Client/Volo/Docs/Admin/DocsHttpApiClientModule.cs

@ -1,11 +1,17 @@
using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
namespace Volo.Docs.Admin
{
[DependsOn(
typeof(DocsAdminApplicationContractsModule))]
typeof(DocsAdminApplicationContractsModule),
typeof(AbpHttpClientModule))]
public class DocsAdminHttpApiClientModule : AbpModule
{
//TODO: Create client proxies!
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(typeof(DocsAdminApplicationContractsModule).Assembly, DocsAdminRemoteServiceConsts.RemoteServiceName);
}
}
}

7
modules/docs/src/Volo.Docs.Application.Contracts/Volo/Docs/DocsRemoteServiceConsts.cs

@ -0,0 +1,7 @@
namespace Volo.Docs
{
public static class DocsRemoteServiceConsts
{
public const string RemoteServiceName = "AbpDocs";
}
}

1
modules/docs/src/Volo.Docs.HttpApi.Client/Volo.Docs.HttpApi.Client.csproj

@ -12,6 +12,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Docs.Application.Contracts\Volo.Docs.Application.Contracts.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
</ItemGroup>
</Project>

13
modules/docs/src/Volo.Docs.HttpApi.Client/Volo/Docs/DocsHttpApiClientModule.cs

@ -1,11 +1,18 @@
using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client;
using Volo.Abp.Modularity;
namespace Volo.Docs
{
[DependsOn(
typeof(DocsApplicationContractsModule))]
typeof(DocsApplicationContractsModule),
typeof(AbpHttpClientModule)
)]
public class DocsHttpApiClientModule : AbpModule
{
//TODO: Create client proxies
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHttpClientProxies(typeof(DocsApplicationContractsModule).Assembly, DocsRemoteServiceConsts.RemoteServiceName);
}
}
}

2
modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityUserLookupAppService.cs

@ -13,5 +13,7 @@ namespace Volo.Abp.Identity
Task<UserData> FindByUserNameAsync(string userName);
Task<ListResultDto<UserData>> SearchAsync(UserLookupSearchInputDto input);
Task<long> GetCountAsync(UserLookupCountInputDto input);
}
}

7
modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupCountInputDto.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Identity
{
public class UserLookupCountInputDto
{
public string Filter { get; set; }
}
}

4
modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupSearchInputDto.cs

@ -2,10 +2,8 @@
namespace Volo.Abp.Identity
{
public class UserLookupSearchInputDto : LimitedResultRequestDto, ISortedResultRequest
public class UserLookupSearchInputDto : PagedAndSortedResultRequestDto
{
public string Sorting { get; set; }
public string Filter { get; set; }
}
}

8
modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserLookupAppService.cs

@ -45,7 +45,8 @@ namespace Volo.Abp.Identity
var users = await UserLookupServiceProvider.SearchAsync(
input.Sorting,
input.Filter,
input.MaxResultCount
input.MaxResultCount,
input.SkipCount
);
return new ListResultDto<UserData>(
@ -54,5 +55,10 @@ namespace Volo.Abp.Identity
.ToList()
);
}
public async Task<long> GetCountAsync(UserLookupCountInputDto input)
{
return await UserLookupServiceProvider.GetCountAsync(input.Filter);
}
}
}

2
modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json

@ -72,7 +72,7 @@
"Permission:ChangePermissions": "İzinleri değiştirme",
"Permission:UserManagement": "Kullanıcı yönetimi",
"Permission:UserLookup": "Kullanıcı sorgulama",
"DisplayName:Abp.Identity.Password.RequiredLength": "Uzunluk gerekli",
"DisplayName:Abp.Identity.Password.RequiredLength": "Uzunluk gerekli",
"DisplayName:Abp.Identity.Password.RequiredUniqueChars": "Tekil karakter gerekli",
"DisplayName:Abp.Identity.Password.RequireNonAlphanumeric": "Alfasayısal olmayan karakter gerekli",
"DisplayName:Abp.Identity.Password.RequireLowercase": "Küçük harf gerekli",

15
modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRepositoryExternalUserLookupServiceProvider.cs

@ -49,14 +49,16 @@ namespace Volo.Abp.Identity
}
public virtual async Task<List<IUserData>> SearchAsync(
string sorting,
string filter,
int maxResultCount,
string sorting = null,
string filter = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
CancellationToken cancellationToken = default)
{
var users = await UserRepository.GetListAsync(
sorting: sorting,
maxResultCount: maxResultCount,
skipCount: skipCount,
filter: filter,
includeDetails: false,
cancellationToken: cancellationToken
@ -64,5 +66,12 @@ namespace Volo.Abp.Identity
return users.Select(u => u.ToAbpUserData()).ToList();
}
public async Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = new CancellationToken())
{
return await UserRepository.GetCountAsync(filter, cancellationToken);
}
}
}

7
modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs

@ -111,7 +111,8 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter) ||
(u.PhoneNumber != null && u.PhoneNumber.Contains(filter))
(u.Name != null && u.Name.Contains(filter)) ||
(u.Surname != null && u.Surname.Contains(filter))
)
.OrderBy(sorting ?? nameof(IdentityUser.UserName))
.PageBy(skipCount, maxResultCount)
@ -139,7 +140,9 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter)
u.Email.Contains(filter) ||
(u.Name != null && u.Name.Contains(filter)) ||
(u.Surname != null && u.Surname.Contains(filter))
)
.LongCountAsync(GetCancellationToken(cancellationToken));
}

24
modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientIdentityUserLookupService.cs

@ -29,20 +29,36 @@ namespace Volo.Abp.Identity
}
public async Task<List<IUserData>> SearchAsync(
string sorting,
string filter,
int maxResultCount,
string sorting = null,
string filter = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
CancellationToken cancellationToken = default)
{
var result = await UserLookupAppService.SearchAsync(
new UserLookupSearchInputDto
{
Filter = filter,
MaxResultCount = maxResultCount
MaxResultCount = maxResultCount,
SkipCount = skipCount,
Sorting = sorting
}
);
return result.Items.Cast<IUserData>().ToList();
}
public async Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = new CancellationToken())
{
return await UserLookupAppService
.GetCountAsync(
new UserLookupCountInputDto
{
Filter = filter
}
);
}
}
}

7
modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs

@ -40,5 +40,12 @@ namespace Volo.Abp.Identity
{
return LookupAppService.SearchAsync(input);
}
[HttpGet]
[Route("count")]
public Task<long> GetCountAsync(UserLookupCountInputDto input)
{
return LookupAppService.GetCountAsync(input);
}
}
}

28
modules/identity/src/Volo.Abp.Identity.MongoDB/Volo/Abp/Identity/MongoDB/MongoIdentityUserRepository.cs

@ -15,13 +15,13 @@ namespace Volo.Abp.Identity.MongoDB
{
public class MongoIdentityUserRepository : MongoDbRepository<IAbpIdentityMongoDbContext, IdentityUser, Guid>, IIdentityUserRepository
{
public MongoIdentityUserRepository(IMongoDbContextProvider<IAbpIdentityMongoDbContext> dbContextProvider)
public MongoIdentityUserRepository(IMongoDbContextProvider<IAbpIdentityMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public virtual async Task<IdentityUser> FindByNormalizedUserNameAsync(
string normalizedUserName,
string normalizedUserName,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
@ -33,7 +33,7 @@ namespace Volo.Abp.Identity.MongoDB
}
public virtual async Task<List<string>> GetRoleNamesAsync(
Guid id,
Guid id,
CancellationToken cancellationToken = default)
{
var user = await GetAsync(id, cancellationToken: GetCancellationToken(cancellationToken));
@ -42,8 +42,8 @@ namespace Volo.Abp.Identity.MongoDB
}
public virtual async Task<IdentityUser> FindByLoginAsync(
string loginProvider,
string providerKey,
string loginProvider,
string providerKey,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
@ -71,7 +71,7 @@ namespace Volo.Abp.Identity.MongoDB
}
public virtual async Task<List<IdentityUser>> GetListByNormalizedRoleNameAsync(
string normalizedRoleName,
string normalizedRoleName,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
@ -89,9 +89,9 @@ namespace Volo.Abp.Identity.MongoDB
public virtual async Task<List<IdentityUser>> GetListAsync(
string sorting = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
bool includeDetails = false,
CancellationToken cancellationToken = default)
{
@ -100,7 +100,9 @@ namespace Volo.Abp.Identity.MongoDB
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter)
u.Email.Contains(filter) ||
(u.Name != null && u.Name.Contains(filter)) ||
(u.Surname != null && u.Surname.Contains(filter))
)
.OrderBy(sorting ?? nameof(IdentityUser.UserName))
.As<IMongoQueryable<IdentityUser>>()
@ -127,9 +129,11 @@ namespace Volo.Abp.Identity.MongoDB
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter)
u.Email.Contains(filter) ||
(u.Name != null && u.Name.Contains(filter)) ||
(u.Surname != null && u.Surname.Contains(filter))
)
.LongCountAsync(GetCancellationToken(cancellationToken));
}
}
}
}

12
modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs

@ -12,9 +12,15 @@ namespace Volo.Abp.Users
Task<IUserData> FindByUserNameAsync(string userName, CancellationToken cancellationToken = default);
Task<List<IUserData>> SearchAsync(
string sorting,
string filter,
int maxResultCount,
string sorting = null,
string filter = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
CancellationToken cancellationToken = default);
Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default
);
}
}

21
modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs

@ -8,14 +8,25 @@ namespace Volo.Abp.Users
public interface IUserLookupService<TUser>
where TUser : class, IUser
{
Task<TUser> FindByIdAsync(Guid id, CancellationToken cancellationToken = default);
Task<TUser> FindByIdAsync(
Guid id,
CancellationToken cancellationToken = default
);
Task<TUser> FindByUserNameAsync(string userName, CancellationToken cancellationToken = default);
Task<TUser> FindByUserNameAsync(
string userName,
CancellationToken cancellationToken = default
);
Task<List<IUserData>> SearchAsync(
string sorting,
string filter,
int maxResultCount,
string sorting = null,
string filter = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
CancellationToken cancellationToken = default);
Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default);
}
}

4
modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs

@ -21,5 +21,9 @@ namespace Volo.Abp.Users
string filter = null,
CancellationToken cancellationToken = default
);
Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default);
}
}

50
modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs

@ -140,15 +140,57 @@ namespace Volo.Abp.Users
return await _userRepository.FindAsync(externalUser.Id, cancellationToken: cancellationToken);
}
public async Task<List<IUserData>> SearchAsync(string sorting, string filter, int maxResultCount, CancellationToken cancellationToken = default)
public async Task<List<IUserData>> SearchAsync(
string sorting = null,
string filter = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
CancellationToken cancellationToken = default)
{
if (ExternalUserLookupServiceProvider != null)
{
return await ExternalUserLookupServiceProvider.SearchAsync(sorting, filter, maxResultCount, cancellationToken);
return await ExternalUserLookupServiceProvider
.SearchAsync(
sorting,
filter,
maxResultCount,
skipCount,
cancellationToken
);
}
var localUsers = await _userRepository
.SearchAsync(
sorting,
maxResultCount,
skipCount,
filter,
cancellationToken
);
return localUsers
.Cast<IUserData>()
.ToList();
}
public async Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default)
{
if (ExternalUserLookupServiceProvider != null)
{
return await ExternalUserLookupServiceProvider
.GetCountAsync(
filter,
cancellationToken
);
}
var localUsers = await _userRepository.SearchAsync(sorting, maxResultCount, 0, filter, cancellationToken);
return localUsers.Cast<IUserData>().ToList();
return await _userRepository
.GetCountAsync(
filter,
cancellationToken
);
}
protected abstract TUser CreateUser(IUserData externalUser);

22
modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs

@ -32,7 +32,7 @@ namespace Volo.Abp.Users.EntityFrameworkCore
public async Task<List<TUser>> SearchAsync(
string sorting = null,
int maxResultCount = Int32.MaxValue,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
CancellationToken cancellationToken = default)
@ -42,11 +42,29 @@ namespace Volo.Abp.Users.EntityFrameworkCore
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter)
u.Email.Contains(filter) ||
u.Name.Contains(filter) ||
u.Surname.Contains(filter)
)
.OrderBy(sorting ?? nameof(IUser.UserName))
.PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<long> GetCountAsync(
string filter = null,
CancellationToken cancellationToken = default)
{
return await DbSet
.WhereIf(
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter) ||
u.Name.Contains(filter) ||
u.Surname.Contains(filter)
)
.LongCountAsync(GetCancellationToken(cancellationToken));
}
}
}

24
modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs

@ -31,7 +31,11 @@ namespace Volo.Abp.Users.MongoDB
return await GetMongoQueryable().Where(u => ids.Contains(u.Id)).ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<List<TUser>> SearchAsync(string sorting = null, int maxResultCount = Int32.MaxValue, int skipCount = 0, string filter = null,
public async Task<List<TUser>> SearchAsync(
string sorting = null,
int maxResultCount = int.MaxValue,
int skipCount = 0,
string filter = null,
CancellationToken cancellationToken = default)
{
return await GetMongoQueryable()
@ -39,12 +43,28 @@ namespace Volo.Abp.Users.MongoDB
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter)
u.Email.Contains(filter) ||
u.Name.Contains(filter) ||
u.Surname.Contains(filter)
)
.OrderBy(sorting ?? nameof(IUserData.UserName))
.As<IMongoQueryable<TUser>>()
.PageBy<TUser, IMongoQueryable<TUser>>(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<long> GetCountAsync(string filter = null, CancellationToken cancellationToken = default)
{
return await GetMongoQueryable()
.WhereIf<TUser, IMongoQueryable<TUser>>(
!filter.IsNullOrWhiteSpace(),
u =>
u.UserName.Contains(filter) ||
u.Email.Contains(filter) ||
u.Name.Contains(filter) ||
u.Surname.Contains(filter)
)
.LongCountAsync(GetCancellationToken(cancellationToken));
}
}
}

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

Loading…
Cancel
Save