Browse Source

Merge remote-tracking branch 'abpframework/dev' into docs

pull/4071/head
liangshiwei 6 years ago
parent
commit
d8d63eccad
  1. 24
      .github/ISSUE_TEMPLATE
  2. 9
      .gitignore
  3. 1
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  4. 2
      common.props
  5. 214
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/Post.md
  6. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png
  7. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png
  8. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png
  9. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif
  10. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png
  11. BIN
      docs/en/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png
  12. 10
      docs/en/CLI.md
  13. 4
      docs/en/Customizing-Application-Modules-Guide.md
  14. 6
      docs/en/Dapper.md
  15. 4
      docs/en/Index.md
  16. 4
      docs/en/Microservice-Architecture.md
  17. 11
      docs/en/Road-Map.md
  18. 14
      docs/en/SignalR-Integration.md
  19. 8
      docs/en/Text-Templating.md
  20. 2
      docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md
  21. 16
      docs/en/docs-nav.json
  22. 247
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/Post.md
  23. BIN
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/abp-commercial-ui-extensions.png
  24. BIN
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/audit-log-entity-changes.png
  25. BIN
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/easy-crm.png
  26. BIN
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/github-contribution-graph.png
  27. BIN
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/module-entity-extended-ui.png
  28. BIN
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/tenant-entity-changes.png
  29. BIN
      docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/text-template-content-ui.png
  30. 214
      docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/Post.md
  31. BIN
      docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png
  32. BIN
      docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png
  33. BIN
      docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png
  34. BIN
      docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif
  35. BIN
      docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png
  36. BIN
      docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png
  37. 9
      framework/Volo.Abp.sln
  38. 12
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/ar.json
  39. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Bootstrap/BootstrapStyleContributor.cs
  40. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  41. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
  42. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
  43. 11
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionResultHelper.cs
  44. 18
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs
  45. 6
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs
  46. 17
      framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalRUserIdProvider.cs
  47. 10
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/AbpApplicationBuilderExtensions.cs
  48. 46
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapMiddleware.cs
  49. 21
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/AbpClaimsMapOptions.cs
  50. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Security/Claims/HttpContextCurrentPrincipalAccessor.cs
  51. 3
      framework/src/Volo.Abp.BackgroundJobs.Abstractions/Volo/Abp/BackgroundJobs/AbpBackgroundJobOptions.cs
  52. 24
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/AbpBackgroundJobsHangfireModule.cs
  53. 2
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireBackgroundJobManager.cs
  54. 19
      framework/src/Volo.Abp.BackgroundJobs.HangFire/Volo/Abp/BackgroundJobs/Hangfire/HangfireJobExecutionAdapter.cs
  55. 46
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobQuartzOptions.cs
  56. 18
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/AbpBackgroundJobsQuartzModule.cs
  57. 22
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManageExtensions.cs
  58. 33
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzBackgroundJobManager.cs
  59. 29
      framework/src/Volo.Abp.BackgroundJobs.Quartz/Volo/Abp/BackgroundJobs/Quartz/QuartzJobExecutionAdapter.cs
  60. 10
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkerQuartzOptions.cs
  61. 30
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/AbpBackgroundWorkersQuartzModule.cs
  62. 8
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/IQuartzBackgroundWorker.cs
  63. 7
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerBase.cs
  64. 36
      framework/src/Volo.Abp.BackgroundWorkers.Quartz/Volo/Abp/BackgroundWorkers/Quartz/QuartzBackgroundWorkerManager.cs
  65. 12
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs
  66. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  67. 9
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/AbpIoSourceCodeStore.cs
  68. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/TemplateInfoProvider.cs
  69. 16
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplate.cs
  70. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Templates/Console/ConsoleTemplateBase.cs
  71. 6
      framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
  72. 6
      framework/src/Volo.Abp.Ddd.Application.Contracts/Volo/Abp/Application/Localization/Resources/AbpDdd/ar.json
  73. 23
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Localization/ar.json
  74. 8
      framework/src/Volo.Abp.HangFire/Volo/Abp/Hangfire/AbpHangfireModule.cs
  75. 7
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/Resources/AbpLocalization/ar.json
  76. 31
      framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs
  77. 48
      framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzOptions.cs
  78. 23
      framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzPreOptions.cs
  79. 5
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ICurrentPrincipalAccessor.cs
  80. 29
      framework/src/Volo.Abp.Security/Volo/Abp/Security/Claims/ThreadCurrentPrincipalAccessor.cs
  81. 50
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateLocalizer.cs
  82. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs
  83. 6
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/ar.json
  84. 34
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/ar.json
  85. 4
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/zh-Hans.json
  86. 13
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs
  87. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/FakeAuthenticationMiddleware.cs
  88. 27
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs
  89. 64
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs
  90. 6
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs
  91. 7
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/ar.json
  92. 22
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Claims/ClaimsMapTestController.cs
  93. 41
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Security/Claims/ClaimsMapTestController_Tests.cs
  94. 8
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs
  95. 10257
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.css
  96. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.css.map
  97. 9
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.min.css
  98. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap-rtl.min.css.map
  99. 1
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.css.map
  100. 7
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/wwwroot/libs/bootstrap/css/bootstrap.min.css

24
.github/ISSUE_TEMPLATE

@ -0,0 +1,24 @@
### Documentation
Please check the official documentation before asking questions: https://docs.abp.io
### GitHub Issues
GitHub issues are for bug reports, feature requests and other discussions about the framework.
If you're creating a bug/problem report, please include followings:
* Your **ABP Framework version**.
* Your **User Interface** type (Angular/MVC/React... etc.) if the issue is related to a specific UI
* Exception message and **stack trace** if available (check the logs).
* Steps needed to **reproduce** the problem.
Please **write in English**.
### Stack Overflow
Please use Stack Overflow for your questions about using the framework, templates and samples:
https://stackoverflow.com/questions/tagged/abp
Use **abp** tag in your questions.

9
.gitignore

@ -293,10 +293,11 @@ samples/MicroserviceDemo/applications/ConsoleClientDemo/Logs/logs.txt
modules/docs/app/Volo.DocsTestApp/Logs/logs.txt modules/docs/app/Volo.DocsTestApp/Logs/logs.txt
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Logs/logs.txt framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Logs/logs.txt
samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.txt samples/MicroserviceDemo/microservices/TenantManagementService.Host/Logs/logs.txt
/modules/client-simulation/demo/Volo.ClientSimulation.Demo/package-lock.json /framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/package-lock.json
/samples/BookStore/src/Acme.BookStore.Web/package-lock.json /npm/packs/bootstrap/package-lock.json
/samples/DashboardDemo/src/DashboardDemo.Web/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web.Host/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.Web/package-lock.json
/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package-lock.json /templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/package-lock.json
/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

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

@ -35,6 +35,7 @@
"NameFilter": "Name", "NameFilter": "Name",
"CreationTime": "Creation time", "CreationTime": "Creation time",
"IsPro": "Is pro", "IsPro": "Is pro",
"ShowOnModuleList": "Show on module list",
"EfCoreConfigureMethodName": "Configure method name", "EfCoreConfigureMethodName": "Configure method name",
"IsProFilter": "Is pro", "IsProFilter": "Is pro",
"ApplicationType": "Application type", "ApplicationType": "Application type",

2
common.props

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

214
docs/en/Blog-Posts/2020-05-22 v2_8_Release/Post.md

@ -0,0 +1,214 @@
# ABP v2.8.0 Releases & Road Map
The **ABP Framework** & and the **ABP Commercial** v2.8 have been released. This post will cover **what's new** with these releases and the **middle-term road maps** for the projects.
## What's New in the ABP Framework 2.8?
You can see all the changes on the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.8.0). This post will only cover the important features/changes.
### SignalR Integration Package
We've published [a new package](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) to integrate SignalR to ABP framework based applications.
> 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.
See the [SignalR Integration document](https://docs.abp.io/en/abp/latest/SignalR-Integration) to start with the SignalR.
#### SignalR Demo Application
We've also created a simple chat application to demonstrate how to use it.
![signalr-chat-demo](signalr-chat-demo.png)
See [the source code of the application.](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo)
### Console Application Startup Template
The new console application template can be used to create a new console application that has the ABP Framework integrated.
Use ABP CLI to create a new console application, specifying the `console` as the `-t` (template) option:
````bash
abp new MyApp -t console
````
Thanks to the contribution of [@liangshiw](https://github.com/liangshiw) for this template.
### RTL Support for the MVC UI & Arabic Localization
[@kgamalseif](https://github.com/kgamalseif) has contributed a RTL implementation for the MVC UI which looks pretty fine:
![rtl-ui](rtl-ui.png)
He also localized all the framework and module resources. Thanks to him for this great contribution.
### Others
Some other highlights from this release:
* Converted HttpApi.Client packages of the modules to .netstandard 2.0 to be compatible with other kind of applications.
* Improved the object extensibility system to better handle UI, localization and validation.
* Implemented disabling background job execution for HangFire & Quartz intergrations.
* New JsTree integration package for the MVC UI.
* Moved all samples to the new [abp-samples](https://github.com/abpframework/abp-samples) repository and created an [index page](https://docs.abp.io/en/abp/latest/Samples/Index) to see all.
### Deprecations
* Deprecated the `app.UseMvcWithDefaultRouteAndArea()` and introduced the `app.UseConfiguredEndpoints()` (see [#3880](https://github.com/abpframework/abp/issues/3880)).
* Deprecated the `UsePostgreSql()` and introduced the `UseNpgsql()` for the [Volo.Abp.EntityFrameworkCore.PostgreSql](http://nuget.org/packages/Volo.Abp.EntityFrameworkCore.PostgreSql) package. Switch to `UseNpgsql()` if you are using PostgreSQL.
Old methods are marked as `Obsolete` and will be removed in the next major versions.
## What's New in the ABP Commercial 2.8?
### The New Lepton Theme
We've completely revised [the lepton theme](https://commercial.abp.io/themes). See with different styles:
![lepton-themes](lepton-themes.gif)
Example screenshots from the language management page of the ABP Commercial:
![lepton-abp-default-theme](lepton-abp-default-theme.png)
(Default style UI)
![lepton-abp-material-theme](lepton-abp-material-theme.png)
(Material style UI)
[Create a demo](https://commercial.abp.io/demo) to test all the styles in live. You can change the style from the settings page.
### The New Chat Module
The first version of [the chat module](https://commercial.abp.io/modules/Volo.Chat) has been released with this version. It has only the MVC / Razor Pages UI. Angular UI is on the way.
![abp-chat-module](abp-chat-module.png)
It currently has a simple **real time text messaging** functionality. More features like group messaging, sending images/files are on the road map.
### Others
* Implemented [module entity extension](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions) system for the Angular UI. Also improved the system to better handle float/double/decimal, date, datetime, enum and boolean properties.
* Managing product groups on a tree view for the [EasyCRM sample application](https://docs.abp.io/en/commercial/latest/samples/easy-crm).
## About the Next Versions
We publish feature releases in **every 2 weeks**. So, the planned date of the next feature version is **June 04** and the version number is **2.9**. This (probably) will be the **last 2.x version** and the following version will be **3.0**.
### ABP Framework 2.9 & 3.0
#### Organization Unit System
Organization Unit system for the Identity module was intended to be released with 2.8, but unfortunately we couldn't be sure about the stability of the feature, so deferred it to the 2.9.
#### gRPC
We planned to work on a gRPC integrated example application. Then we will plan to create gRPC endpoints for all [pre-built modules](https://docs.abp.io/en/abp/latest/Modules/Index) and to [the startup templates](https://docs.abp.io/en/abp/latest/Startup-Templates/Index). We want to use these endpoints with the new planned [Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) UI option (there is a [huge demand](https://github.com/abpframework/abp/issues/394) on a Blazor UI, we know). It doesn't mean that we'll finish the whole work in 3.0, but we are starting and will continue in 3.0+ versions.
#### Oracle with EF Core
We see that the people using Oracle with EF Core has some pains, independent from the ABP Framework. Because there is no stable & free Oracle provider for EF Core 3.1 yet. We only see the [Devart](https://www.devart.com/) has created a [paid package](https://www.nuget.org/packages/Devart.Data.Oracle.EFCore).
[@ebicoglu](https://github.com/ebicoglu) has [created a gist](https://gist.github.com/ebicoglu/9f364c7eff9d87315af0178866186401) to demonstrate how to use it. We [planned](https://github.com/abpframework/abp/issues/3983) to work on an integration package to make it even easier.
#### API Documentation
We are [working](https://github.com/abpframework/abp/issues/1184) to create an API documentation for the framework and build a CD pipeline to automatically publish it in every new release. This will make easier to explore the framework classes.
#### Sample Application: Using SignalR on a Tiered/Distributed system
Using SignalR on a distributed/microservice system can be tricky since the services are not connected to clients and can not directly call client functions from the server. One way to overcome this problem is using a distributed message bus (like RabbitMQ) that transfers the message from the service to the web application to deliver to the client.
We will create an example application and document it to demonstrate such an architecture and how it is easy by using the ABP Framework.
While this topic is not directly related to the ABP Framework and the problem is not unique to an ABP based application, we find useful to create such guides to developers.
#### And...
We will spend more time to write more documentation, implement performance improvements, make more tests, creating more extensibility points and so on.
### ABP Commercial 2.9 & 3.0
#### Organization Unit Management UI
In parallel to the OU system in the ABP Framework (mentioned above), we are creating a UI to manage the organization units, which will be released with the 2.9.
#### Angular UI for the Chat Module
The Chat Module (mentioned above) only has the ASP.NET Core MVC / Razor Pages UI now. We are working to create the Angular UI for this module.
#### New Module Idea: File Management
We are looking to create a File Management Module that is used to manage (upload/download) and share files between users. You may think as a very simple and lightweight Google Drive :).
#### Easy CRM Angular UI
[Easy CRM](https://docs.abp.io/en/commercial/latest/samples/easy-crm) is a sample application we've released with the previous version of the ABP Commercial. In this version, we've added more features to this application. In the next version, we will work on the Angular UI for it.
We found this application very useful since it is very close to a real world application compared to the simple [BookStore](https://docs.abp.io/en/commercial/latest/samples/index#book-store) example.
#### And...
We are working to improve current [modules](https://commercial.abp.io/modules), [themes](https://commercial.abp.io/themes) and the [tooling](https://commercial.abp.io/tools) to provide a more comfortable developer experience with the version 3.0.
## The Road Map
We are frequently asked about the road map of the [ABP Framework](https://abp.io/) and the [ABP Commercial](https://commercial.abp.io/). While we've answered to it in various platforms, with this release, we've adding road map pages for these products to their documentation:
* [ABP Framework Road Map](https://docs.abp.io/en/abp/latest/Road-Map)
* [ABP Commercial Road Map](https://docs.abp.io/en/commercial/latest/road-map)
I am also writing the road map here, in the following sections;
### ABP Framework Road Map
You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones).
While we will **continue to add other exciting features**, we will work on the following major items in the middle term:
* **gRPC integration** and implementation for all the pre-built modules.
* **Blazor UI** for the framework and all the pre-built modules.
* **.NET 5.0**! As Microsoft has announced that the .NET 5.0 will be released in November 2020, we will prepare for this change before and move to the .NET 5.0 just after Microsoft releases it. We hope a smooth transition.
### ABP Commercial Road Map
We will work on the same items in parallel to to ABP Framework to implement them in the ABP Commercial side:
* gRPC integration
* Blazor UI
* .NET 5.0
In addition, we will be working on the following items in the middle term:
* A startup template to create microservice solutions (that has Ocelot, Redis, RabbitMQ, ElasticSearch, IdentityServer... etc. pre-integrated and configured).
* More module extension points.
* Dynamic dashboard system.
* Real-time notification system.
* Subscription and payment system for the SaaS module.
* More authentication options.
* New application modules (we have tens of module ideas and will share by the time - the "file management" announced above was one of them).
* New themes & theme styles (including public/corporate web site themes).
## BONUS: ABP.IO Platform Road Map
While the ABP Framework and the ABP Commercial are the fundamental components of the ABP.IO Platform, we want to create a much bigger platform to bring the .NET community together to create reusable modules, share knowledge, help each other by taking the advantage of the ABP Framework's unified and standardized development model.
So, we have new *.abp.io web site ideas I want to share with the community
#### market.abp.io
A platform that is used by developers/companies to publish their reusable application modules, themes, libraries and tools base don the ABP Framework. There will be free/open source and commercial products on this web site.
#### jobs.abp.io
We are getting too many emails from companies want to hire developers or other other companies to build their products based on the ABP.IO Platform. We, as [Volosoft](https://volosoft.com/), want to stay in the product side rather than customer basis projects. We generally lead them to experienced developers and companies.
We have a plan to create a web site to meet each side, so you can find developers for your projects or you find short or long term works to do.
## Follow the ABP!
Follow the social media accounts to get informed about happenings on the ABP.IO Platform:
* [@abpframework](https://twitter.com/abpframework): ABP Framework official Twitter account
* [@abpcommercial](https://twitter.com/abpcommercial): ABP Commercial official Twitter account

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/en/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

10
docs/en/CLI.md

@ -40,7 +40,7 @@ abp new Acme.BookStore
#### Options #### Options
* `--template` or `-t`: Specifies the template name. Default template name is `app`, which generates a web application. Available templates: * `--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: * `--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: * `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. * `--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: * `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks:
* `none`: no mobile application. * `none`: no mobile application.
* `react-native`: React Native. * `react-native`: React Native.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core. * `ef`: Entity Framework Core.
* `mongodb`: MongoDB. * `mongodb`: MongoDB.
* `module`: [Module template](Startup-Templates/Module.md). Additional options: * **`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). * `--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. * `--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. * `--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 # 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**. 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 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 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. 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 ## Installation
Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md).
`Volo.Abp.Dapper` is the library for the Dapper integration. `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 You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper

4
docs/en/Index.md

@ -8,8 +8,8 @@ Explore the left navigation menu to deep dive in the documentation.
Easiest way to start a new project with ABP is to use the startup templates: Easiest way to start a new project with ABP is to use the startup templates:
* [ASP.NET Core MVC (Razor Pages) UI Startup Template](Getting-Started-AspNetCore-MVC-Template.md) * [ASP.NET Core MVC (Razor Pages) UI Startup Template](Getting-Started?UI=MVC&DB=EF&Tiered=No)
* [Angular UI Startup Template](Getting-Started-Angular-Template.md) * [Angular UI Startup Template](Getting-Started?UI=NG&DB=EF&Tiered=No)
If you want to start from scratch (with an empty project) then manually install the ABP Framework and use the following tutorials: If you want to start from scratch (with an empty project) then manually install the ABP Framework and use the following tutorials:

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. 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 ## 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.

11
docs/en/Road-Map.md

@ -0,0 +1,11 @@
# ABP Framework Road Map
You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones) for a detailed road map.
While we will **continue to add other exciting features**, we will work on the following major items in the **middle term**:
* **gRPC integration** and implementation for all the pre-built modules.
* **Blazor UI** for the framework and all the pre-built modules.
* **.NET 5.0**! As Microsoft has announced that the .NET 5.0 will be released in November 2020, we will prepare for this change before and move to the .NET 5.0 just after Microsoft releases it. We hope a smooth transition.
Please create an issue on [the GitHub repository](https://github.com/abpframework/abp) for your feature requests, but first search in in the existing issues.

14
docs/en/SignalR-Integration.md

@ -1,6 +1,6 @@
# SignalR Integration # 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 ## 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. 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: 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: `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 ````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: 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 "PasswordReset", //the template name
new PasswordResetModel new PasswordResetModel
{ {
Name = "john",
Link = "https://abp.io/example-link?userId=123&token=ABC" 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: You will see the localized result:
````csharp ````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. > If you define the [default localization resource](Localization.md) for your application, then no need to declare the resource type for the template definition.

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

@ -88,7 +88,7 @@ You can set some of the attributes on your c# property, or directly on html tag.
* `label`: Sets the label for input. * `label`: Sets the label for input.
* `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`. * `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/tr-tr/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-input-tag-helper) are also valid for `abp-input` tag helper. `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 ### Label & Localization

16
docs/en/docs-nav.json

@ -460,6 +460,15 @@
} }
] ]
}, },
{
"text": "Real Time",
"items": [
{
"text": "SignalR Integration",
"path": "SignalR-Integration.md"
}
]
},
{ {
"text": "Background", "text": "Background",
"items": [ "items": [
@ -531,13 +540,14 @@
"text": "Microservice Architecture", "text": "Microservice Architecture",
"path": "Microservice-Architecture.md" "path": "Microservice-Architecture.md"
}, },
{
"text": "Testing"
},
{ {
"text": "Nightly Builds", "text": "Nightly Builds",
"path": "Nightly-Builds.md" "path": "Nightly-Builds.md"
}, },
{
"text": "Road Map",
"path": "Road-Map.md"
},
{ {
"text": "Contribution Guide", "text": "Contribution Guide",
"path": "Contribution/Index.md" "path": "Contribution/Index.md"

247
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/Post.md

@ -0,0 +1,247 @@
# ABP框架 v2.7.0已经发布!
**ABP框架**和**ABP商业版** v2.7已经发布.我们没有为2.4,2.5和2.6发布博客文章,所以这篇文章也将涵盖这几个版本中**新增内容**和过去的2个月里**我们完成了什么**.
## 关于发布周期与开发
之前说过我们已经开始**每两个星期**发布一个新的次要功能版本,一般在星期四.我们的目标是尽快提供新功能.
在过去的7-8周里, 我们在**1,300+次提交**中完成和合并了数百个issue和pull request, 这只是ABP框架的库.每日提交的次数不断增加:
![github-contribution-graph](github-contribution-graph.png)
ABP.IO平台正在快速增长,我们从社区获取的贡献越来越多.
## ABP框架有哪些新增内容?
### 对象扩展系统(Object Extending System)
在过去的几个版本中,我们主要集中在以NuGet/NPM包使用现有模块时, 提供扩展的方法,
对象扩展系统允许模块开发者创建可扩展的模块并允许应用开发者更容易地定制和扩展.
例如,你可以这样为Identity模块的User实体添加两个扩展属性:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdate<IdentityUser>(options =>
{
options.AddOrUpdateProperty<string>("SocialSecurityNumber");
options.AddOrUpdateProperty<bool>("IsSuperUser");
}
);
````
也很容易为这些属性定义验证规则:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUserCreateDto, string>(
"SocialSecurityNumber",
options =>
{
options.Attributes.Add(new RequiredAttribute());
options.Attributes.Add(
new StringLengthAttribute(32) {
MinimumLength = 6
}
);
});
````
你甚至可以编写自定义代码来验证属性.它会自动适用于应用服务, 控制器或页面的参数对象.
实体的扩展属性通常存储在数据库表的一个单独的JSON格式的字段中,但是你也可以轻松地使用EF Core映射配置该属性为一个表字段:
````csharp
ObjectExtensionManager.Instance
.AddOrUpdateProperty<IdentityUser, string>(
"SocialSecurityNumber",
options =>
{
options.MapEfCore(b => b.HasMaxLength(32));
}
);
````
请参见有关该系统的详细信息[对象扩展文档](https://docs.abp.io/en/abp/latest/Object-Extensions).
也可参见[自定义现有模块](https://docs.abp.io/en/abp/latest/Customizing-Application-Modules-Guide)指南,以了解所有可能的自定义选项.
### 文本模板包
[Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating)是v2.7.0中新加入的包.此前,[Volo.Abp.Emailing](https://www.nuget.org/packages/Volo.Abp.Emailing)包也有类似的功能,但它的功能是有限的,实验性质的并且和发送邮件紧密耦合在一起.
新文本模板包允许你定义基于文本的模板, 可以很容易地本地化和重用.你可以定义布局模板并且与其它模板共享.
目前,我们正在使用它发送邮件.一个需要发送邮件的模块通常定义了一个模板.如:
````xml
<h3>{{L "PasswordReset"}}</h3>
<p>{{L "PasswordResetInfoInEmail"}}</p>
<div>
<a href="{{model.link}}">{{L "ResetMyPassword"}}</a>
</div>
````
这是一个典型的密码重置邮件模板.
* 模板系统是基于开源的[Scriban库](https://github.com/lunet-io/scriban).因此,它支持if条件,循环等等.
* `model`用于将数据传递到模板(就像ASP.NET Core MVC).
* `L`是一个特殊函数用于本地化给定的字符串.
为所有邮件使用相同的布局是一个典型应用.所以,你可以定义一个布局模板.这是框架自带的标准的布局:
````xml
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
{{content}}
</body>
</html>
````
布局中应该有一个`{{content}}`区域用来呈现子内容(就像MVC中的`RenderBody()`).
最终应用程序可以很容易地通过覆盖模板内容来自定义它.
每当你需要渲染模板时,提供模板名称和model来使用`ITemplateRenderer`服务.详情请参见[文本模板文档](https://docs.abp.io/en/abp/latest/Text-Templating).我们甚至还为ABP商业版创建了UI(请参见下面的相关章节).
### 订阅异常
ABP框架的[异常处理系统](https://docs.abp.io/en/abp/latest/Exception-Handling)会自动处理异常并且为客户端返回相应的结果.在某些情况下,你可能希望每当异常发生时有一个回调.通过这种方式,例如,你可以发送邮件或采取基于异常的任何动作.
只需要在你的应用程序中创建一个从`ExceptionSubscriber`派生的类:
````csharp
public class MyExceptionSubscriber : ExceptionSubscriber
{
public override async Task HandleAsync(ExceptionNotificationContext context)
{
//TODO...
}
}
````
更多信息请参见[异常处理](https://docs.abp.io/en/abp/latest/Exception-Handling).
### 其他
在之前的发布中框架也有很多小的功能和改进.这里列举几个:
* 新增了`AbpLocalizationOptions.DefaultResourceType`用来设置应用程序的默认资源类型.通过这种方式,当资源未指定时, 本地化系统使用默认的资源.最新的应用程序启动模板已经配置了,你也可以为你现有的应用程序设置它.
* 权限定义新增了`IsEnabled`.通过这种方式,你可以从应用程序中完全禁用权限和隐藏相关功能.这可在一些应用程序中做为功能开关的方式.用法请参见[#3486](https://github.com/abpframework/abp/issues/3486).
* 框架中定义的所有本地化资源新增了荷兰语和德语本地化.感谢贡献者们.
## ABP商业版有哪些新增内容?
[ABP商业版](https://commercial.abp.io/)的目标是基于ABP框架项目提供预构建的应用程序功能,代码生成工具,专业的主题,先进的示例和高级支持.
我们正在并行工作于ABP商业版与ABP框架功能对齐,并提供更多的模块,主题选项和工具.
本节将介绍ABP商业版这边有哪些进展.
### 模块实体扩展系统
模块实体扩展系统是相对于对象扩展系统(上面介绍的)的一个更高级别的API,并提供了一种简单的方法来向现有实体中增加扩展属性.一个新的扩展属性可以很容易地自动成为HTTP API和用户界面的一部分.
例如:向Identity模块的User实体中添加`SocialSecurityNumber`
````csharp
ObjectExtensionManager.Instance.Modules()
.ConfigureIdentity(identity =>
{
identity.ConfigureUser(user =>
{
user.AddOrUpdateProperty<string>( //属性类型: string
"SocialSecurityNumber", //属性名
property =>
{
//验证规则
property.Attributes.Add(new RequiredAttribute());
property.Attributes.Add(
new StringLengthAttribute(64) {
MinimumLength = 4
}
);
//...该属性的其它配置
}
);
});
});
````
仅通过这样的配置,用户界面就将具有新的属性(在表中和在创建/编辑表单中):
![module-entity-extended-ui](module-entity-extended-ui.png)
新属性可以轻松地本地化和验证.目前,它支持原始类型,如字符串,数字和布尔型,但我们计划添加更多高级场景(如导航/查找属性).
请参阅[模块实体扩展](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions)指南来了解如何使用和配置的详细信息.
#### 其他扩展点
还有其他一些预先定义的点来定制和扩展依赖模块的用户界面:
* 你可以为数据表中的实体添加新动作(下方左侧的图).
* 你可以向页面的工具栏中添加新的按钮(或其他控件)(下方右侧的图).
* 你可以向数据表中添加自定义列.
![abp-commercial-ui-extensions](abp-commercial-ui-extensions.png)
请参阅[自定义模块](https://docs.abp.io/en/commercial/latest/guides/customizing-modules)指南,以了解所有可能的方式来定制依赖模块.
### 文本模板管理模块
我们在v2.7中推出一个新的模块:[文本模板管理](https://docs.abp.io/en/commercial/latest/modules/text-template-management).它基本上是用来在用户界面上编辑文本/邮件模板(在ABP框架2.7中加入的),并保存更改到数据库中.
一个密码重置邮件模板的内容编辑截图:
![text-template-content-ui](text-template-content-ui.png)
当创建新工程时, 这个模块已经预装了.
### 实体历史视图
审计日志UI模块现在显示所有应用程序中的实体变更, 带有属性修改的细节.
![audit-log-entity-changes](audit-log-entity-changes.png)
当点击实体的动作菜单时, 你还可以查看实体的历史信息:
![tenant-entity-changes](tenant-entity-changes.png)
### 更多示例
我们正在创建ABP商业版更多高级的示例应用程序.其中一个是简易CRM, 将在几天内提供给商业客户.
这是一个简易CRM仪表盘的截图:
![easy-crm](easy-crm.png)
具有帐户,联系人,产品组,产品,订单等.
### 新模块
我们将继续改善现有模块和创建新的模块.除了上面介绍的新的[文本模板管理](https://docs.abp.io/en/commercial/latest/modules/text-template-management), 还有:
* 我们最近发布了[支付模块](https://commercial.abp.io/modules/Volo.Payment),目前对接了PayU和的2Checkout支付网关.更多网关陆续添加.
* 我们已经创建了一个简单的[Twilio短信集成](https://docs.abp.io/en/commercial/latest/modules/twilio-sms)模块,以通过Twilio发送短信.
* 我们正在开发一个**聊天模块**, 将在未来几周内可用.
* 我们正在致力于为Identity模块增加**组织单元管理**系统, 用于创建分层组织单元(Domain层将是开源和免费的).
更多ABP商业版和ABP框架的模块,主题和工具选项正在开发中.
## ABP框架 VS ABP商业版
我们([Volosoft](https://volosoft.com/) - ABP.IO平台背后的核心团队),在ABP框架和ABP商业版上花费几乎相同的精力,我们认为ABP.IO平台是一个整体.
[ABP框架](https://abp.io/)提供了所有的基础设施和应用程序的独立框架功能,使你更具生产力,专注于自己的业务代码,并实现软件开发最佳实践.它为你提供不重复作业的一个明确和舒适的开发经验.
[ABP商业版](https://commercial.abp.io/)提供内置功能,主题和工具, 如果你的需求涉及到这些, 就可以节省你的时间.除此之外还有框架的高级支持和预构建模块.

BIN
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/abp-commercial-ui-extensions.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/audit-log-entity-changes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/easy-crm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/github-contribution-graph.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/module-entity-extended-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/tenant-entity-changes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-08 v2_7_Release/text-template-content-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

214
docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/Post.md

@ -0,0 +1,214 @@
# ABP v2.8.0发布 & 路线图
**ABP框架**和**ABP商业版**v2.8已经发布.这篇文章将涵盖这些发布中的**新增内容**和项目的**中期路线图**.
## ABP框架2.8有哪些新增内容?
你可在[GitHub的发行说明](https://github.com/abpframework/abp/releases/tag/2.8.0)中看到所有的变更.这篇博客只包括重要的一些功能/变更.
### SignalR集成包
我们已经发布了[一个新的包](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR)用来集成SignalR到基于ABP框架应用程序中.
> 其实跟随[标准Microsoft教程](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr)添加[SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction)到你的应用程序中已经可以实现.但是,ABP提供了SignalR集成包用来简化集成和使用.
参见[SignalR集成文档](https://docs.abp.io/en/abp/latest/SignalR-Integration)开始使用SignalR.
#### SignalR演示应用程序
我们也创建了一个简单的聊天应用程序来演示如何使用它.
![signalr-chat-demo](signalr-chat-demo.png)
参见[应用程序的源代码.](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo)
### 控制台应用程序启动模板
新的控制台应用程序模板,可用来创建整合了ABP框架的控制台应用程序.
使用ABP CLI来创建新的控制台应用程序,并为`-t`(模板)选项指定`console`:
````bash
abp new MyApp -t console
````
感谢[@liangshiw](https://github.com/liangshiw)为此模板的贡献.
### 为MVC UI增加RTL支持和阿拉伯语本地化
[@kgamalseif](https://github.com/kgamalseif)为MVC UI贡献了RTL实现, 看起来很棒:
![rtl-ui](rtl-ui.png)
他还本地化了所有的框架和模块的资源.感谢他伟大的贡献.
### 其它
本次发布的其它一些亮点:
* 转换HttpApi.Client模块的包为.netstandard 2.0, 用来与其他类型的应用程序兼容.
* 改进的对象扩展系统,以更好地处理UI,本地化和验证.
* 为Hangfire和Quartz集成实现了禁用后台作业执行.
* 为MVC UI新增JsTree集成包.
* 移动所有示例到新的[ABP-示例](https://github.com/abpframework/abp-samples)库中并创建了[索引页](https://docs.abp.io/en/abp/latest/Samples/Index)来查看所有示例.
### 弃用
* 弃用了`app.UseMvcWithDefaultRouteAndArea()`,并引入了`app.UseConfiguredEndpoints()`(见[#3880](https://github.com/abpframework/abp/issues/3880)).
* 弃用了`UsePostgreSql()`,并为[Volo.Abp.EntityFrameworkCore.PostgreSql](http://nuget.org/packages/Volo.Abp.EntityFrameworkCore.PostgreSql)包引入了`UseNpgsql()`.如果你正在使用PostgreSQL, 切换到`UseNpgsql()`.
旧方法被标记为`Obsolete`,将在下一主要版本中删除.
## ABP商业版2.8中有哪些新增内容?
### 新的Lepton主题
我们已经彻底修改[lepton主题](https://commercial.abp.io/themes).来看下不同的风格:
![lepton-themes](lepton-themes.gif)
ABP商业版中语言管理页面的截图:
![lepton-abp-default-theme](lepton-abp-default-theme.png)
(默认风格UI)
![lepton-abp-material-theme](lepton-abp-material-theme.png)
(Material风格UI)
[创建一个演示](https://commercial.abp.io/demo)来现场测试所有的风格.你可以设置页面更改风格.
### 新增聊天模块
第一版的[聊天模块](https://commercial.abp.io/modules/Volo.Chat)已经在本版本中发布.当前只有MVC / Razor PagesUI.Angular UI正在开发中.
![abp-chat-module](abp-chat-module.png)
目前,它有一个简单的**实时文本消息**功能.更多的功能,比如群组消息,发送图像/文件在路线图中.
### 其它
* 为Angular UI实现了[模块实体扩展](https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions)系统.还提高该系统以更好地处理float/double/decimal,date,datetime,enum和bool属性.
* [EasyCRM示例应用程序](https://docs.abp.io/en/commercial/latest/samples/easy-crm)树形视图中管理产品组.
## 关于下一个版本
我们**每2周**发布一次.因此,下一个功能版本的计划的日期为**6月4日**, 版本号为**2.9**.这(可能)会是**最后的2.x版本**, 下个版本将是**3.0**.
### ABP框架 2.9 & 3.0
#### 组织单元系统
Identity模块的组织单元系统本来要在2.8中发布,但不幸的是我们不能肯定该功能的稳定性,所以推迟到了2.9.
#### gRPC
我们打算创建一个gPRC集成示例应用程序.然后,我们打算为所有[预构建模块](https://docs.abp.io/en/abp/latest/Modules/Index)和[启动模板](https://docs.abp.io/en/abp/latest/Startup-Templates/Index)创建gRPC端点.我们想为这些端点使用新计划的[Blazor](https://docs.microsoft.com/en-us/aspnet/core/blazor/) UI选项(我们知道Blazor UI有着[巨大的需求](https://github.com/abpframework/abp/issues/394).这并不意味着我们在3.0中会完成全部工作,但我们已经开始了, 并将在3.0+版本中继续.
#### Oracle与EF Core
我们看到,在ABP框架之外,人们使用Oracle与EF Core有一些痛点.这是因为EF Core 3.1目前还没有稳定并且免费的Oracle提供器.我们只看到了[Devart](https://www.devart.com/)创建了一个[付费的包](https://www.nuget.org/packages/Devart.Data.Oracle.EFCore).
[@ebicoglu](https://github.com/ebicoglu)[创建了一个gist](https://gist.github.com/ebicoglu/9f364c7eff9d87315af0178866186401)用来演示如何使用它. 我们[打算](https://github.com/abpframework/abp/issues/3983)创建一个集成包来让它更简单.
#### API文档
我们正[致力于](https://github.com/abpframework/abp/issues/1184)为框架创建一个API文档,并建立一个CD管线自动地在每次发布时公开它.这会更容易地探索框架中的类.
#### 示例应用程序:在分层/分布式系统上使用SignalR
在分布式/微服务系统上使用SignalR可能有一些麻烦,因为服务没有连接到客户端所以不能直接从服务器调用客户端函数.一个解决这个问题的方法是使用分布式消息总线(如RabbitMQ),用来向客户端转发从服务到Web应用程序的消息.
我们将创建一个示例应用程序和文档来阐述这样一个架构,和使用ABP框架它会多么的容易.
虽然这个话题与ABP框架没有直接关系, 而且这个问题不是ABP应用程序独有的,我们仍然觉得创建这样的指南对开发者是很有用的.
#### 还有...
我们会花更多的时间来写更多的文档,实现性能的提升,做更多的测试,创建更多的扩展点等等.
### ABP商业版 2.9 & 3.0
#### 组织单元系统
并行于ABP框架的组织单元系统(如上所述),我们正在创建一个UI用来管理组织单元,将在2.9中发布.
#### 聊天模块的Angular UI
聊天模块(如上所述)目前只有ASP.NET Core MVC / Razor Pages UI.我们正努力为该模块创建Angular UI
#### 新模块想法:文件管理
我们正在创建一个文件管理模块,用来管理(上传/下载)和在用户之间共享文件.你可将之视为一个轻量级的Google Drive :).
#### Easy CRM Angular UI
[Easy CRM](https://docs.abp.io/en/commercial/latest/samples/easy-crm)是我们之前发布的ABP商业版中的一个示例应用程序.在这个版本中,我们向这个应用程序中加入了更多的功能.在下一个版本中,我们会为它创建Angular UI.
我们发现这个应用程序非常有用,因为与简单的[图书商城](https://docs.abp.io/en/commercial/latest/samples/index#book-store)相比, 它非常接近现实世界的应用程序.
#### 还有..
我们正在致力于改进现有的[模块](https://commercial.abp.io/modules),[主题](https://commercial.abp.io/themes)和[工具](https://commercial.abp.io/tools), 旨在3.0版本中提供更舒适的开发者体验.
## 路线图
我们经常被问到的[ABP框架](https://abp.io/)和[ABP商业版](https://commercial.abp.io/)的路线图.虽然我们已经在各种平台上回答过,但在这次发布中,我们在这些产品的文档里增加了路线图页面:
* [ABP框架路线图](https://docs.abp.io/en/abp/latest/Road-Map)
* [ABP商业版路线图](https://docs.abp.io/en/commercial/latest/road-map)
我也把路线图写在下面的章节中:
### ABP框架路线图
你可随时在[GitHub的仓库](https://github.com/abpframework/abp/milestones)中检查里程碑规划和优先积压问题.
虽然我们会**继续添加其它令人激动的功能**,但我们在中期将主要致力于下列内容的工作:
* 为所有的预构建模块实现**gPRC集成**.
* 为框架和所有预构建的模块实现**Blazor UI**.
* **NET 5.0**!由于微软已经宣布了.NET 5.0将在2020年11月发布,在这之前我们会做好准备,并在微软发布后就迁移到.NET 5.0上, 我们希望能平稳过渡.
### ABP商业版路线图
我们将与ABP框架同步进行, 为ABP商业版实现一些相同的内容:
* gRPC集成
* Blazor UI
* .NET 5.0
除此之外,我们将在中期致力于下列内容:
* 一个用来创建微服务解决方案的启动模板(含有Ocelot,Redis,RabbitMQ,ElasticSearch,IdentityServer ..等等. 并且预先集成和配置好的).
* 更多的模块扩展点.
* 动态仪表板系统.
* 实时通知系统.
* SaaS的模块的认购及支付系统.
* 更多的身份认证选项.
* 新的应用模块(我们有几十个模块的想法, 将陆续与大家分享 - 上面宣布的"文件管理"就是其中之一).
* 新的主题与主题风格(包括公共/企业网站的主题).
## 彩蛋:ABP.IO平台路线图
ABP框架和ABP商业版是ABP.IO平台的基本组成部分,我们还想创造一个更大的平台将.NET社区聚集在一起,利用ABP框架的统一性和标准开发模型来创建可重用模块,分享知识,彼此互相帮助.
所以,我们有了新的*.abp.io网站的想法,我想分享给社区
#### market.abp.io
一个平台用于让开发者/公司发布他们基于ABP框架的可重用的应用模块,主题, 库和工具.这个网站上会有免费/开源和商业产品.
#### jobs.abp.io
我们收到了太多来自其它公司的电子邮件, 想聘请其它公司的开发者来打造基于ABP.IO平台的自己的产品.我们作为[Volosoft](https://volosoft.com/),想留在产品端,而不是客户的基础工程.我们一般会把有经验的开发者和公司介绍给他们.
我们计划创建一个网站来满足各方需要,这样你可以为你的项目找到开发者,或者找一个短期或长期的工作.
## 跟随ABP!
跟随社交媒体帐号,即可了解ABP.IO平台上发生的事情:
* [@abpframework](https://twitter.com/abpframework): ABP框架官方推特帐号
* [@abpcommercial](https://twitter.com/abpcommercial): ABP商业版官方推特帐号

BIN
docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/abp-chat-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-default-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-abp-material-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/lepton-themes.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/rtl-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-05-22 v2_8_Release/signalr-chat-demo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

9
framework/Volo.Abp.sln

@ -287,7 +287,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Validation.Abstrac
EndProject 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}" 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 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution 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}.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.ActiveCfg = Release|Any CPU
{8B758716-DCC9-4223-8421-5588D1597487}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -1005,6 +1011,7 @@ Global
{FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{B64FCE08-E9D2-4984-BF12-FE199F257416} = {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} {8B758716-DCC9-4223-8421-5588D1597487} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{79323211-E658-493E-9863-035AA4C3F913} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

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 System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.Localization;
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Bootstrap 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) 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.WidgetScripts
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles @using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.MultiTenancy @using Volo.Abp.MultiTenancy
@using Volo.Abp.Localization
@inject IAbpAntiForgeryManager AbpAntiForgeryManager @inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider @inject IBrandingProvider BrandingProvider
@inject IOptions<AbpMultiTenancyOptions> MultiTenancyOptions @inject IOptions<AbpMultiTenancyOptions> MultiTenancyOptions
@ -23,12 +24,11 @@
Layout = null; Layout = null;
AbpAntiForgeryManager.SetCookie(); AbpAntiForgeryManager.SetCookie();
var containerClass = ViewBag.FluidLayout == true ? "container-fluid" : "container"; //TODO: Better and type-safe options var containerClass = ViewBag.FluidLayout == true ? "container-fluid" : "container"; //TODO: Better and type-safe options
} }
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name"> <html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")>
<head> <head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Account) @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.Theming
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts @using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles @using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.Localization
@inject IAbpAntiForgeryManager AbpAntiForgeryManager @inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider @inject IBrandingProvider BrandingProvider
@inject IPageLayout PageLayout @inject IPageLayout PageLayout
@ -31,7 +32,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name"> <html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")>
<head> <head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Application) @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.Theming
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts @using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetScripts
@using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles @using Volo.Abp.AspNetCore.Mvc.UI.Widgets.Components.WidgetStyles
@using Volo.Abp.Localization
@inject IAbpAntiForgeryManager AbpAntiForgeryManager @inject IAbpAntiForgeryManager AbpAntiForgeryManager
@inject IBrandingProvider BrandingProvider @inject IBrandingProvider BrandingProvider
@inject IPageLayout PageLayout @inject IPageLayout PageLayout
@ -30,7 +31,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name"> <html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")>
<head> <head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Empty) @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Empty)
@ -39,7 +40,7 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>@pageTitle</title> <title>@pageTitle</title>
@if (ViewBag.Description!=null) @if (ViewBag.Description != null)
{ {
<meta name="description" content="@ViewBag.Description" /> <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 class ActionResultHelper
{ {
public static List<Type> ObjectResultTypes { get; } public static List<Type> ObjectResultTypes { get; }
static ActionResultHelper() 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); returnType = AsyncHelper.UnwrapTask(returnType);
if (!excludeTypes.IsNullOrEmpty() && excludeTypes.Any(t => t.IsAssignableFrom(returnType)))
{
return false;
}
if (!typeof(IActionResult).IsAssignableFrom(returnType)) if (!typeof(IActionResult).IsAssignableFrom(returnType))
{ {
return true; return true;
@ -32,4 +37,4 @@ namespace Volo.Abp.AspNetCore.Mvc
return ObjectResultTypes.Any(t => t.IsAssignableFrom(returnType)); 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 System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -25,7 +25,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
public AbpExceptionPageFilter( public AbpExceptionPageFilter(
IExceptionToErrorInfoConverter errorInfoConverter, IExceptionToErrorInfoConverter errorInfoConverter,
IHttpExceptionStatusCodeFinder statusCodeFinder, IHttpExceptionStatusCodeFinder statusCodeFinder,
IJsonSerializer jsonSerializer) IJsonSerializer jsonSerializer)
{ {
_errorInfoConverter = errorInfoConverter; _errorInfoConverter = errorInfoConverter;
@ -34,13 +34,12 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
Logger = NullLogger<AbpExceptionPageFilter>.Instance; Logger = NullLogger<AbpExceptionPageFilter>.Instance;
} }
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{ {
if (context.HandlerMethod == null || !ShouldHandleException(context)) if (context.HandlerMethod == null || !ShouldHandleException(context))
@ -54,20 +53,20 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{ {
return;; return;;
} }
await HandleAndWrapException(pageHandlerExecutedContext); await HandleAndWrapException(pageHandlerExecutedContext);
} }
protected virtual bool ShouldHandleException(PageHandlerExecutingContext context) protected virtual bool ShouldHandleException(PageHandlerExecutingContext context)
{ {
//TODO: Create DontWrap attribute to control wrapping..? //TODO: Create DontWrap attribute to control wrapping..?
if (context.ActionDescriptor.IsPageAction() && if (context.ActionDescriptor.IsPageAction() &&
ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType)) ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType, typeof(void)))
{ {
return true; return true;
} }
if (context.HttpContext.Request.CanAccept(MimeTypes.Application.Json)) if (context.HttpContext.Request.CanAccept(MimeTypes.Application.Json))
{ {
return true; return true;
@ -107,6 +106,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
context.Exception = null; //Handled! 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 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) if (unitOfWorkAttr?.IsDisabled == true)
@ -71,7 +71,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
} }
} }
} }
private AbpUnitOfWorkOptions CreateOptions(PageHandlerExecutingContext context, UnitOfWorkAttribute unitOfWorkAttribute) private AbpUnitOfWorkOptions CreateOptions(PageHandlerExecutingContext context, UnitOfWorkAttribute unitOfWorkAttribute)
{ {
var options = new AbpUnitOfWorkOptions(); var options = new AbpUnitOfWorkOptions();
@ -102,4 +102,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow
return result.Exception == null || result.ExceptionHandled; 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 Microsoft.AspNetCore.SignalR;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users; using Volo.Abp.Users;
namespace Volo.Abp.AspNetCore.SignalR namespace Volo.Abp.AspNetCore.SignalR
{ {
public class AbpSignalRUserIdProvider : IUserIdProvider, ITransientDependency public class AbpSignalRUserIdProvider : IUserIdProvider, ITransientDependency
{ {
public ICurrentUser CurrentUser { get; } private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public AbpSignalRUserIdProvider(ICurrentUser currentUser) private readonly ICurrentUser _currentUser;
public AbpSignalRUserIdProvider(ICurrentPrincipalAccessor currentPrincipalAccessor, ICurrentUser currentUser)
{ {
CurrentUser = currentUser; _currentPrincipalAccessor = currentPrincipalAccessor;
_currentUser = currentUser;
} }
public virtual string GetUserId(HubConnectionContext connection) 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 JetBrains.Annotations;
using Microsoft.AspNetCore.RequestLocalization; using Microsoft.AspNetCore.RequestLocalization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -6,6 +6,7 @@ using Microsoft.Extensions.Hosting;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.AspNetCore.Auditing;
using Volo.Abp.AspNetCore.ExceptionHandling; using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Security.Claims;
using Volo.Abp.AspNetCore.Tracing; using Volo.Abp.AspNetCore.Tracing;
using Volo.Abp.AspNetCore.Uow; using Volo.Abp.AspNetCore.Uow;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -76,5 +77,10 @@ namespace Microsoft.AspNetCore.Builder
app.Properties[ExceptionHandlingMiddlewareMarker] = true; app.Properties[ExceptionHandlingMiddlewareMarker] = true;
return app.UseMiddleware<AbpExceptionHandlingMiddleware>(); 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 class HttpContextCurrentPrincipalAccessor : ThreadCurrentPrincipalAccessor
{ {
public override ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor) public HttpContextCurrentPrincipalAccessor(IHttpContextAccessor httpContextAccessor)
{ {
_httpContextAccessor = 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<Type, BackgroundJobConfiguration> _jobConfigurationsByArgsType;
private readonly Dictionary<string, BackgroundJobConfiguration> _jobConfigurationsByName; private readonly Dictionary<string, BackgroundJobConfiguration> _jobConfigurationsByName;
//TODO: Implement for all providers! (Hangfire does not implement yet)
/// <summary> /// <summary>
/// Default: true. /// Default: true.
/// </summary> /// </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; using Volo.Abp.Modularity;
namespace Volo.Abp.BackgroundJobs.Hangfire namespace Volo.Abp.BackgroundJobs.Hangfire
@ -6,9 +10,23 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
[DependsOn( [DependsOn(
typeof(AbpBackgroundJobsAbstractionsModule), typeof(AbpBackgroundJobsAbstractionsModule),
typeof(AbpHangfireModule) typeof(AbpHangfireModule)
)] )]
public class AbpBackgroundJobsHangfireModule : AbpModule 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)] [Dependency(ReplaceServices = true)]
public class HangfireBackgroundJobManager : IBackgroundJobManager, ITransientDependency 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) TimeSpan? delay = null)
{ {
if (!delay.HasValue) 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.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Threading; using Volo.Abp.Threading;
@ -11,8 +13,8 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
protected IBackgroundJobExecuter JobExecuter { get; } protected IBackgroundJobExecuter JobExecuter { get; }
public HangfireJobExecutionAdapter( public HangfireJobExecutionAdapter(
IOptions<AbpBackgroundJobOptions> options, IOptions<AbpBackgroundJobOptions> options,
IBackgroundJobExecuter jobExecuter, IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory) IServiceScopeFactory serviceScopeFactory)
{ {
JobExecuter = jobExecuter; JobExecuter = jobExecuter;
@ -22,6 +24,17 @@ namespace Volo.Abp.BackgroundJobs.Hangfire
public void Execute(TArgs args) 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()) using (var scope = ServiceScopeFactory.CreateScope())
{ {
var jobType = Options.GetJob(typeof(TArgs)).JobType; 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.Modularity;
using Volo.Abp.Quartz; using Volo.Abp.Quartz;
@ -8,11 +10,21 @@ namespace Volo.Abp.BackgroundJobs.Quartz
typeof(AbpBackgroundJobsAbstractionsModule), typeof(AbpBackgroundJobsAbstractionsModule),
typeof(AbpQuartzModule) typeof(AbpQuartzModule)
)] )]
public class AbpBackgroundJobsQuartzModule :AbpModule public class AbpBackgroundJobsQuartzModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
context.Services.AddTransient(typeof(QuartzJobExecutionAdapter<>)); 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;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Quartz; using Quartz;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -8,19 +9,39 @@ namespace Volo.Abp.BackgroundJobs.Quartz
[Dependency(ReplaceServices = true)] [Dependency(ReplaceServices = true)]
public class QuartzBackgroundJobManager : IBackgroundJobManager, ITransientDependency 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) 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(); 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(); 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;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Quartz; using Quartz;
@ -8,29 +10,50 @@ namespace Volo.Abp.BackgroundJobs.Quartz
{ {
public class QuartzJobExecutionAdapter<TArgs> : IJob public class QuartzJobExecutionAdapter<TArgs> : IJob
{ {
public ILogger<QuartzJobExecutionAdapter<TArgs>> Logger { get; set; }
protected AbpBackgroundJobOptions Options { get; } protected AbpBackgroundJobOptions Options { get; }
protected AbpBackgroundJobQuartzOptions BackgroundJobQuartzOptions { get; }
protected IServiceScopeFactory ServiceScopeFactory { get; } protected IServiceScopeFactory ServiceScopeFactory { get; }
protected IBackgroundJobExecuter JobExecuter { get; } protected IBackgroundJobExecuter JobExecuter { get; }
public QuartzJobExecutionAdapter( public QuartzJobExecutionAdapter(
IOptions<AbpBackgroundJobOptions> options, IOptions<AbpBackgroundJobOptions> options,
IOptions<AbpBackgroundJobQuartzOptions> backgroundJobQuartzOptions,
IBackgroundJobExecuter jobExecuter, IBackgroundJobExecuter jobExecuter,
IServiceScopeFactory serviceScopeFactory) IServiceScopeFactory serviceScopeFactory)
{ {
JobExecuter = jobExecuter; JobExecuter = jobExecuter;
ServiceScopeFactory = serviceScopeFactory; ServiceScopeFactory = serviceScopeFactory;
Options = options.Value; Options = options.Value;
BackgroundJobQuartzOptions = backgroundJobQuartzOptions.Value;
Logger = NullLogger<QuartzJobExecutionAdapter<TArgs>>.Instance;
} }
public async Task Execute(IJobExecutionContext context) public async Task Execute(IJobExecutionContext context)
{ {
using (var scope = ServiceScopeFactory.CreateScope()) 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 jobType = Options.GetJob(typeof(TArgs)).JobType;
var jobContext = new JobExecutionContext(scope.ServiceProvider, jobType, args); 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;
}
}

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

@ -1,5 +1,7 @@
using Microsoft.Extensions.DependencyInjection; using System.Linq;
using Microsoft.Extensions.DependencyInjection.Extensions; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Quartz; using Volo.Abp.Quartz;
@ -16,14 +18,28 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
context.Services.AddConventionalRegistrar(new AbpQuartzConventionalRegistrar()); context.Services.AddConventionalRegistrar(new AbpQuartzConventionalRegistrar());
} }
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
{ {
var backgroundWorkerManager = context.ServiceProvider.GetService<IBackgroundWorkerManager>(); var options = context.ServiceProvider.GetService<IOptions<AbpBackgroundWorkerOptions>>().Value;
var works = context.ServiceProvider.GetServices<IQuartzBackgroundWorker>(); if (!options.IsEnabled)
{
var quartzOptions = context.ServiceProvider.GetService<IOptions<AbpQuartzOptions>>().Value;
quartzOptions.StartSchedulerFactory = scheduler => Task.CompletedTask;
}
}
foreach (var work in works) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var quartzBackgroundWorkerOptions = context.ServiceProvider.GetService<IOptions<AbpBackgroundWorkerQuartzOptions>>().Value;
if (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 namespace Volo.Abp.BackgroundWorkers.Quartz
{ {
@ -7,5 +9,9 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
ITrigger Trigger { get; set; } ITrigger Trigger { get; set; }
IJobDetail JobDetail { 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; using Quartz;
namespace Volo.Abp.BackgroundWorkers.Quartz namespace Volo.Abp.BackgroundWorkers.Quartz
@ -8,6 +9,10 @@ namespace Volo.Abp.BackgroundWorkers.Quartz
public ITrigger Trigger { get; set; } public ITrigger Trigger { get; set; }
public IJobDetail JobDetail { 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); 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; _scheduler = scheduler;
} }
public async Task StartAsync(CancellationToken cancellationToken = default) public virtual async Task StartAsync(CancellationToken cancellationToken = default)
{ {
await _scheduler.ResumeAll(cancellationToken); await _scheduler.ResumeAll(cancellationToken);
} }
public async Task StopAsync(CancellationToken cancellationToken = default) public virtual async Task StopAsync(CancellationToken cancellationToken = default)
{ {
if (!_scheduler.IsShutdown) 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) if (worker is IQuartzBackgroundWorker quartzWork)
{ {
Check.NotNull(quartzWork.Trigger, nameof(quartzWork.Trigger)); Check.NotNull(quartzWork.Trigger, nameof(quartzWork.Trigger));
Check.NotNull(quartzWork.JobDetail, nameof(quartzWork.JobDetail)); 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);
} }
} }
} }
} }

12
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GenerateProxyCommand.cs

@ -13,6 +13,7 @@ using Volo.Abp.DependencyInjection;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using Volo.Abp.Reflection;
namespace Volo.Abp.Cli.Commands namespace Volo.Abp.Cli.Commands
{ {
@ -516,7 +517,16 @@ namespace Volo.Abp.Cli.Commands
baseTypeKebabCase = "@abp/ng.core"; baseTypeKebabCase = "@abp/ng.core";
baseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Split("<")[0].TrimStart('.'); baseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Split("<")[0].TrimStart('.');
customBaseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].Replace("System.Guid", "string").TrimStart('.'); customBaseTypeName = baseType.Split("Volo.Abp.Application.Dtos")[1].TrimStart('.');
if (customBaseTypeName.Contains("<"))
{
var genericType = customBaseTypeName.Split("<")[1].TrimEnd('>');
var customBaseType = Type.GetType(genericType);
if (customBaseType != null)
{
customBaseTypeName = customBaseTypeName.Replace(genericType, TypeHelper.GetSimplifiedName(customBaseType));
}
}
} }
if (baseTypeName.Contains("guid") || baseTypeName.Contains("Guid")) if (baseTypeName.Contains("guid") || baseTypeName.Contains("Guid"))

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.Args;
using Volo.Abp.Cli.ProjectBuilding; using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.ProjectBuilding.Building; using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Templates.Console;
using Volo.Abp.Cli.Utils; using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -266,6 +267,7 @@ namespace Volo.Abp.Cli.Commands
protected virtual MobileApp GetMobilePreference(CommandLineArgs commandLineArgs) protected virtual MobileApp GetMobilePreference(CommandLineArgs commandLineArgs)
{ {
var optionValue = commandLineArgs.Options.GetOrNull(Options.Mobile.Short, Options.Mobile.Long); var optionValue = commandLineArgs.Options.GetOrNull(Options.Mobile.Short, Options.Mobile.Long);
var template = commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long);
switch (optionValue) switch (optionValue)
{ {
case "none": case "none":
@ -273,7 +275,7 @@ namespace Volo.Abp.Cli.Commands
case "react-native": case "react-native":
return MobileApp.ReactNative; return MobileApp.ReactNative;
default: 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 System.Threading.Tasks;
using Volo.Abp.Cli.Http; using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ProjectBuilding.Templates.App; using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.Cli.ProjectBuilding.Templates.Console;
using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule; using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Http; 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("The remote service is currently unavailable, please specify the version.");
Logger.LogWarning(string.Empty); Logger.LogWarning(string.Empty);
Logger.LogWarning("Find the following template in your cache directory: "); Logger.LogWarning("Find the following template in your cache directory: ");
Logger.LogWarning("\t Template Name\tVersion"); Logger.LogWarning("\tTemplate Name\tVersion");
var templateList = GetLocalTemplates(); var templateList = GetLocalTemplates();
foreach (var cacheFile in templateList) 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); Logger.LogWarning(string.Empty);
@ -241,7 +242,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
stringBuilder.AppendLine(cacheFile); 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) foreach (Match match in matches)
{ {
templateList.Add((match.Groups[1].Value, match.Groups[2].Value)); templateList.Add((match.Groups[1].Value, match.Groups[2].Value));
@ -278,4 +279,4 @@ namespace Volo.Abp.Cli.ProjectBuilding
public string Version { get; set; } 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 System;
using Volo.Abp.Cli.ProjectBuilding.Building; using Volo.Abp.Cli.ProjectBuilding.Building;
using Volo.Abp.Cli.ProjectBuilding.Templates.App; using Volo.Abp.Cli.ProjectBuilding.Templates.App;
using Volo.Abp.Cli.ProjectBuilding.Templates.Console;
using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule; using Volo.Abp.Cli.ProjectBuilding.Templates.MvcModule;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -25,9 +26,11 @@ namespace Volo.Abp.Cli.ProjectBuilding
return new ModuleTemplate(); return new ModuleTemplate();
case ModuleProTemplate.TemplateName: case ModuleProTemplate.TemplateName:
return new ModuleProTemplate(); return new ModuleProTemplate();
case ConsoleTemplate.TemplateName:
return new ConsoleTemplate();
default: default:
throw new Exception("There is no template found with given name: " + name); 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 System.Globalization;
using JetBrains.Annotations; 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) public static bool IsValidCultureCode(string cultureCode)
{ {
if (cultureCode.IsNullOrWhiteSpace()) if (cultureCode.IsNullOrWhiteSpace())
@ -60,4 +62,4 @@ namespace Volo.Abp.Localization
: cultureName; : 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) public override void OnApplicationShutdown(ApplicationShutdownContext context)
{ {
//TODO: ABP may provide two methods for application shutdown: OnPreApplicationShutdown & OnApplicationShutdown if (_backgroundJobServer != null)
_backgroundJobServer.SendStop(); {
_backgroundJobServer.Dispose(); _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) 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(AsyncHelper.RunSync(() => new StdSchedulerFactory(options.Properties).GetScheduler()));
context.Services.AddSingleton(typeof(IJobFactory), typeof(AbpQuartzJobFactory)); context.Services.AddSingleton(typeof(IJobFactory), typeof(AbpQuartzJobFactory));
Configure<AbpQuartzOptions>(quartzOptions =>
{
quartzOptions.Properties = options.Properties;
quartzOptions.StartDelay = options.StartDelay;
});
} }
public override void OnApplicationInitialization(ApplicationInitializationContext context) 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 = context.ServiceProvider.GetService<IScheduler>();
_scheduler.JobFactory = context.ServiceProvider.GetService<IJobFactory>(); _scheduler.JobFactory = context.ServiceProvider.GetService<IJobFactory>();
if (options.StartDelay.Ticks > 0) AsyncHelper.RunSync(() => options.StartSchedulerFactory.Invoke(_scheduler));
{
AsyncHelper.RunSync(() => _scheduler.StartDelayed(options.StartDelay));
}
else
{
AsyncHelper.RunSync(() => _scheduler.Start());
}
} }
public override void OnApplicationShutdown(ApplicationShutdownContext context) public override void OnApplicationShutdown(ApplicationShutdownContext context)
{ {
//TODO: ABP may provide two methods for application shutdown: OnPreApplicationShutdown & OnApplicationShutdown if (_scheduler.IsStarted)
AsyncHelper.RunSync(() => _scheduler.Shutdown()); {
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 namespace Volo.Abp.Security.Claims
{ {
public interface ICurrentPrincipalAccessor public interface ICurrentPrincipalAccessor
{ {
ClaimsPrincipal Principal { get; } 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 System.Threading;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -6,6 +7,28 @@ namespace Volo.Abp.Security.Claims
{ {
public class ThreadCurrentPrincipalAccessor : ICurrentPrincipalAccessor, ISingletonDependency 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); var localizer = GetLocalizerOrNull(templateDefinition);
if (localizer != null) if (localizer != null)
{ {
scriptObject.Import( scriptObject.SetValue("L", new TemplateLocalizer(localizer), true);
"L",
new Func<string, string>(
name => localizer[name]
)
);
} }
context.PushGlobal(scriptObject); context.PushGlobal(scriptObject);
@ -163,4 +158,4 @@ namespace Volo.Abp.TextTemplating
return _stringLocalizerFactory.CreateDefaultOrNull(); 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 be between {1} and {2}.": "字段{0}值必须在{1}和{2}范围内.",
"The field {0} must match the regular expression '{1}'.": "字段{0}与请求的格式不匹配。", "The field {0} must match the regular expression '{1}'.": "字段{0}与请求的格式不匹配。",
"The {0} field is required.": "字段{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 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 {0} field is not a valid fully-qualified http, https, or ftp URL.": "字段{0}不是有效的完全限定的http,https或ftp URL.",
"The field {0} is invalid.": "字段{0}是无效值.", "The field {0} is invalid.": "字段{0}是无效值.",
@ -31,4 +31,4 @@
"ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "字段{0}不是有效的完全限定的http,https或ftp URL.", "ThisFieldIsNotAValidFullyQualifiedHttpHttpsOrFtpUrl": "字段{0}不是有效的完全限定的http,https或ftp URL.",
"ThisFieldIsInvalid.": "该字段无效." "ThisFieldIsInvalid.": "该字段无效."
} }
} }

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

@ -1,13 +1,13 @@
using System; using System;
using System.Linq; using System.Security.Claims;
using Localization.Resources.AbpUi; using Localization.Resources.AbpUi;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.Authorization; using Volo.Abp.AspNetCore.Mvc.Authorization;
using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.Localization.Resource; using Volo.Abp.AspNetCore.Mvc.Localization.Resource;
using Volo.Abp.AspNetCore.Security.Claims;
using Volo.Abp.AspNetCore.TestBase; using Volo.Abp.AspNetCore.TestBase;
using Volo.Abp.Autofac; using Volo.Abp.Autofac;
using Volo.Abp.Localization; 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("en", "en", "English"));
options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe")); options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe"));
}); });
Configure<RazorPagesOptions>(options => Configure<RazorPagesOptions>(options =>
{ {
options.RootDirectory = "/Volo/Abp/AspNetCore/Mvc"; 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) public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -92,6 +98,7 @@ namespace Volo.Abp.AspNetCore.Mvc
app.UseAbpRequestLocalization(); app.UseAbpRequestLocalization();
app.UseRouting(); app.UseRouting();
app.UseMiddleware<FakeAuthenticationMiddleware>(); app.UseMiddleware<FakeAuthenticationMiddleware>();
app.UseAbpClaimsMap();
app.UseAuthorization(); app.UseAuthorization();
app.UseAuditing(); app.UseAuditing();
app.UseUnitOfWork(); 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); 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; using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
{ {
public class ExceptionTestPage : AbpPageModel public class ExceptionTestPage : AbpPageModel
{ {
public void OnGetUserFriendlyException1() public void OnGetUserFriendlyException_void()
{ {
throw new UserFriendlyException("This is a sample exception!"); 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!"); 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 System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@ -24,15 +24,33 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
} }
[Fact] [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); await Assert.ThrowsAsync<UserFriendlyException>(
result.Error.ShouldNotBeNull(); async () => await GetResponseAsStringAsync(
result.Error.Message.ShouldBe("This is a sample exception!"); "/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_Void"
)
);
#pragma warning disable 4014 #pragma warning disable 4014
_fakeExceptionSubscriber _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>()); .HandleAsync(Arg.Any<ExceptionNotificationContext>());
#pragma warning restore 4014 #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() public async Task Should_Not_Handle_Exceptions_For_ActionResult_Return_Values()
{ {
await Assert.ThrowsAsync<UserFriendlyException>( await Assert.ThrowsAsync<UserFriendlyException>(
async () => await GetResponseAsObjectAsync<RemoteServiceErrorResponse>( async () => await GetResponseAsStringAsync(
"/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException2" "/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException_ActionResult"
) )
); );
@ -50,7 +68,35 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling
_fakeExceptionSubscriber _fakeExceptionSubscriber
.DidNotReceive() .DidNotReceive()
.HandleAsync(Arg.Any<ExceptionNotificationContext>()); .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 #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")] [RequiresFeature("NotAllowedFeature")]
public void OnGetNotAllowedFeature() public ObjectResult OnGetNotAllowedFeature()
{ {
return new ObjectResult(42);
} }
public ObjectResult OnGetNoFeature() public ObjectResult OnGetNoFeature()
@ -24,4 +24,4 @@ namespace Volo.Abp.AspNetCore.Mvc.Features
return new ObjectResult(42); 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)] [UnitOfWork(isTransactional: true)]
public void OnGetHandledException() public ObjectResult OnGetHandledException()
{ {
CurrentUnitOfWork.ShouldNotBeNull(); CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue(); CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue();
throw new UserFriendlyException("This is a sample exception!"); throw new UserFriendlyException("This is a sample exception!");
} }
public ObjectResult OnGetExceptionOnComplete() public ObjectResult OnGetExceptionOnComplete()
{ {
CurrentUnitOfWork.ShouldNotBeNull(); CurrentUnitOfWork.ShouldNotBeNull();
CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse(); CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse();
_testUnitOfWorkConfig.ThrowExceptionOnComplete = true; _testUnitOfWorkConfig.ThrowExceptionOnComplete = true;
//Prevent rendering of pages. //Prevent rendering of pages.
return new ObjectResult(""); 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

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

Loading…
Cancel
Save