Browse Source

Merge branch 'dev' into liangshiwei/virtualfilesystem-explorer-module

pull/3971/head
liangshiwei 6 years ago
committed by GitHub
parent
commit
57c0d40ef2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      .gitignore
  2. 10
      docs/en/CLI.md
  3. 4
      docs/en/Customizing-Application-Modules-Guide.md
  4. 6
      docs/en/Dapper.md
  5. 4
      docs/en/Microservice-Architecture.md
  6. 14
      docs/en/SignalR-Integration.md
  7. 8
      docs/en/Text-Templating.md
  8. 4
      docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
  9. 10
      docs/zh-Hans/Dapper.md
  10. 38
      docs/zh-Hans/Object-Extensions.md
  11. 3
      docs/zh-Hans/Samples/Index.md
  12. 455
      docs/zh-Hans/Text-Templating.md
  13. 8
      docs/zh-Hans/UI/Angular/Confirmation-Service.md
  14. 4
      docs/zh-Hans/UI/Angular/Track-By-Service.md
  15. 4
      docs/zh-Hans/docs-nav.json
  16. 9
      framework/Volo.Abp.sln
  17. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json
  18. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Bootstrap/BootstrapStyleContributor.cs
  19. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  20. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
  21. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
  22. 11
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs
  23. 18
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
  24. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs
  25. 17
      framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs
  26. 10
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  27. 46
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs
  28. 21
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapOptions.cs
  29. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs
  30. 3
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs
  31. 24
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs
  32. 2
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs
  33. 19
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs
  34. 46
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobQuartzOptions.cs
  35. 18
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobsQuartzModule.cs
  36. 22
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManageExtensions.cs
  37. 33
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs
  38. 29
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs
  39. 10
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkerQuartzOptions.cs
  40. 31
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs
  41. 8
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorker.cs
  42. 7
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerBase.cs
  43. 36
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs
  44. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  45. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs
  46. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs
  47. 16
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs
  48. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs
  49. 6
      framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
  50. 6
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/ar.json
  51. 23
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/ar.json
  52. 8
      framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs
  53. 7
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/ar.json
  54. 31
      framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs
  55. 48
      framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzOptions.cs
  56. 23
      framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzPreOptions.cs
  57. 5
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs
  58. 29
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs
  59. 50
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs
  60. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs
  61. 6
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/ar.json
  62. 34
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json
  63. 4
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json
  64. 13
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs
  65. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs
  66. 27
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs
  67. 64
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs
  68. 6
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs
  69. 7
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/ar.json
  70. 22
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Claims/ClaimsMapTestController.cs
  71. 41
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Claims/ClaimsMapTestController_Tests.cs
  72. 8
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs
  73. 10257
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.css
  74. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.css.map
  75. 9
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.min.css
  76. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.min.css.map
  77. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.css.map
  78. 7
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.min.css
  79. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.min.css.map
  80. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/js/bootstrap.bundle.js.map
  81. 7
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/js/bootstrap.bundle.min.js
  82. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/js/bootstrap.bundle.min.js.map
  83. 26
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Program.cs
  84. 27
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Properties/launchSettings.json
  85. 15
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj
  86. 6
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/AbpAspNetCoreMvcUiThemeSharedTestBase.cs
  87. 19
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/AbpAspNetCoreMvcUiThemeSharedTestModule.cs
  88. 113
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/PageToolbars/PageToolbar_Tests.cs
  89. 19
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Startup.cs
  90. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/ar.json
  91. 7
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/ar.json
  92. 7
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/ar.json
  93. 11
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/ar.json
  94. 6
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/ar.json
  95. 50
      framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Claims/CurrentPrincipalAccessor_Test.cs
  96. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json
  97. 7
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json
  98. 2
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl
  99. 21
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs
  100. 45
      modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json

10
.gitignore

@ -293,12 +293,14 @@ samples/MicroserviceDemo/applications/ConsoleClientDemo/Logs/logs.txt
modules/docs/app/Volo.DocsTestApp/Logs/logs.txt
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Logs/logs.txt
samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.txt
/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json
/samples/BookStore/src/Acme.BookStore.Web/package-lock.json
/samples/DashboardDemo/src/DashboardDemo.Web/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package-lock.json
/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package-lock.json
/npm/packs/bootstrap/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package-lock.json
modules/virtual-file-explorer/app/Volo.Abp.VirtualFileExplorer.DemoApp/Logs/
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package-lock.json
/templates/app/angular/package-lock.json
/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json

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.

4
docs/en/Customizing-Application-Modules-Guide.md

@ -1,6 +1,6 @@
# Customizing the Existing Modules
ABP Framework provides was designed to support to build fully [modular applications](Module-Development-Basics.md) and systems. It also provides some [pre-built application modules](Modules/Index.md) those are **ready to use** in any kind of application.
ABP Framework has been designed to support to build fully [modular applications](Module-Development-Basics.md) and systems. It also provides some [pre-built application modules](Modules/Index.md) those are **ready to use** in any kind of application.
For example, you can **re-use** the [Identity Management Module](Modules/Identity.md) to add user, role and permission management to your application. The [application startup template](Startup-Templates/Application.md) already comes with Identity and some other modules **pre-installed**.
@ -59,4 +59,4 @@ Also, see the following documents:
* See [the localization document](Localization.md) to learn how to extend existing localization resources.
* See [the settings document](Settings.md) to learn how to change setting definitions of a depended module.
* See [the authorization document](Authorization.md) to learn how to change permission definitions of a depended module.
* See [the authorization document](Authorization.md) to learn how to change permission definitions of a depended module.

6
docs/en/Dapper.md

@ -2,13 +2,15 @@
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 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).
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
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.
`Volo.Abp.Dapper` is the main nuget package for the Dapper integration.
You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper

4
docs/en/Microservice-Architecture.md

@ -23,8 +23,8 @@ One common advise to start a new solution is **always to start with a monolith**
However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts.
ABP can help you in that point by oferring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration.
ABP can help you in that point by offerring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration.
## Microservice Demo Solution
The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework.
The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework.

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序列化"
},

9
framework/Volo.Abp.sln

@ -287,7 +287,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Validation.Abstrac
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR", "src\Volo.Abp.AspNetCore.SignalR\Volo.Abp.AspNetCore.SignalR.csproj", "{B64FCE08-E9D2-4984-BF12-FE199F257416}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.SignalR.Tests", "test\Volo.Abp.AspNetCore.SignalR.Tests\Volo.Abp.AspNetCore.SignalR.Tests.csproj", "{8B758716-DCC9-4223-8421-5588D1597487}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR.Tests", "test\Volo.Abp.AspNetCore.SignalR.Tests\Volo.Abp.AspNetCore.SignalR.Tests.csproj", "{8B758716-DCC9-4223-8421-5588D1597487}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj", "{79323211-E658-493E-9863-035AA4C3F913}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -859,6 +861,10 @@ Global
{8B758716-DCC9-4223-8421-5588D1597487}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B758716-DCC9-4223-8421-5588D1597487}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B758716-DCC9-4223-8421-5588D1597487}.Release|Any CPU.Build.0 = Release|Any CPU
{79323211-E658-493E-9863-035AA4C3F913}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79323211-E658-493E-9863-035AA4C3F913}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79323211-E658-493E-9863-035AA4C3F913}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79323211-E658-493E-9863-035AA4C3F913}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1005,6 +1011,7 @@ Global
{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{B64FCE08-E9D2-4984-BF12-FE199F257416} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{8B758716-DCC9-4223-8421-5588D1597487} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{79323211-E658-493E-9863-035AA4C3F913} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

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

@ -0,0 +1,12 @@
{
"culture": "ar",
"texts": {
"GivenTenantIsNotAvailable": "الجهة المحددة غير متاحة: {0}",
"Tenant": "الجهة",
"Switch": "تغيير",
"Name": "اسم",
"SwitchTenantHint": "اترك حقل الاسم فارغًا للتبديل إلى المضيف.",
"SwitchTenant": "تغيير الجهة",
"NotSelected": "غير محدد"
}
}

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Bootstrap/BootstrapStyleContributor.cs

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap
{
@ -7,7 +8,14 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/bootstrap/css/bootstrap.css");
if (CultureHelper.IsRtl)
{
context.Files.AddIfNotContains("/libs/bootstrap/css/bootstrap-rtl.css");
}
else
{
context.Files.AddIfNotContains("/libs/bootstrap/css/bootstrap.css");
}
}
}
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml

@ -12,6 +12,7 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.MultiTenancy
@using Volo.Abp.Localization
@inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider
@inject IOptions<AbpMultiTenancyOptions> MultiTenancyOptions
@ -23,12 +24,11 @@
Layout = null;
AbpAntiForgeryManager.SetCookie();
var containerClass = ViewBag.FluidLayout == true ? "container-fluid" : "container"; //TODO: Better and type-safe options
}
<!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name">
<html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")>
<head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Account)

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml

@ -8,6 +8,7 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.Localization
@inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider
@inject IPageLayout PageLayout
@ -31,7 +32,7 @@
<!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name">
<html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")>
<head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Application)

5
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml

@ -7,6 +7,7 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.Localization
@inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider
@inject IPageLayout PageLayout
@ -30,7 +31,7 @@
<!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name">
<html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")>
<head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Empty)
@ -39,7 +40,7 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>@pageTitle</title>
@if (ViewBag.Description!=null)
@if (ViewBag.Description != null)
{
<meta name="description" content="@ViewBag.Description" />
}

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

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

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

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

10
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs

@ -1,4 +1,4 @@
using System;
using System;
using JetBrains.Annotations;
using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection;
@ -6,6 +6,7 @@ using Microsoft.Extensions.Hosting;
using Volo.Abp;
using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Security.Claims;
using Volo.Abp.AspNetCore.Tracing;
using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection;
@ -76,5 +77,10 @@ namespace Microsoft.AspNetCore.Builder
app.Properties[ExceptionHandlingMiddlewareMarker] = true;
return app.UseMiddleware<AbpExceptionHandlingMiddleware>();
}
public static IApplicationBuilder UseAbpClaimsMap(this IApplicationBuilder app)
{
return app.UseMiddleware<AbpClaimsMapMiddleware>();
}
}
}
}

46
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs

@ -0,0 +1,46 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Security.Claims
{
public class AbpClaimsMapMiddleware : IMiddleware, ITransientDependency
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var currentPrincipalAccessor = context.RequestServices
.GetRequiredService<ICurrentPrincipalAccessor>();
var mapOptions = context.RequestServices
.GetRequiredService<IOptions<AbpClaimsMapOptions>>().Value;
var mapClaims = currentPrincipalAccessor
.Principal
.Claims
.Where(claim => mapOptions.Maps.Keys.Contains(claim.Type));
currentPrincipalAccessor
.Principal
.AddIdentity(
new ClaimsIdentity(
mapClaims
.Select(
claim => new Claim(
mapOptions.Maps[claim.Type](),
claim.Value,
claim.ValueType,
claim.Issuer
)
)
)
);
await next(context);
}
}
}

21
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapOptions.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.AspNetCore.Security.Claims
{
public class AbpClaimsMapOptions
{
public Dictionary<string, Func<string>> Maps { get; }
public AbpClaimsMapOptions()
{
Maps = new Dictionary<string, Func<string>>()
{
{ "sub", () => AbpClaimTypes.UserId },
{ "role", () => AbpClaimTypes.Role },
{ "email", () => AbpClaimTypes.Email },
};
}
}
}

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

3
framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs

@ -8,8 +8,7 @@ namespace Volo.Abp.BackgroundJobs
{
private readonly Dictionary<Type, BackgroundJobConfiguration> _jobConfigurationsByArgsType;
private readonly Dictionary<string, BackgroundJobConfiguration> _jobConfigurationsByName;
//TODO: Implement for all providers! (Hangfire does not implement yet)
/// <summary>
/// Default: true.
/// </summary>

24
framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs

@ -1,4 +1,8 @@
using Volo.Abp.Hangfire;
using System;
using Hangfire;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Hangfire;
using Volo.Abp.Modularity;
namespace Volo.Abp.BackgroundJobs.Hangfire
@ -6,9 +10,23 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
[DependsOn(
typeof(AbpBackgroundJobsAbstractionsModule),
typeof(AbpHangfireModule)
)]
)]
public class AbpBackgroundJobsHangfireModule : AbpModule
{
public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpBackgroundJobOptions>>().Value;
if (!options.IsJobExecutionEnabled)
{
var hangfireOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpHangfireOptions>>().Value;
hangfireOptions.BackgroundJobServerFactory = CreateOnlyEnqueueJobServer;
}
}
private BackgroundJobServer CreateOnlyEnqueueJobServer(IServiceProvider serviceProvider)
{
serviceProvider.GetRequiredService<JobStorage>();
return null;
}
}
}
}

2
framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
[Dependency(ReplaceServices = true)]
public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDependency
{
public Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
public virtual Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
TimeSpan? delay = null)
{
if (!delay.HasValue)

19
framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs

@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Threading;
@ -11,8 +13,8 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
protected IBackgroundJobExecuter JobExecuter { get; }
public HangfireJobExecutionAdapter(
IOptions<AbpBackgroundJobOptions> options,
IBackgroundJobExecuter jobExecuter,
IOptions<AbpBackgroundJobOptions> options,
IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory)
{
JobExecuter = jobExecuter;
@ -22,6 +24,17 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
public void Execute(TArgs args)
{
if (!Options.IsJobExecutionEnabled)
{
throw new AbpException(
"Background job execution is disabled. " +
"This method should not be called! " +
"If you want to enable the background job execution, " +
$"set {nameof(AbpBackgroundJobOptions)}.{nameof(AbpBackgroundJobOptions.IsJobExecutionEnabled)} to true! " +
"If you've intentionally disabled job execution and this seems a bug, please report it."
);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var jobType = Options.GetJob(typeof(TArgs)).JobType;
@ -30,4 +43,4 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
}
}
}
}
}

46
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobQuartzOptions.cs

@ -0,0 +1,46 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Quartz;
namespace Volo.Abp.BackgroundJobs.Quartz
{
public class AbpBackgroundJobQuartzOptions
{
public int RetryCount { get; set; }
public int RetryIntervalMillisecond { get; set; }
[NotNull]
public Func<int, IJobExecutionContext, JobExecutionException,Task> RetryStrategy
{
get => _retryStrategy;
set => _retryStrategy = Check.NotNull(value, nameof(value));
}
private Func<int, IJobExecutionContext, JobExecutionException,Task> _retryStrategy;
public AbpBackgroundJobQuartzOptions()
{
RetryCount = 3;
RetryIntervalMillisecond = 3000;
_retryStrategy = DefaultRetryStrategy;
}
private async Task DefaultRetryStrategy(int retryIndex, IJobExecutionContext executionContext, JobExecutionException exception)
{
exception.RefireImmediately = true;
var retryCount = executionContext.JobDetail.JobDataMap.GetIntValue(QuartzBackgroundJobManager.JobDataPrefix+ nameof(RetryCount));
if (retryIndex > retryCount)
{
exception.RefireImmediately = false;
exception.UnscheduleAllTriggers = true;
return;
}
var retryInterval = executionContext.JobDetail.JobDataMap.GetIntValue(QuartzBackgroundJobManager.JobDataPrefix+ nameof(RetryIntervalMillisecond));
await Task.Delay(retryInterval);
}
}
}

18
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobsQuartzModule.cs

@ -1,4 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Modularity;
using Volo.Abp.Quartz;
@ -8,11 +10,21 @@ namespace Volo.Abp.BackgroundJobs.Quartz
typeof(AbpBackgroundJobsAbstractionsModule),
typeof(AbpQuartzModule)
)]
public class AbpBackgroundJobsQuartzModule :AbpModule
public class AbpBackgroundJobsQuartzModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddTransient(typeof(QuartzJobExecutionAdapter<>));
}
public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
{
var options = context.ServiceProvider.GetService<IOptions<AbpBackgroundJobOptions>>().Value;
if (!options.IsJobExecutionEnabled)
{
var quartzOptions = context.ServiceProvider.GetService<IOptions<AbpQuartzOptions>>().Value;
quartzOptions.StartSchedulerFactory = scheduler => Task.CompletedTask;
}
}
}
}
}

22
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManageExtensions.cs

@ -0,0 +1,22 @@
using System;
using System.Threading.Tasks;
using Quartz;
namespace Volo.Abp.BackgroundJobs.Quartz
{
public static class QuartzBackgroundJobManageExtensions
{
public static async Task<string> EnqueueAsync<TArgs>(this IBackgroundJobManager backgroundJobManager,
TArgs args, int retryCount, int retryIntervalMillisecond,
BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null)
{
if (backgroundJobManager is QuartzBackgroundJobManager quartzBackgroundJobManager)
{
return await quartzBackgroundJobManager.ReEnqueueAsync(args, retryCount, retryIntervalMillisecond,
priority, delay);
}
return null;
}
}
}

33
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs

@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Quartz;
using Volo.Abp.DependencyInjection;
@ -8,19 +9,39 @@ namespace Volo.Abp.BackgroundJobs.Quartz
[Dependency(ReplaceServices = true)]
public class QuartzBackgroundJobManager : IBackgroundJobManager, ITransientDependency
{
private readonly IScheduler _scheduler;
public const string JobDataPrefix = "Abp";
public const string RetryIndex = "RetryIndex";
public QuartzBackgroundJobManager(IScheduler scheduler)
protected IScheduler Scheduler { get; }
protected AbpBackgroundJobQuartzOptions Options { get; }
public QuartzBackgroundJobManager(IScheduler scheduler, IOptions<AbpBackgroundJobQuartzOptions> options)
{
_scheduler = scheduler;
Scheduler = scheduler;
Options = options.Value;
}
public async Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
public virtual async Task<string> EnqueueAsync<TArgs>(TArgs args, BackgroundJobPriority priority = BackgroundJobPriority.Normal,
TimeSpan? delay = null)
{
var jobDetail = JobBuilder.Create<QuartzJobExecutionAdapter<TArgs>>().SetJobData(new JobDataMap { { nameof(TArgs), args } }).Build();
return await ReEnqueueAsync(args, Options.RetryCount, Options.RetryIntervalMillisecond, priority, delay);
}
public virtual async Task<string> ReEnqueueAsync<TArgs>(TArgs args, int retryCount, int retryIntervalMillisecond,
BackgroundJobPriority priority = BackgroundJobPriority.Normal, TimeSpan? delay = null)
{
var jobDataMap = new JobDataMap
{
{nameof(TArgs), args},
{JobDataPrefix+ nameof(Options.RetryCount), retryCount},
{JobDataPrefix+ nameof(Options.RetryIntervalMillisecond), retryIntervalMillisecond},
{JobDataPrefix+ RetryIndex, 0}
};
var jobDetail = JobBuilder.Create<QuartzJobExecutionAdapter<TArgs>>().RequestRecovery().SetJobData(jobDataMap).Build();
var trigger = !delay.HasValue ? TriggerBuilder.Create().StartNow().Build() : TriggerBuilder.Create().StartAt(new DateTimeOffset(DateTime.Now.Add(delay.Value))).Build();
await _scheduler.ScheduleJob(jobDetail, trigger);
await Scheduler.ScheduleJob(jobDetail, trigger);
return jobDetail.Key.ToString();
}
}

29
framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs

@ -1,6 +1,8 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Quartz;
@ -8,29 +10,50 @@ namespace Volo.Abp.BackgroundJobs.Quartz
{
public class QuartzJobExecutionAdapter<TArgs> : IJob
{
public ILogger<QuartzJobExecutionAdapter<TArgs>> Logger { get; set; }
protected AbpBackgroundJobOptions Options { get; }
protected AbpBackgroundJobQuartzOptions BackgroundJobQuartzOptions { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IBackgroundJobExecuter JobExecuter { get; }
public QuartzJobExecutionAdapter(
IOptions<AbpBackgroundJobOptions> options,
IOptions<AbpBackgroundJobQuartzOptions> backgroundJobQuartzOptions,
IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory)
{
JobExecuter = jobExecuter;
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
BackgroundJobQuartzOptions = backgroundJobQuartzOptions.Value;
Logger = NullLogger<QuartzJobExecutionAdapter<TArgs>>.Instance;
}
public async Task Execute(IJobExecutionContext context)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var args = (TArgs)context.JobDetail.JobDataMap.Get(nameof(TArgs));
var args = (TArgs) context.JobDetail.JobDataMap.Get(nameof(TArgs));
var jobType = Options.GetJob(typeof(TArgs)).JobType;
var jobContext = new JobExecutionContext(scope.ServiceProvider, jobType, args);
await JobExecuter.ExecuteAsync(jobContext);
try
{
await JobExecuter.ExecuteAsync(jobContext);
}
catch (Exception exception)
{
var jobExecutionException = new JobExecutionException(exception);
var retryIndex = context.JobDetail.JobDataMap.GetIntValue(QuartzBackgroundJobManager.JobDataPrefix+ QuartzBackgroundJobManager.RetryIndex);
retryIndex++;
context.JobDetail.JobDataMap.Put(QuartzBackgroundJobManager.JobDataPrefix+ QuartzBackgroundJobManager.RetryIndex, retryIndex);
await BackgroundJobQuartzOptions.RetryStrategy.Invoke(retryIndex, context, jobExecutionException);
throw jobExecutionException;
}
}
}
}
}
}

10
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkerQuartzOptions.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.BackgroundWorkers.Quartz
{
public class AbpBackgroundWorkerQuartzOptions
{
/// <summary>
/// Default : true.
/// </summary>
public bool IsAutoRegisterEnabled { get; set; } = true;
}
}

31
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs

@ -1,5 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Modularity;
using Volo.Abp.Quartz;
@ -15,15 +17,30 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
{
context.Services.AddConventionalRegistrar(new AbpQuartzConventionalRegistrar());
}
public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
{
var options = context.ServiceProvider.GetService<IOptions<AbpBackgroundWorkerOptions>>().Value;
if (!options.IsEnabled)
{
var quartzOptions = context.ServiceProvider.GetService<IOptions<AbpQuartzOptions>>().Value;
quartzOptions.StartSchedulerFactory = scheduler => Task.CompletedTask;
}
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var backgroundWorkerManager = context.ServiceProvider.GetService<IBackgroundWorkerManager>();
var works = context.ServiceProvider.GetServices<IQuartzBackgroundWorker>();
foreach (var work in works)
var options = context.ServiceProvider.GetService<IOptions<AbpBackgroundWorkerOptions>>().Value;
var quartzBackgroundWorkerOptions = context.ServiceProvider.GetService<IOptions<AbpBackgroundWorkerQuartzOptions>>().Value;
if (options.IsEnabled && quartzBackgroundWorkerOptions.IsAutoRegisterEnabled)
{
backgroundWorkerManager.Add(work);
var backgroundWorkerManager = context.ServiceProvider.GetService<IBackgroundWorkerManager>();
var works = context.ServiceProvider.GetServices<IQuartzBackgroundWorker>().Where(x=>x.AutoRegister);
foreach (var work in works)
{
backgroundWorkerManager.Add(work);
}
}
}
}

8
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorker.cs

@ -1,4 +1,6 @@
using Quartz;
using System;
using System.Threading.Tasks;
using Quartz;
namespace Volo.Abp.BackgroundWorkers.Quartz
{
@ -7,5 +9,9 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
ITrigger Trigger { get; set; }
IJobDetail JobDetail { get; set; }
bool AutoRegister { get; set; }
Func<IScheduler,Task> ScheduleJob { get; set; }
}
}

7
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerBase.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Quartz;
namespace Volo.Abp.BackgroundWorkers.Quartz
@ -8,6 +9,10 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
public ITrigger Trigger { get; set; }
public IJobDetail JobDetail { get; set; }
public bool AutoRegister { get; set; } = true;
public Func<IScheduler, Task> ScheduleJob { get; set; } = null;
public abstract Task Execute(IJobExecutionContext context);
}

36
framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs

@ -16,12 +16,12 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
_scheduler = scheduler;
}
public async Task StartAsync(CancellationToken cancellationToken = default)
public virtual async Task StartAsync(CancellationToken cancellationToken = default)
{
await _scheduler.ResumeAll(cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken = default)
public virtual async Task StopAsync(CancellationToken cancellationToken = default)
{
if (!_scheduler.IsShutdown)
{
@ -29,15 +29,41 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
}
}
public void Add(IBackgroundWorker worker)
public virtual void Add(IBackgroundWorker worker)
{
AsyncHelper.RunSync(() => ReScheduleJobAsync(worker));
}
protected virtual async Task ReScheduleJobAsync(IBackgroundWorker worker)
{
if (worker is IQuartzBackgroundWorker quartzWork)
{
Check.NotNull(quartzWork.Trigger, nameof(quartzWork.Trigger));
Check.NotNull(quartzWork.JobDetail, nameof(quartzWork.JobDetail));
AsyncHelper.RunSync(() => _scheduler.ScheduleJob(quartzWork.JobDetail, quartzWork.Trigger));
if (quartzWork.ScheduleJob != null)
{
await quartzWork.ScheduleJob.Invoke(_scheduler);
}
else
{
await DefaultScheduleJobAsync(quartzWork);
}
}
}
protected virtual async Task DefaultScheduleJobAsync(IQuartzBackgroundWorker quartzWork)
{
if (await _scheduler.CheckExists(quartzWork.JobDetail.Key))
{
await _scheduler.AddJob(quartzWork.JobDetail, true, true);
await _scheduler.ResumeJob(quartzWork.JobDetail.Key);
await _scheduler.RescheduleJob(quartzWork.Trigger.Key, quartzWork.Trigger);
}
else
{
await _scheduler.ScheduleJob(quartzWork.JobDetail, quartzWork.Trigger);
}
}
}
}
}

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

6
framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization;
using JetBrains.Annotations;
@ -35,6 +35,8 @@ namespace Volo.Abp.Localization
});
}
public static bool IsRtl => CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
public static bool IsValidCultureCode(string cultureCode)
{
if (cultureCode.IsNullOrWhiteSpace())
@ -60,4 +62,4 @@ namespace Volo.Abp.Localization
: cultureName;
}
}
}
}

6
framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/ar.json

@ -0,0 +1,6 @@
{
"culture": "ar",
"texts": {
"MaxResultCountExceededExceptionMessage": "لا يمكن أن يكون {0} أكثر من {1}! قم بزيادة{2}. {3} على الخادم للسماح بمزيد من النتائج."
}
}

23
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/ar.json

@ -0,0 +1,23 @@
{
"culture": "ar",
"texts": {
"DisplayName:Abp.Mailing.DefaultFromAddress": "العنوان الإفتراضي",
"DisplayName:Abp.Mailing.DefaultFromDisplayName": "اسم المرسل الإفتراضي",
"DisplayName:Abp.Mailing.Smtp.Host": "المضيف",
"DisplayName:Abp.Mailing.Smtp.Port": "المنفذ",
"DisplayName:Abp.Mailing.Smtp.UserName": "اسم المستخدم",
"DisplayName:Abp.Mailing.Smtp.Password": "كلمة المرور",
"DisplayName:Abp.Mailing.Smtp.Domain": "المجال",
"DisplayName:Abp.Mailing.Smtp.EnableSsl": "تمكين SSL",
"DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "استخدام الصلاحيات الإفتراضية",
"Description:Abp.Mailing.DefaultFromAddress": "وصف العنوان الافتراضي",
"Description:Abp.Mailing.DefaultFromDisplayName": "وصف اسم المرسل الإفتراضي",
"Description:Abp.Mailing.Smtp.Host": "اسم أو عنوان IP للمضيف المستخدم في معاملات SMTP.",
"Description:Abp.Mailing.Smtp.Port": "المنفذ المستخدم في معاملات.",
"Description:Abp.Mailing.Smtp.UserName": "اسم المستخدم المرتبط ببيانات الاعتماد.",
"Description:Abp.Mailing.Smtp.Password": "كلمة المرور لاسم المستخدم المرتبط ببيانات الاعتماد.",
"Description:Abp.Mailing.Smtp.Domain": "اسم المجال أو الكمبيوتر الذي يتحقق من بيانات الاعتماد.",
"Description:Abp.Mailing.Smtp.EnableSsl": "ما إذا كان SmtpClient يستخدم (SSL) لتشفير الاتصال.",
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "إرسال الصلاحيات الافتراضية مع الطلب."
}
}

8
framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs

@ -25,9 +25,11 @@ namespace Volo.Abp.Hangfire
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
//TODO: ABP may provide two methods for application shutdown: OnPreApplicationShutdown & OnApplicationShutdown
_backgroundJobServer.SendStop();
_backgroundJobServer.Dispose();
if (_backgroundJobServer != null)
{
_backgroundJobServer.SendStop();
_backgroundJobServer.Dispose();
}
}
}
}

7
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/ar.json

@ -0,0 +1,7 @@
{
"culture": "ar",
"texts": {
"DisplayName:Abp.Localization.DefaultLanguage": "اللغة الافتراضية",
"Description:Abp.Localization.DefaultLanguage": "اللغة الافتراضية للتطبيق."
}
}

31
framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs

@ -14,32 +14,33 @@ namespace Volo.Abp.Quartz
public override void ConfigureServices(ServiceConfigurationContext context)
{
var options = context.Services.ExecutePreConfiguredActions<AbpQuartzPreOptions>();
var options = context.Services.ExecutePreConfiguredActions<AbpQuartzOptions>();
context.Services.AddSingleton(AsyncHelper.RunSync(() => new StdSchedulerFactory(options.Properties).GetScheduler()));
context.Services.AddSingleton(typeof(IJobFactory), typeof(AbpQuartzJobFactory));
Configure<AbpQuartzOptions>(quartzOptions =>
{
quartzOptions.Properties = options.Properties;
quartzOptions.StartDelay = options.StartDelay;
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpQuartzPreOptions>>().Value;
var options = context.ServiceProvider.GetRequiredService<IOptions<AbpQuartzOptions>>().Value;
_scheduler = context.ServiceProvider.GetService<IScheduler>();
_scheduler.JobFactory = context.ServiceProvider.GetService<IJobFactory>();
if (options.StartDelay.Ticks > 0)
{
AsyncHelper.RunSync(() => _scheduler.StartDelayed(options.StartDelay));
}
else
{
AsyncHelper.RunSync(() => _scheduler.Start());
}
AsyncHelper.RunSync(() => options.StartSchedulerFactory.Invoke(_scheduler));
}
public override void OnApplicationShutdown(ApplicationShutdownContext context)
{
//TODO: ABP may provide two methods for application shutdown: OnPreApplicationShutdown & OnApplicationShutdown
AsyncHelper.RunSync(() => _scheduler.Shutdown());
if (_scheduler.IsStarted)
{
AsyncHelper.RunSync(() => _scheduler.Shutdown());
}
}
}
}
}

48
framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzOptions.cs

@ -0,0 +1,48 @@
using System;
using System.Collections.Specialized;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Quartz;
namespace Volo.Abp.Quartz
{
public class AbpQuartzOptions
{
/// <summary>
/// The quartz configuration. Available properties can be found within Quartz.Impl.StdSchedulerFactory.
/// </summary>
public NameValueCollection Properties { get; set; }
/// <summary>
/// How long Quartz should wait before starting. Default: 0.
/// </summary>
public TimeSpan StartDelay { get; set; }
[NotNull]
public Func<IScheduler, Task> StartSchedulerFactory
{
get => _startSchedulerFactory;
set => _startSchedulerFactory = Check.NotNull(value, nameof(value));
}
private Func<IScheduler, Task> _startSchedulerFactory;
public AbpQuartzOptions()
{
Properties = new NameValueCollection();
StartDelay = new TimeSpan(0);
_startSchedulerFactory = StartSchedulerAsync;
}
private async Task StartSchedulerAsync(IScheduler scheduler)
{
if (StartDelay.Ticks > 0)
{
await scheduler.StartDelayed(StartDelay);
}
else
{
await scheduler.Start();
}
}
}
}

23
framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzPreOptions.cs

@ -1,23 +0,0 @@
using System;
using System.Collections.Specialized;
namespace Volo.Abp.Quartz
{
public class AbpQuartzPreOptions
{
/// <summary>
/// The quartz configuration. Available properties can be found within Quartz.Impl.StdSchedulerFactory.
/// </summary>
public NameValueCollection Properties { get; set; }
/// <summary>
/// How long Quartz should wait before starting. Default: 0.
/// </summary>
public TimeSpan StartDelay { get; set; }
public AbpQuartzPreOptions()
{
Properties = new NameValueCollection();
StartDelay = new TimeSpan(0);
}
}
}

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

6
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/ar.json

@ -0,0 +1,6 @@
{
"culture": "ar",
"texts": {
"Menu:Administration": "الإدارة"
}
}

34
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json

@ -0,0 +1,34 @@
{
"culture": "ar",
"texts": {
"'{0}' and '{1}' do not match.": "'{0}' and '{1}' do not match.",
"The {0} field is not a valid credit card number.": "The {0} field is not a valid credit card number.",
"{0} is not valid.": "{0} is not valid.",
"The {0} field is not a valid e-mail address.": "The {0} field is not a valid e-mail address.",
"The {0} field only accepts files with the following extensions: {1}": "The {0} field only accepts files with the following extensions: {1}",
"The field {0} must be a string or array type with a maximum length of '{1}'.": "The field {0} must be a string or array type with a maximum length of '{1}'.",
"The field {0} must be a string or array type with a minimum length of '{1}'.": "The field {0} must be a string or array type with a minimum length of '{1}'.",
"The {0} field is not a valid phone number.": "The {0} field is not a valid phone number.",
"The field {0} must be between {1} and {2}.": "The field {0} must be between {1} and {2}.",
"The field {0} must match the regular expression '{1}'.": "The field {0} must match the regular expression '{1}'.",
"The {0} field is required.": "The {0} field is required.",
"The field {0} must be a string with a maximum length of {1}.": "The field {0} must be a string with a maximum length of {1}.",
"The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.": "The field {0} must be a string with a minimum length of {2} and a maximum length of {1}.",
"The {0} field is not a valid fully-qualified http, https, or ftp URL.": "The {0} field is not a valid fully-qualified http, https, or ftp URL.",
"The field {0} is invalid.": "The field {0} is invalid.",
"ThisFieldIsNotAValidCreditCardNumber.": "This field is not a valid credit card number.",
"ThisFieldIsNotValid.": "This field is not valid.",
"ThisFieldIsNotAValidEmailAddress.": "This field is not a valid e-mail address.",
"ThisFieldOnlyAcceptsFilesWithTheFollowingExtensions:{0}": "This field only accepts files with the following extensions: {0}",
"ThisFieldMustBeAStringOrArrayTypeWithAMaximumLengthoOf{0}": "This field must be a string or array type with a maximum length of '{0}'.",
"ThisFieldMustBeAStringOrArrayTypeWithAMinimumLengthOf{0}": "This field must be a string or array type with a minimum length of '{0}'.",
"ThisFieldIsNotAValidPhoneNumber.": "This field is not a valid phone number.",
"ThisFieldMustBeBetween{0}And{1}": "This field must be between {0} and {1}.",
"ThisFieldMustMatchTheRegularExpression{0}": "This field must match the regular expression '{0}'.",
"ThisFieldIsRequired.": "This field is required.",
"ThisFieldMustBeAStringWithAMaximumLengthOf{0}": "This field must be a string with a maximum length of {0}.",
"ThisFieldMustBeAStringWithAMinimumLengthOf{1}AndAMaximumLengthOf{0}": "This field must be a string with a minimum length of {1} and a maximum length of {0}.",
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "This field is not a valid fully-qualified http, https, or ftp URL.",
"ThisFieldIsInvalid.": "This field is invalid."
}
}

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.": "该字段无效."
}
}
}

13
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs

@ -1,13 +1,13 @@
using System;
using System.Linq;
using System.Security.Claims;
using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.Authorization;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.Localization.Resource;
using Volo.Abp.AspNetCore.Security.Claims;
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Autofac;
using Volo.Abp.Localization;
@ -76,11 +76,17 @@ namespace Volo.Abp.AspNetCore.Mvc
options.Languages.Add(new LanguageInfo("en", "en", "English"));
options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
});
Configure<RazorPagesOptions>(options =>
{
options.RootDirectory = "/Volo/Abp/AspNetCore/Mvc";
});
Configure<AbpClaimsMapOptions>(options =>
{
options.Maps.Add("SerialNumber", () => ClaimTypes.SerialNumber);
options.Maps.Add("DateOfBirth", () => ClaimTypes.DateOfBirth);
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -92,6 +98,7 @@ namespace Volo.Abp.AspNetCore.Mvc
app.UseAbpRequestLocalization();
app.UseRouting();
app.UseMiddleware<FakeAuthenticationMiddleware>();
app.UseAbpClaimsMap();
app.UseAuthorization();
app.UseAuditing();
app.UseUnitOfWork();

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

7
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/ar.json

@ -0,0 +1,7 @@
{
"culture": "ar",
"texts": {
"BirthDate": "تاريخ الميلاد",
"Value1": "القيمة الأولى"
}
}

22
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Claims/ClaimsMapTestController.cs

@ -0,0 +1,22 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using Shouldly;
namespace Volo.Abp.AspNetCore.Mvc.Security.Claims
{
public class ClaimsMapTestController : AbpController
{
public ActionResult ClaimsMapTest()
{
var serialNumber = CurrentUser.FindClaim(ClaimTypes.SerialNumber);
serialNumber.ShouldNotBeNull();
serialNumber.Value.ShouldBe("123456");
var dateOfBirth = CurrentUser.FindClaim(ClaimTypes.DateOfBirth);
dateOfBirth.ShouldNotBeNull();
dateOfBirth.Value.ShouldBe("2020");
return Content("OK");
}
}
}

41
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Claims/ClaimsMapTestController_Tests.cs

@ -0,0 +1,41 @@
using System.Security.Claims;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.AspNetCore.Mvc.Authorization;
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Autofac;
using Volo.Abp.MemoryDb;
using Volo.Abp.Modularity;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.Security.Claims
{
[DependsOn(
typeof(AbpAspNetCoreTestBaseModule),
typeof(AbpMemoryDbTestModule),
typeof(AbpAspNetCoreMvcModule),
typeof(AbpAutofacModule)
)]
public class ClaimsMapTestController_Tests : AspNetCoreMvcTestBase
{
private readonly FakeUserClaims _fakeRequiredService;
public ClaimsMapTestController_Tests()
{
_fakeRequiredService = GetRequiredService<FakeUserClaims>();
}
[Fact]
public async Task Claims_Should_Be_Mapped()
{
_fakeRequiredService.Claims.AddRange(new[]
{
new Claim("SerialNumber", "123456"),
new Claim("DateOfBirth", "2020")
});
var result = await GetResponseAsStringAsync("/ClaimsMapTest/ClaimsMapTest");
result.ShouldBe("OK");
}
}
}

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

10257
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.css

File diff suppressed because it is too large

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.css.map

File diff suppressed because one or more lines are too long

9
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.min.css

File diff suppressed because one or more lines are too long

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.min.css.map

File diff suppressed because one or more lines are too long

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.css.map

File diff suppressed because one or more lines are too long

7
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.min.css

File diff suppressed because one or more lines are too long

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.min.css.map

File diff suppressed because one or more lines are too long

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/js/bootstrap.bundle.js.map

File diff suppressed because one or more lines are too long

7
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/js/bootstrap.bundle.min.js

File diff suppressed because one or more lines are too long

1
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/js/bootstrap.bundle.min.js.map

File diff suppressed because one or more lines are too long

26
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Program.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

27
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Properties/launchSettings.json

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:56574",
"sslPort": 44374
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

15
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.AspNetCore.TestBase\Volo.Abp.AspNetCore.TestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Tests\Volo.Abp.AspNetCore.Tests.csproj" />
</ItemGroup>
</Project>

6
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/AbpAspNetCoreMvcUiThemeSharedTestBase.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared
{
public class AbpAspNetCoreMvcUiThemeSharedTestBase : AbpAspNetCoreTestBase<Startup>
{
}
}

19
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/AbpAspNetCoreMvcUiThemeSharedTestModule.cs

@ -0,0 +1,19 @@
using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Autofac;
using Volo.Abp.Modularity;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpAspNetCoreTestBaseModule),
typeof(AbpAspNetCoreMvcUiThemeSharedModule)
)]
public class AbpAspNetCoreMvcUiThemeSharedTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
}
}

113
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/PageToolbars/PageToolbar_Tests.cs

@ -0,0 +1,113 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Shouldly;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar.Button;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars;
using Volo.Abp.Localization;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars
{
public class PageToolbar_Tests : AbpAspNetCoreMvcUiThemeSharedTestBase
{
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
services.Configure<AbpPageToolbarOptions>(options =>
{
options.Configure("TestPage1", toolbar =>
{
toolbar.Contributors.Add(new MyToolbarContributor());
toolbar.Contributors.Add(new MyToolbarContributor2());
toolbar.AddComponent<MyPageComponent5>();
toolbar.AddButton(new FixedLocalizableString("My button"), order: -1);
});
});
}
[Fact]
public void AbpPageToolbarOptions_Should_Contain_Contributors()
{
var options = GetRequiredService<IOptions<AbpPageToolbarOptions>>().Value;
options.Toolbars.Count.ShouldBe(1);
options.Toolbars.ShouldContainKey("TestPage1");
options.Toolbars["TestPage1"].Contributors.Count.ShouldBe(4);
}
[Fact]
public async Task PageToolbarManager_Should_Return_ToolbarItems()
{
var pageToolbarManager = GetRequiredService<IPageToolbarManager>();
var items = await pageToolbarManager.GetItemsAsync("TestPage1");
items.Length.ShouldBe(5);
items[0].ComponentType.ShouldBe(typeof(AbpPageToolbarButtonViewComponent));
items[1].ComponentType.ShouldBe(typeof(MyPageComponent2));
items[2].ComponentType.ShouldBe(typeof(MyPageComponent3));
items[3].ComponentType.ShouldBe(typeof(MyPageComponent4));
items[4].ComponentType.ShouldBe(typeof(MyPageComponent5));
}
public class MyToolbarContributor : IPageToolbarContributor
{
public Task ContributeAsync(PageToolbarContributionContext context)
{
context.Items.Add(new PageToolbarItem(typeof(MyPageComponent1)));
context.Items.Add(new PageToolbarItem(typeof(MyPageComponent2)));
return Task.CompletedTask;
}
}
public class MyToolbarContributor2 : IPageToolbarContributor
{
public Task ContributeAsync(PageToolbarContributionContext context)
{
context.Items.RemoveAll(i => i.ComponentType == typeof(MyPageComponent1));
context.Items.Add(new PageToolbarItem(typeof(MyPageComponent3)));
context.Items.Add(new PageToolbarItem(typeof(MyPageComponent4)));
return Task.CompletedTask;
}
}
public class MyPageComponent1 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyPageComponent1");
}
}
public class MyPageComponent2 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyPageComponent2");
}
}
public class MyPageComponent3 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyPageComponent3");
}
}
public class MyPageComponent4 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyPageComponent4");
}
}
public class MyPageComponent5 : AbpViewComponent
{
public IViewComponentResult InvokeAsync()
{
return Content("MyPageComponent4");
}
}
}
}

19
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests/Volo/Abp/AspNetCore/Mvc/UI/Theme/Shared/Startup.cs

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddApplication<AbpAspNetCoreMvcUiThemeSharedTestModule>();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.InitializeApplication();
}
}
}

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

@ -0,0 +1,6 @@
{
"culture": "ar",
"texts": {
"hello": "مرحبا"
}
}

7
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/ar.json

@ -0,0 +1,7 @@
{
"culture": "ar",
"texts": {
"USA": "الولايات المتحدة الامريكية",
"Brazil": "البرازيل"
}
}

7
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/ar.json

@ -0,0 +1,7 @@
{
"culture": "ar",
"texts": {
"ThisFieldIsRequired": "الحقل مطلوب",
"MaxLenghtErrorMessage": "اقصى طول للحقل '{0}' حرف"
}
}

11
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/ar.json

@ -0,0 +1,11 @@
{
"culture": "ar",
"texts": {
"Hello <b>{0}</b>.": "مرحباً <b>{0}</b>.",
"Car": "سيارة",
"CarPlural": "سيارات",
"MaxLenghtErrorMessage": "اقصى طول للحقل '{0}' حرف",
"Universe": "عالم",
"FortyTwo": "اثنان وأربعون"
}
}

6
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/ar.json

@ -0,0 +1,6 @@
{
"culture": "ar",
"texts": {
"SeeYou": "الى لقاء"
}
}

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

45
modules/account/src/Volo.Abp.Account.Application.Contracts/Volo/Abp/Account/Localization/Resources/ar.json

@ -0,0 +1,45 @@
{
"culture": "ar",
"texts": {
"UserName": "اسم المستخدم",
"EmailAddress": "البريد الإلكتروني",
"UserNameOrEmailAddress": "اسم المستخدم أو البريد الإلكتروني",
"Password": "كلمة المرور",
"RememberMe": "تذكرني",
"UseAnotherServiceToLogin": "استخدم خدمة أخرى لتسجيل الدخول",
"UserLockedOutMessage": "تم قفل حساب المستخدم بسبب محاولات تسجيل الدخول غير الصالحة. يرجى الانتظار بعض الوقت والمحاولة مرة أخرى.",
"InvalidUserNameOrPassword": "اسم مستخدم أو كلمة مرور غير صالحة!",
"LoginIsNotAllowed": "غير مسموح لك بتسجيل الدخول! أنت بحاجة إلى تأكيد بريدك الإلكتروني / رقم هاتفك.",
"SelfRegistrationDisabledMessage": "تم تعطيل التسجيل الذاتي لهذا التطبيق. يرجى الاتصال بمسؤول التطبيق لتسجيل مستخدم جديد.",
"LocalLoginDisabledMessage": "تسجيل الدخول المحلي معطّل لهذا التطبيق.",
"Login": "دخول",
"Cancel": "إلغاء",
"Register": "تسجيل",
"AreYouANewUser": "هل أنت مستخدم جديد?",
"AlreadyRegistered": "مسجل بالفعل?",
"InvalidLoginRequest": "طلب تسجيل دخول غير صالح",
"ThereAreNoLoginSchemesConfiguredForThisClient": "لم يتم تكوين أنظمة تسجيل دخول لهذا العميل.",
"LogInUsingYourProviderAccount": "قم بتسجيل الدخول باستخدام حسابك في {0}",
"DisplayName:CurrentPassword": "كلمة المرور الحالية",
"DisplayName:NewPassword": "كلمة مرور جديدة",
"DisplayName:NewPasswordConfirm": "تأكيد كلمة المرور الجديدة",
"PasswordChangedMessage": "تم تغيير كلمة مرورك بنجاح.",
"DisplayName:UserName": "اسم المستخدم",
"DisplayName:Email": "البريد الإلكتروني",
"DisplayName:Name": "الاسم",
"DisplayName:Surname": "اللقب",
"DisplayName:Password": "كلمة المرور",
"DisplayName:EmailAddress": "البريد الإلكتروني",
"DisplayName:PhoneNumber": "رقم الهاتف",
"PersonalSettings": "الإعدادات الشخصية",
"PersonalSettingsSaved": "تم حفظ الإعدادات الشخصية",
"PasswordChanged": "تم تغيير كلمة المرور",
"NewPasswordConfirmFailed": "يرجى تأكيد كلمة المرور الجديدة.",
"Manage": "إدارة",
"ManageYourProfile": "إدارة ملف التعريف الخاص بك",
"DisplayName:Abp.Account.IsSelfRegistrationEnabled": "هل تم تمكين التسجيل الذاتي",
"Description:Abp.Account.IsSelfRegistrationEnabled": "ما إذا كان يمكن للمستخدم تسجيل الحساب بنفسه أم لا.",
"DisplayName:Abp.Account.EnableLocalLogin": "المصادقة باستخدام حساب محلي",
"Description:Abp.Account.EnableLocalLogin": "يشير إلى ما إذا كان الخادم سيسمح للمستخدمين بالمصادقة باستخدام حساب محلي."
}
}

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

Loading…
Cancel
Save