Browse Source

Merge branch 'dev' into maliming/BlobStoringAzureProvider

pull/4200/head
maliming 6 years ago
parent
commit
6f19c73b9c
  1. 11
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  3. 9
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/pt-BR.json
  4. 4
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json
  5. 2
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json
  6. 2
      common.props
  7. 3
      docs/en/Blob-Storing.md
  8. 296
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/Post.md
  9. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/abp-chat-module.png
  10. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/easy-crm.png
  11. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/lepton-theme.png
  12. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/organization-units.png
  13. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/signalr-tiered-demo.png
  14. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/suite.png
  15. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-1.png
  16. BIN
      docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-2.png
  17. 1
      docs/en/CLI.md
  18. 19
      docs/en/Contribution/Index.md
  19. 166
      docs/en/CurrentUser.md
  20. 11
      docs/en/Entity-Framework-Core-Oracle.md
  21. 5
      docs/en/How-To/Customize-Login-Page-MVC.md
  22. 4
      docs/en/Index.md
  23. 4
      docs/en/Object-Extensions.md
  24. 23
      docs/en/Road-Map.md
  25. 21
      docs/en/Startup-Templates/Console.md
  26. 4
      docs/en/Startup-Templates/Index.md
  27. 21
      docs/en/Tutorials/Part-1.md
  28. 22
      docs/en/Tutorials/Part-2.md
  29. 4
      docs/en/UI/Angular/Component-Replacement.md
  30. 500
      docs/en/UI/Angular/Permission-Management-Component-Replacement.md
  31. BIN
      docs/en/UI/Angular/images/permission-management-modal.png
  32. 6
      docs/en/UI/AspNetCore/Customization-User-Interface.md
  33. 10
      docs/en/Virtual-File-System.md
  34. 8
      docs/en/docs-nav.json
  35. 11
      docs/pt-BR/Tutorials/AspNetCore-Mvc/Part-I.md
  36. 8
      docs/pt-BR/Tutorials/AspNetCore-Mvc/Part-II.md
  37. 294
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/Post.md
  38. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/abp-chat-module.png
  39. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/easy-crm.png
  40. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/lepton-theme.png
  41. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/organization-units.png
  42. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/signalr-tiered-demo.png
  43. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/suite.png
  44. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-1.png
  45. BIN
      docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-2.png
  46. 18
      docs/zh-Hans/Contribution/Index.md
  47. 10
      docs/zh-Hans/Entity-Framework-Core-Oracle.md
  48. 5
      docs/zh-Hans/How-To/Customize-Login-Page-MVC.md
  49. 55
      docs/zh-Hans/Object-To-Object-Mapping.md
  50. 26
      docs/zh-Hans/Road-Map.md
  51. 15
      docs/zh-Hans/Tutorials/Part-1.md
  52. 10
      docs/zh-Hans/Tutorials/Part-2.md
  53. 6
      docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md
  54. 10
      docs/zh-Hans/Virtual-File-System.md
  55. 5
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Account.cshtml
  56. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Application.cshtml
  57. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts/Empty.cshtml
  58. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/AbpPageSearchBoxViewComponent.cs
  59. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/Default.cshtml
  60. 30
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs
  61. 24
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs
  62. 2
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpViewComponent.cs
  63. 30
      framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHub.cs
  64. 4
      framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs
  65. 1
      framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj
  66. 53
      framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs
  67. 21
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs
  68. 23
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  69. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs
  70. 25
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs
  71. 5
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/ProjectBuildArgs.cs
  72. 3
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs
  73. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs
  74. 1
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  75. 24
      framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs
  76. 12
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs
  77. 8
      framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs
  78. 1
      framework/src/Volo.Abp.IdentityModel/Volo.Abp.IdentityModel.csproj
  79. 6
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/AbpIdentityModelModule.cs
  80. 24
      framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs
  81. 2
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/ApplicationMenuItem.cs
  82. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json
  83. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json
  84. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json
  85. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json
  86. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json
  87. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json
  88. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json
  89. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sl.json
  90. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json
  91. 7
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json
  92. 3
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json
  93. 12
      framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs
  94. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml
  95. 4
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/_ViewImports.cshtml
  96. 4
      modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs
  97. 13
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPage.cs
  98. 4
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml
  99. 3
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml
  100. 6
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml

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

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

2
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

@ -39,7 +39,7 @@
"Theming": "Theming", "Theming": "Theming",
"ThemingExplanationShort": "Use and customize the bootstrap-based standard UI theme or create your own one.", "ThemingExplanationShort": "Use and customize the bootstrap-based standard UI theme or create your own one.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms",
"BootstrapTagHelpersDynamicFormsExplanation": "Built-in background job system that can be integrated to Hangfire, RabbitMQ or any tool you like.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "Instead of manually writing the repeating details of bootstrap components, Use ABP's tag helpers to simplify it and take advantage of intellisense. Dynamic form can create the complete form from a C# class as the model.",
"HTTPAPIsDynamicProxies": "HTTP APIs & Dynamic Proxies", "HTTPAPIsDynamicProxies": "HTTP APIs & Dynamic Proxies",
"HTTPAPIsDynamicProxiesExplanation": "Automatically expose application services as REST style HTTP APIs and consume with dynamic JavaScript & C# proxies.", "HTTPAPIsDynamicProxiesExplanation": "Automatically expose application services as REST style HTTP APIs and consume with dynamic JavaScript & C# proxies.",
"CompleteArchitectureInfo": "Modern architecture to create maintainable software solutions.", "CompleteArchitectureInfo": "Modern architecture to create maintainable software solutions.",

9
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/pt-BR.json

@ -29,17 +29,17 @@
"Authorization": "Autorização", "Authorization": "Autorização",
"AuthorizationExplanation": "Autorização avançada com usuário, função e sistema de permissão refinado. Criada na biblioteca Microsoft Identity.", "AuthorizationExplanation": "Autorização avançada com usuário, função e sistema de permissão refinado. Criada na biblioteca Microsoft Identity.",
"MultiTenancy": "Múltiplos inquilinos", "MultiTenancy": "Múltiplos inquilinos",
"MultiTenancyExplanation": "Aplicativos SaaS facilitados! Multilocação integrada do banco de dados à interface do usuário.", "MultiTenancyExplanationShort": "Aplicativos SaaS facilitados! Multilocação integrada do banco de dados à interface do usuário.",
"CrossCuttingConcerns": "Características transversais", "CrossCuttingConcerns": "Características transversais",
"CrossCuttingConcernsExplanation": "Infraestrutura completa para autorização, validação, tratamento de exceções, armazenamento em cache, log de auditoria, gerenciamento de transações e assim por diante.", "CrossCuttingConcernsExplanationShort": "Infraestrutura completa para autorização, validação, tratamento de exceções, armazenamento em cache, log de auditoria, gerenciamento de transações e assim por diante.",
"BuiltInBundlingMinification": "Pacote & Minificação Integrados", "BuiltInBundlingMinification": "Pacote & Minificação Integrados",
"BuiltInBundlingMinificationExplanation": "Pare de usar ferramentas externas para empacotamento e minificação. O ABP oferece uma maneira mais simples, dinâmica, poderosa, modular e integrada!", "BuiltInBundlingMinificationExplanation": "Pare de usar ferramentas externas para empacotamento e minificação. O ABP oferece uma maneira mais simples, dinâmica, poderosa, modular e integrada!",
"VirtualFileSystem": "Sistema de arquivos virtual", "VirtualFileSystem": "Sistema de arquivos virtual",
"VirtualFileSystemExplanation": "Incorpore visualizações, scripts, estilos, imagens ... aos pacotes/bibliotecas e reutilize-os em diferentes aplicativos.", "VirtualFileSystemExplanation": "Incorpore visualizações, scripts, estilos, imagens ... aos pacotes/bibliotecas e reutilize-os em diferentes aplicativos.",
"Theming": "Theming", "Theming": "Theming",
"ThemingExplanation": "Use e personalize o tema da interface do usuário padrão baseado em bootstrap-based ou crie o seu próprio.", "ThemingExplanationShort": "Use e personalize o tema da interface do usuário padrão baseado em bootstrap-based ou crie o seu próprio.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms",
"BootstrapTagHelpersDynamicFormsExplanation": "Sistema de tarefas em segundo plano integrado que pode ser integrado ao Hangfire, RabbitMQ ou a qualquer ferramenta que você desejar.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "Em vez de escrever manualmente os detalhes repetidos dos componentes de autoinicialização, use os auxiliares de tag da ABP para simplificá-lo e tirar proveito do intellisense. O formulário dinâmico pode criar o formulário completo a partir de uma classe C# como modelo.",
"HTTPAPIsDynamicProxies": "APIs HTTP e proxies dinâmicos", "HTTPAPIsDynamicProxies": "APIs HTTP e proxies dinâmicos",
"HTTPAPIsDynamicProxiesExplanation": "Exponha automaticamente os serviços de aplicativo como APIs HTTP do estilo REST e consuma com proxies JavaScript & C # dinâmicos.", "HTTPAPIsDynamicProxiesExplanation": "Exponha automaticamente os serviços de aplicativo como APIs HTTP do estilo REST e consuma com proxies JavaScript & C # dinâmicos.",
"CompleteArchitectureInfo": "Arquitetura moderna para criar soluções de software sustentáveis.", "CompleteArchitectureInfo": "Arquitetura moderna para criar soluções de software sustentáveis.",
@ -100,7 +100,6 @@
"DistributedEventBus": "Barramento de Eventos Distribuídos", "DistributedEventBus": "Barramento de Eventos Distribuídos",
"DistributedEventBusWithRabbitMQIntegration": "Barramento de Eventos Distribuídos com Integração RabbitMQ", "DistributedEventBusWithRabbitMQIntegration": "Barramento de Eventos Distribuídos com Integração RabbitMQ",
"TestInfrastructure": "Infraestrutura de Teste", "TestInfrastructure": "Infraestrutura de Teste",
"AuditLogging": "Log de auditoria",
"AuditLoggingEntityHistories": "Log de auditoria e históricos de entidades", "AuditLoggingEntityHistories": "Log de auditoria e históricos de entidades",
"ObjectToObjectMapping": "Objeto para Mapeamento de Objetos", "ObjectToObjectMapping": "Objeto para Mapeamento de Objetos",
"EmailSMSAbstractions": "Abstrações de E-mail e SMS", "EmailSMSAbstractions": "Abstrações de E-mail e SMS",

4
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hans.json

@ -31,7 +31,7 @@
"MultiTenancy": "多租户", "MultiTenancy": "多租户",
"MultiTenancyExplanationShort": "SaaS应用程序变得简单! 从数据库到UI的多租户集成.", "MultiTenancyExplanationShort": "SaaS应用程序变得简单! 从数据库到UI的多租户集成.",
"CrossCuttingConcerns": "横切关注点", "CrossCuttingConcerns": "横切关注点",
"CrossCuttingConcernsExplanationShort": "完整的基础架构,用于授权,验证,异常处理,缓存,审计日志记录,事务管理等.", "CrossCuttingConcernsExplanationShort": "完整的基础架构,用于授权,验证,异常处理,缓存,审计日志记录,事务管理等.",
"BuiltInBundlingMinification": "内置Bundling & Minification", "BuiltInBundlingMinification": "内置Bundling & Minification",
"BuiltInBundlingMinificationExplanation": "无需使用外部工具进行Bundling & Minification. ABP提供了一种更简单,动态,功能强大,模块化和内置的方式!", "BuiltInBundlingMinificationExplanation": "无需使用外部工具进行Bundling & Minification. ABP提供了一种更简单,动态,功能强大,模块化和内置的方式!",
"VirtualFileSystem": "虚拟文件系统", "VirtualFileSystem": "虚拟文件系统",
@ -39,7 +39,7 @@
"Theming": "主题", "Theming": "主题",
"ThemingExplanationShort": "使用和定制基于bootstrap的标准UI主题,或创建自己的主题.", "ThemingExplanationShort": "使用和定制基于bootstrap的标准UI主题,或创建自己的主题.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和动态表单", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和动态表单",
"BootstrapTagHelpersDynamicFormsExplanation": "内置的后台作业系统可以集成到Hangfire,RabbitMQ或您喜欢的任何工具中.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "与其手动重复编写Bootstrap的组件,不如使用ABP的Tag Helpers利用智能感知来简化它. 动态表单可以从C#类创建完整的表单.",
"HTTPAPIsDynamicProxies": "HTTP APIs和动态代理", "HTTPAPIsDynamicProxies": "HTTP APIs和动态代理",
"HTTPAPIsDynamicProxiesExplanation": "自动将应用程序服务公开为REST样式的HTTP API,并与动态JavaScript和C#代理一起使用.", "HTTPAPIsDynamicProxiesExplanation": "自动将应用程序服务公开为REST样式的HTTP API,并与动态JavaScript和C#代理一起使用.",
"CompleteArchitectureInfo": "现代架构用来创建可维护的软件解决方案.", "CompleteArchitectureInfo": "现代架构用来创建可维护的软件解决方案.",

2
abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/zh-Hant.json

@ -39,7 +39,7 @@
"Theming": "主題", "Theming": "主題",
"ThemingExplanationShort": "使用和訂製基於bootstrap的標準UI主題,或建立自己的主題.", "ThemingExplanationShort": "使用和訂製基於bootstrap的標準UI主題,或建立自己的主題.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和動態表單", "BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers和動態表單",
"BootstrapTagHelpersDynamicFormsExplanation": "內建的背景作業系統可以整合到Hangfire,RabbitMQ或您喜歡的任何工具中.", //TODO explanation doesn't match. "BootstrapTagHelpersDynamicFormsExplanation": "與其手動重複編寫Bootstrap的組件,不如使用ABP的Tag Helpers利用智能感知來簡化它. 動態表單可以從C#類創建完整的表單.",
"HTTPAPIsDynamicProxies": "HTTP APIs和動態代理", "HTTPAPIsDynamicProxies": "HTTP APIs和動態代理",
"HTTPAPIsDynamicProxiesExplanation": "自動將應用程式服務公開為REST樣式的HTTP API,並與動態JavaScript和C#代理一起使用.", "HTTPAPIsDynamicProxiesExplanation": "自動將應用程式服務公開為REST樣式的HTTP API,並與動態JavaScript和C#代理一起使用.",
"CompleteArchitectureInfo": "現在架構用來建立可維護的軟體解決方案.", "CompleteArchitectureInfo": "現在架構用來建立可維護的軟體解決方案.",

2
common.props

@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Version>2.9.0</Version> <Version>3.0.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>

3
docs/en/Blob-Storing.md

@ -0,0 +1,3 @@
# Blog Storing
TODO

296
docs/en/Blog-Posts/2020-06-05 v2_9_Release/Post.md

@ -0,0 +1,296 @@
# ABP Framework v2.9 Has Been Released
The **ABP Framework** & and the **ABP Commercial** version 2.9 have been released, which are the last versions before v3.0! This post will cover **what's new** with these this release.
## What's New with the ABP Framework 2.9?
You can see all the changes on the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.9.0). This post will only cover the important features/changes.
### Pre-Compiling Razor Pages
Pre-built pages (for [the application modules](https://docs.abp.io/en/abp/latest/Modules/Index)) and view components were compiling on runtime until this version. Now, they are pre-compiled and we've measured that the application startup time (especially for the MVC UI) has been reduced more than 50%. In other words, it is **two-times faster** than the previous version. The speed change also effects when you visit a page for the first time.
Here, a test result for the startup application template with v2.8 and v.2.9:
````
### v2.8
2020-06-04 22:59:04.891 +08:00 [INF] Starting web host.
2020-06-04 22:59:07.662 +08:00 [INF] Now listening on: https://localhost:44391
2020-06-04 22:59:17.315 +08:00 [INF] Request finished in 7756.6218ms 200 text/html;
Total: 12.42s
### v2.9
2020-06-04 22:59:13.720 +08:00 [INF] Starting web host.
2020-06-04 22:59:16.639 +08:00 [INF] Now listening on: https://localhost:44369
2020-06-04 22:59:18.957 +08:00 [INF] Request finished in 1780.5461ms 200 text/html;
Total: 5.24s
````
You do nothing to get the benefit of the new approach. [Overriding UI pages/components](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface) are also just working as before. We will be working on more performance improvements in the v3.0.
### Organization Unit System
[The Identity Module](https://docs.abp.io/en/abp/latest/Modules/Identity) now has the most requested feature: Organization Units!
Organization unit system is used to create a hierarchical organization tree in your application. You can then use this organization tree to authorize data and functionality in your application.
The documentation will come soon...
### New Blob Storing Package
We've created a new [Blob Storing package](https://www.nuget.org/packages/Volo.Abp.BlobStoring) to store arbitrary binary objects. It is generally used to store the content of the files in your application. This package provides an abstraction, so any application or [module](https://docs.abp.io/en/abp/latest/Module-Development-Basics) can save and retrieve files independent from the actual storing provider.
There are two storage provider currently implemented:
* [Volo.Abp.BlobStoring.FileSystem](https://www.nuget.org/packages/Volo.Abp.BlobStoring.FileSystem) package stores objects/files in the local file system.
* [Volo.Abp.BlobStoring.Database](https://github.com/abpframework/abp/tree/dev/modules/blob-storing-database) module stores objects/files in a database. It currently supports [Entity Framework Core](https://docs.abp.io/en/abp/latest/Entity-Framework-Core) (so, you can use [any relational DBMS](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Other-DBMS)) and [MongoDB](https://docs.abp.io/en/abp/latest/MongoDB).
[Azure BLOB provider](https://github.com/abpframework/abp/issues/4098) will be available with v3.0. You can request other cloud providers or contribute yourself on the [GitHub repository](https://github.com/abpframework/abp/issues/new).
One of the benefits of the blob storing system is that it allows you to create multiple containers (each container is a blob storage) and use different storage providers for each container.
**Example: Use the default container to save and get a byte array**
````csharp
public class MyService : ITransientDependency
{
private readonly IBlobContainer _container;
public MyService(IBlobContainer container)
{
_container = container;
}
public async Task FooAsync()
{
//Save a BLOB
byte[] bytes = GetBytesFromSomeWhere();
await _container.SaveAsync("my-unique-blob-name", bytes);
//Retrieve a BLOB
bytes = await _container.GetAllBytesAsync("my-unique-blob-name");
}
}
````
It can work with `byte[]` and `Stream` objects.
**Example: Use a typed (named) container to save and get a stream**
````csharp
public class MyService : ITransientDependency
{
private readonly IBlobContainer<TestContainer> _container;
public MyService(IBlobContainer<TestContainer> container)
{
_container = container;
}
public async Task FooAsync()
{
//Save a BLOB
Stream stream = GetStreamFromSomeWhere();
await _container.SaveAsync("my-unique-blob-name", stream);
//Retrieve a BLOB
stream = await _container.GetAsync("my-unique-blob-name");
}
}
````
`TestContainer` is an empty class that has no purpose than identifying the container:
````csharp
[BlobContainerName("test")] //specifies the name of the container
public class TestContainer
{
}
````
A typed (named) container can be configured to use a different storing provider than the default one. It is a good practice to always use a typed container while developing re-usable modules, so the final application can configure provider for this container without effecting the other containers.
**Example: Configure the File System provider for the `TestContainer`**
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.Configure<TestContainer>(configuration =>
{
configuration.UseFileSystem(fileSystem =>
{
fileSystem.BasePath = "C:\\MyStorageFolder";
});
});
});
````
See the [blob storing documentation](https://docs.abp.io/en/abp/latest/Blob-Storing) for more information.
### Oracle Integration Package for Entity Framework Core
We've created an [integration package for Oracle](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart), so you can easily switch to the Oracle for the EF Core. It is tested for the framework and pre-built modules.
[See the documentation](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Oracle) to start using the Oracle integration package.
### Automatically Determining the Database Provider
When you develop a **reusable application module** with EF Core integration, you generally want to develop your module **DBMS independent**. However, there are minor (sometimes major) differences between different DBMSs. If you perform a custom mapping based on the DBMS, you can now use `ModelBuilder.IsUsingXXX()` extension methods:
````csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Phone>(b =>
{
//...
if (modelBuilder.IsUsingPostgreSql()) //Check if using PostgreSQL!
{
b.Property(x => x.Number).HasMaxLength(20);
}
else
{
b.Property(x => x.Number).HasMaxLength(32);
}
});
}
````
Beside the stupid example above, you can configure your mapping however you need!
### ABP CLI: Translate Command
`abp translate` is a new command that simplifies to translate [localization](https://docs.abp.io/en/abp/latest/Localization) files when you have multiple JSON localization files in a source control repository.
The main purpose of this command is to **translate the ABP Framework** localization files (since the [abp repository](https://github.com/abpframework/abp) has tens of localization files to be translated in different folders).
It is appreciated if you use this command to translate the framework resources **for your mother language**.
See [the documentation](https://docs.abp.io/en/abp/latest/CLI#translate) to learn how to use it. Also see [the contribution guide](https://docs.abp.io/en/abp/latest/Contribution/Index).
### The New Virtual File System Explorer Module
Thanks to [@liangshiw](https://github.com/liangshiw) created and contributed a new module to explore files in the [Virtual File System](https://docs.abp.io/en/abp/latest/Virtual-File-System). It works for MVC UI and shows all the virtual files in the application. Example screenshots:
![virtual-file-explorer-1](virtual-file-explorer-1.png)
![virtual-file-explorer-2](virtual-file-explorer-2.png)
[See the documentation](https://docs.abp.io/en/abp/latest/Modules/Virtual-File-Explorer) to learn how to use it.
### Sample Application: SignalR with Tiered Architecture
Implementing SignalR in a distributed/tiered architecture can be challenging. We've created a sample application that demonstrate how to implement it using the [SignalR integration](https://docs.abp.io/en/abp/latest/SignalR-Integration) and the [distributed event bus](https://docs.abp.io/en/abp/latest/Distributed-Event-Bus) system easily.
See [the source code](https://github.com/abpframework/abp-samples/tree/master/SignalRTieredDemo) of the sample solution.
**An article is on the road** that will deeply explain the solution. Follow the [@abpframework](https://twitter.com/abpframework) Twitter account.
![signalr-tiered-demo](signalr-tiered-demo.png)
*A picture from the article that shows the communication diagram of the solution*
### About gRPC
We've created a sample application to show how to create and consume gRPC endpoints in your ABP based applications.
See [the source code](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) on GitHub.
We were planning to create gRPC endpoints for all the pre-built application modules, but we see that ASP.NET Core gRPC integration is not mature enough and doesn't support some common deployment scenarios yet. So, deferring this to the next versions ([see this comment](https://github.com/abpframework/abp/issues/2882#issuecomment-633080242) for more). However, it is pretty standard if you want to use gRPC in your applications. ABP Framework has no issue with gRPC. Just check the [sample application](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo).
### Others
* [Time zone system](https://github.com/abpframework/abp/pull/3933) to support different time zones for an application.
* Support for [virtual path deployment](https://github.com/abpframework/abp/issues/4089) on IIS.
* RTL support for the Angular UI.
See the [GitHub release notes](https://github.com/abpframework/abp/releases/tag/2.9.0) for others updates.
## What's New with the ABP Commercial 2.9
In addition to all the features coming with the ABP Framework, the ABP Commercial has additional features with this release, as always. This section covers the [ABP Commercial](https://commercial.abp.io/) highlights in the version 2.9.
### Organization Unit Management UI
We've created the UI for manage organization units, their members and roles for the ABP Commercial [Identity Module](https://commercial.abp.io/modules/Volo.Identity.Pro):
![organization-units](organization-units.png)
OU management is available for both of the MVC (Razor Pages) and the Angular user interfaces.
> See [this entry](https://support.abp.io/QA/Questions/222/Bugs--Problems-v290#answer-3cf5eba3-0bf1-2aa1-cc5e-39f5a0750329) if you're upgrading your solution from an earlier version.
### Chat Module Angular UI
We had introduced a new [chat module](https://commercial.abp.io/modules/Volo.Chat) in the previous version, which was only supporting the ASP.NET Core MVC / Razor Pages UI. Now, it has also an Angular UI option.
![abp-chat-module](abp-chat-module.png)
*A screenshot from the chat module - two users are sending messages to each other*
### Easy CRM Angular UI
Easy CRM is a sample application that is built on the ABP Commercial to provide a relatively complex application to the ABP Commercial customers. In the version 2.7, we have lunched it with MVC / Razor Pages UI. With the 2.9 version, we are releasing the Angular UI for the Easy CRM application.
![easy-crm](easy-crm.png)
*A screenshot from the "Order Details" page of the Easy CRM application.*
See the [Easy CRM document](https://docs.abp.io/en/commercial/latest/samples/easy-crm) to learn how to download and run it.
### Module Code Generation for the ABP Suite
[ABP Suite](https://commercial.abp.io/tools/suite) is a tool that's main feature is to [generate code](https://docs.abp.io/en/commercial/latest/abp-suite/generating-crud-page) for complete CRUD functionality for an entity, from database to the UI layer.
![suite](suite.png)
*A screenshot from the ABP Suite: Define the properties of a new entity and let it to create the application code for you!*
It was working only for [the application template](https://docs.abp.io/en/commercial/latest/startup-templates/application/index) until this release. Now, it supports to generate code for the [module projects](https://docs.abp.io/en/commercial/latest/startup-templates/module/index) too. That's a great way to create reusable application modules by taking the power of the code generation.
In addition to this main feature, we added many minor enhancements on the ABP Suite in this release.
> Notice: Generating code for the module template is currently in beta. Please inform us if you find any bug.
### Lepton Theme
[Lepton Theme](https://commercial.abp.io/themes) is the commercial theme we've developed for the ABP Commercial;
* It is 100% bootstrap compatible - so you don't write theme specific HTML!
* Provides different kind of styles - you see the material style in the picture below.
* Provides different kind of layouts (side/top menu, fluid/boxed layout...).
* It is lightweight, responsive and modern.
* And... it is upgradeable with no cost! You just update a NuGet/NPM package to get the new features.
We've create its own web site: [http://leptontheme.com/](http://leptontheme.com/)
You can view all the components together, independent from an application:
![lepton-theme](lepton-theme.png)
This web site is currently in a very early stage. We will be documenting and improving this web site to be a reference for your development and explore the features of the theme.
### Coming Soon: The File management Module
Based on the new blob storing system (introduced above), we've started to build a file management module that is used to manage (navigate/upload/download) a hierarchical file system on your application and share the files between your users and with your customers.
We plan to release the initial version with the ABP Commercial v3.0 and continue to improve it with the subsequent releases.
## About the Next Version: 3.0
We have added many new features with the [v2.8](https://blog.abp.io/abp/ABP-v2.8.0-Releases-%26-Road-Map) and v2.9. In the next version, we will completely focus on the **documentation, performance improvements** and and other enhancements as well as bug fixes.
For a long time, we were releasing a new feature version in every 2 weeks. We will continue to this approach after v3.0. But, as an exception to the v3.0, the development cycle will be ~4 weeks. **The planned release date for the v3.0 is the July 1, 2020**.
## Bonus: Articles!
Beside developing our products, our team are constantly writing articles/tutorials on various topics. You may want to check the latest articles:
* [ASP.NET Core 3.1 WebHook Implementation Using Pub/Sub](https://volosoft.com/blog/ASP.NET-CORE-3.1-Webhook-Implementation-Using-Pub-Sub)
* [Using Azure Key Vault with ASP.NET Core](https://volosoft.com/blog/Using-Azure-Key-Vault-with-ASP.NET-Core)

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/abp-chat-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/easy-crm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/lepton-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/organization-units.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/signalr-tiered-demo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/suite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/en/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

1
docs/en/CLI.md

@ -87,6 +87,7 @@ abp new Acme.BookStore
* `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).
* **`console`**: [Console template](Startup-Templates/Console.md).
* `--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.
* `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D\localTemplate` or `https://<your url>.zip`). * `--template-source` or `-ts`: Specifies a custom template source to use to build the project. Local and network sources can be used(Like `D\localTemplate` or `https://<your url>.zip`).

19
docs/en/Contribution/Index.md

@ -41,19 +41,22 @@ A new language is published after these minimum translations have been completed
ABP framework has a flexible [localization system](../Localization.md). You can create localized user interfaces for your own application. ABP framework has a flexible [localization system](../Localization.md). You can create localized user interfaces for your own application.
In addition to that, the framework and pre-build modules have already localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). In addition to that, the framework and the [pre-build modules](https://docs.abp.io/en/abp/latest/Modules/Index) have localized texts. As an example, see [the localization texts for the Volo.Abp.UI package](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json).
You can create a new file in the [same folder](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi) to translate it. #### Using the "abp translate" command
This is the recommended approach, since it automatically finds all missing texts for a specific culture and lets you to translate in one place.
* Clone the [ABP repository](https://github.com/abpframework/abp/) from Github. * Clone the [ABP repository](https://github.com/abpframework/abp/) from Github.
* Create a new file for the target language for a localization text (json) file (near to the en.json file). * Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before.
* Copy all texts from the en.json file. * Run `abp translate -c <culture-name>` command for your language in the root folder of the abp repository. For example, use `abp translate -c fr` for French. Check [this document](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes) to find the culture code for your language.
* Translate the texts. * This command creates a file in the same folder, named `abp-translation.json`. Open this file in your favorite editor and fill the missing text values.
* Send pull request on Github. * Once you done the translation, use `abp translate -a` command to apply changes to the related files.
* Send a pull request on GitHub.
You can also use the `abp translate` command of [ABP CLI](CLI.md) to translate localized texts. #### Manual Translation
ABP is a modular framework. So there are many localization text resource, one per module. To find all .json files, you can search for "en.json" after cloning the repository. You can also check [this list](Localization-Text-Files.md) for a list of localization text files. If you want to make a change on a specific resource file, you can find the file yourself, make the necessary change (or create a new file for your language) and send a pull request on GitHub.
### Blog Posts & Tutorials ### Blog Posts & Tutorials

166
docs/en/CurrentUser.md

@ -1,3 +1,167 @@
# Current User # Current User
TODO! It is common to obtain information about the current user in an application. Current user is generally the user related to the current request in a web application.
## ICurrentUser
`ICurrentUser` is the main service to get info about the current user.
Example: [Injecting](Dependency-Injection.md) the `ICurrentUser` into a service:
````csharp
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Users;
namespace AbpDemo
{
public class MyService : ITransientDependency
{
private readonly ICurrentUser _currentUser;
public MyService(ICurrentUser currentUser)
{
_currentUser = currentUser;
}
public void Foo()
{
Guid? userId = _currentUser.Id;
}
}
}
````
Common base classes have already injected this service as a base property. For example, you can directly use the `CurrentUser` property in an [application service](Application-Services.md):
````csharp
using System;
using Volo.Abp.Application.Services;
namespace AbpDemo
{
public class MyAppService : ApplicationService
{
public void Foo()
{
Guid? userId = CurrentUser.Id;
}
}
}
````
### Properties
Here, the fundamental properties of the `ICurrentUser` interface:
* **IsAuthenticated** (bool): Return `true` if current user has logged in (authenticated). `Id` and `UserName` returns `null` if the user has not logged in.
* **Id** (Guid?): Id of the current user or `null` if the current user has not logged in.
* **UserName** (string): User name of the current user or `null` if the current user has not logged in.
* **TenantId** (Guid?): Tenant Id of the current user, which can be useful for a [multi-tenant](Multi-Tenancy.md) application.
* **Email** (string): Email address of the current user or `null` if the current user has not logged in or not set an email address.
* **EmailVerified** (bool): True, if the phone number of the current user has been verified.
* **PhoneNumber** (string): Phone number of the current user or `null` if the current user has not logged in or not set a phone number.
* **PhoneNumberVerified** (bool): True, if the phone number of the current user has been verified.
* **Roles** (string[]): A string array of the role names of the current user.
### Methods
`ICurrentUser` is implemented on the `ICurrentPrincipalAccessor` (see the section below) and works with the claims. So, all of the above properties are actually retrieved from claims of the currently authenticated user.
`ICurrentUser` has some methods to directly work with the claims if you have custom claims or get other non-common claim types.
* **FindClaim**: Gets a claim with the given name or returns `null` if not found.
* **FindClaims**: Gets all the claims with the given name (it is allowed to have multiple claim values with the same name).
* **GetAllClaims**: Gets all the claims.
* **IsInRole**: A shortcut method to check if the current user has a role.
Beside these standard methods, there are extension methods as shortcuts:
* **FindClaimValue**: Gets the value of the claim with the given name, or `null` if not found. It has a generic overload that also casts the value to a specific type.
* **GetId**: Returns `Id` of the current user but throws an exception (instead of returning `null`) if the current user has not logged in. Use this only if you are sure that the user has already authenticated in your code context.
### Authentication & Authorization
`ICurrentUser` works independent of how user is authenticated or authorized. It silently works with any authentication system that works with the current principal (see the section below).
## ICurrentPrincipalAccessor
`ICurrentPrincipalAccessor` is the service that should be used (by the ABP Framework and your application code) whenever the current principle of the current user is needed.
For a web application, it gets the `User` property of the current `HttpContext`. For a non-web application, it returns the `Thread.CurrentPrincipal`.
> You generally don't need to this low level `ICurrentPrincipalAccessor` service and directly work with the `ICurrentUser` explained above.
### Basic Usage
You can inject `ICurrentPrincipalAccessor` and use the `Principal` property to the the current principal:
````csharp
public class MyService : ITransientDependency
{
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public MyService(ICurrentPrincipalAccessor currentPrincipalAccessor)
{
_currentPrincipalAccessor = currentPrincipalAccessor;
}
public void Foo()
{
var allClaims = _currentPrincipalAccessor.Principal.Claims.ToList();
//...
}
}
````
### Changing the Current Principle
Current principle is not something you want to set/change, except some advanced scenarios. If you need it, use the `Change` method of the `ICurrentPrincipalAccessor`. It takes a `ClaimsPrinciple` object and makes it "current" for a scope.
Example:
````csharp
public class MyAppService : ApplicationService
{
private readonly ICurrentPrincipalAccessor _currentPrincipalAccessor;
public MyAppService(ICurrentPrincipalAccessor currentPrincipalAccessor)
{
_currentPrincipalAccessor = currentPrincipalAccessor;
}
public void Foo()
{
var newPrinciple = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[]
{
new Claim(AbpClaimTypes.UserId, Guid.NewGuid().ToString()),
new Claim(AbpClaimTypes.UserName, "john"),
new Claim("MyCustomCliam", "42")
}
)
);
using (_currentPrincipalAccessor.Change(newPrinciple))
{
var userName = CurrentUser.UserName; //returns "john"
//...
}
}
}
````
Use the `Change` method always in a `using` statement, so it is restored to the previous value after the `using` scope ends.
This can be a way to simulate a user login for a scope of the application code, however use it by care.
## AbpClaimTypes
`AbpClaimTypes` is a static class that defines the names of the standard claims and used by the ABP Framework.
* Default values for the `UserName`, `UserId`, `Role` and `Email` properties are set from the [System.Security.Claims.ClaimTypes](https://docs.microsoft.com/en-us/dotnet/api/system.security.claims.claimtypes) class, but you can change them.
* Other properties, like `EmailVerified`, `PhoneNumber`, `TenantId`... are defined by the ABP Framework by following the standard names wherever possible.
It is suggested to use properties of this class instead of magic strings for claim names.

11
docs/en/Entity-Framework-Core-Oracle.md

@ -22,19 +22,19 @@ Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check
In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block
``` ```csharp
var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>() var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default")); .UseSqlServer(configuration.GetConnectionString("Default"));
``` ```
with this one with this one
``` ```csharp
var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>) var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>)
new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle
( (
configuration.GetConnectionString("Default") configuration.GetConnectionString("Default")
); );
``` ```
> Depending on your solution structure, you may find more code files need to be changed. > Depending on your solution structure, you may find more code files need to be changed.
@ -44,11 +44,6 @@ Oracle connection strings are different than SQL Server connection strings. So,
You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure. You typically will change the `appsettings.json` inside the `.DbMigrator` and `.Web` projects, but it depends on your solution structure.
A sample connection string for Oracle:
```
Data Source=localhost;User Id=myuser;Password=mypassword;
```
## Re-Generate the Migrations ## Re-Generate the Migrations
The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) by default. The startup template uses [Entity Framework Core's Code First Migrations](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) by default.

5
docs/en/How-To/Customize-Login-Page-MVC.md

@ -26,7 +26,7 @@ Then you can override any method you need and add new methods and properties nee
## Overriding the Login Page UI ## Overriding the Login Page UI
Create folder named **Account** under **Pages** directory and create a **Login.cshtml** under this folder. It will automatically override the `Login.cshtml` file defined in the Account Module thanks to the [Virtual File System](../Virtual-File-System.md). Create folder named **Account** under **Pages** directory and create a **Login.cshtml** under this folder. It will automatically override the `Login.cshtml` file defined in the Account Module.
A good way to customize a page is to copy its source code. [Click here](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml) for the source code of the login page. At the time this document has been written, the source code was like below: A good way to customize a page is to copy its source code. [Click here](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml) for the source code of the login page. At the time this document has been written, the source code was like below:
@ -35,7 +35,6 @@ A good way to customize a page is to copy its source code. [Click here](https://
@using Volo.Abp.Account.Settings @using Volo.Abp.Account.Settings
@using Volo.Abp.Settings @using Volo.Abp.Settings
@model Acme.BookStore.Web.Pages.Account.CustomLoginModel @model Acme.BookStore.Web.Pages.Account.CustomLoginModel
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage
@inject Volo.Abp.Settings.ISettingProvider SettingProvider @inject Volo.Abp.Settings.ISettingProvider SettingProvider
@if (Model.EnableLocalLogin) @if (Model.EnableLocalLogin)
{ {
@ -110,4 +109,4 @@ You can find the source code of the completed example [here](https://github.com/
## See Also ## See Also
* [ASP.NET Core (MVC / Razor Pages) User Interface Customization Guide](../UI/AspNetCore/Customization-User-Interface.md). * [ASP.NET Core (MVC / Razor Pages) User Interface Customization Guide](../UI/AspNetCore/Customization-User-Interface.md).

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?UI=MVC&DB=EF&Tiered=No) * [ASP.NET Core MVC (Razor Pages) UI Startup Template](Getting-Started.md?UI=MVC&DB=EF&Tiered=No)
* [Angular UI Startup Template](Getting-Started?UI=NG&DB=EF&Tiered=No) * [Angular UI Startup Template](Getting-Started.md?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/Object-Extensions.md

@ -2,7 +2,7 @@
ABP Framework provides an **object extension system** to allow you to **add extra properties** to an existing object **without modifying** the related class. This allows to extend functionalities implemented by a depended [application module](Modules/Index.md), especially when you want to [extend entities](Customizing-Application-Modules-Extending-Entities.md) and [DTOs](Customizing-Application-Modules-Overriding-Services.md) defined by the module. ABP Framework provides an **object extension system** to allow you to **add extra properties** to an existing object **without modifying** the related class. This allows to extend functionalities implemented by a depended [application module](Modules/Index.md), especially when you want to [extend entities](Customizing-Application-Modules-Extending-Entities.md) and [DTOs](Customizing-Application-Modules-Overriding-Services.md) defined by the module.
> Object extension system is not normally not needed for your own objects since you can easily add regular properties to your own classes. > Object extension system normally is not needed for your own objects since you can easily add regular properties to your own classes.
## IHasExtraProperties Interface ## IHasExtraProperties Interface
@ -409,4 +409,4 @@ ObjectExtensionManager.Instance
); );
```` ````
See the [Entity Framework Core Integration document](Entity-Framework-Core.md) for more. See the [Entity Framework Core Integration document](Entity-Framework-Core.md) for more.

23
docs/en/Road-Map.md

@ -4,8 +4,27 @@ You can always check the milestone planning and the prioritized backlog issues o
While we will **continue to add other exciting features**, we will work on the following major items in the **middle term**: 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. * **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. * **.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. Beside this middle term goals, there are many features in the [backlog](https://github.com/abpframework/abp/milestone/2). Here, a list of some major items in the backlog;
* [#4098](https://github.com/abpframework/abp/issues/4098) / Blob Storing Azure provider.
* [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a **gRPC integration** infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](https://docs.abp.io/en/abp/latest/Modules/Index))
* [#236](https://github.com/abpframework/abp/issues/236) Resource based authorization system
* [#1754](https://github.com/abpframework/abp/issues/1754) / Multi-lingual entities
* [#347](https://github.com/abpframework/abp/issues/347) / Support MongoDB ACID transactions
* [#633](https://github.com/abpframework/abp/issues/633) / Realtime notification system
* [#57](https://github.com/abpframework/abp/issues/57) / Built-in CQRS infrastructure
* [#4222](https://github.com/abpframework/abp/issues/4222) / Kafka integration for the Distributed Event Bus
* [#336](https://github.com/abpframework/abp/issues/336) / Health Check abstraction
* [#2532](https://github.com/abpframework/abp/issues/2532), [#2564](https://github.com/abpframework/abp/issues/2465) / CosmosDB integration with EF Core and MongoDB API
* [#1168](https://github.com/abpframework/abp/issues/1168) / Official Vue UI startup template
* [#1638](https://github.com/abpframework/abp/issues/1638) Official React startup template
* [#4223](https://github.com/abpframework/abp/issues/4223) / WebHook system
* [#162](https://github.com/abpframework/abp/issues/162) / Azure ElasticDB Integration for multitenancy
* [#2296](https://github.com/abpframework/abp/issues/2296) / Feature toggling infrastructure
The backlog items are subject to change. We are adding new items and changing priorities based on the community feedbacks and goals of the project.
Vote for your favorite feature on the related GitHub issues (and write your thoughts). You can create an issue on [the GitHub repository](https://github.com/abpframework/abp) for your feature requests, but first search in in the existing issues.

21
docs/en/Startup-Templates/Console.md

@ -0,0 +1,21 @@
# Console Application Startup Template
This template is used to create a minimalist console application project.
## How to Start With?
First, install the [ABP CLI](../CLI.md) if you haven't installed before:
````bash
dotnet tool install -g Volo.Abp.Cli
````
Then use the `abp new` command in an empty folder to create a new solution:
````bash
abp new Acme.MyConsoleApp -t console
````
`Acme.MyConsoleApp` is the solution name, like *YourCompany.YourProduct*. You can use single level, two-levels or three-levels naming.
###

4
docs/en/Startup-Templates/Index.md

@ -4,6 +4,4 @@ While you can start with an empty project and add needed packages manually, star
* [**app**](Application.md): Application template. * [**app**](Application.md): Application template.
* [**module**](Module.md): Module/service template. * [**module**](Module.md): Module/service template.
* [**console**](Console.md): Console template.

21
docs/en/Tutorials/Part-1.md

@ -14,7 +14,7 @@ else if UI == "NG"
DB="mongodb" DB="mongodb"
DB_Text="MongoDB" DB_Text="MongoDB"
UI_Text="angular" UI_Text="angular"
else else
DB ="?" DB ="?"
UI_Text="?" UI_Text="?"
end end
@ -26,7 +26,7 @@ In this tutorial series, you will build an ABP application named `Acme.BookStore
The ASP.NET Core {{UI_Value}} tutorial series consists of 3 parts: The ASP.NET Core {{UI_Value}} tutorial series consists of 3 parts:
- **Part-1: Creating the project and book list page (this tutorial)** - **Part-1: Creating the project and book list page (this tutorial)**
- [Part-2: Creating, updating and deleting books](part-2.md) - [Part-2: Creating, updating and deleting books](part-2.md)
- [Part-3: Integration tests](part-3.md) - [Part-3: Integration tests](part-3.md)
@ -106,7 +106,7 @@ This is how the layered solution structure looks like:
![bookstore-visual-studio-solution](./images/bookstore-solution-structure-{{UI_Text}}.png) ![bookstore-visual-studio-solution](./images/bookstore-solution-structure-{{UI_Text}}.png)
Check out the [solution structure](../startup-templates/application#solution-structure) section to understand the structure in details. Check out the [solution structure](../startup-templates/application#solution-structure) section to understand the structure in details.
### Create the book entity ### Create the book entity
@ -444,7 +444,7 @@ using Volo.Abp.Application.Services;
namespace Acme.BookStore namespace Acme.BookStore
{ {
public interface IBookAppService : public interface IBookAppService :
ICrudAppService< //Defines CRUD methods ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books BookDto, //Used to show books
Guid, //Primary key of the book entity Guid, //Primary key of the book entity
@ -473,12 +473,12 @@ using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore namespace Acme.BookStore
{ {
public class BookAppService : public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto, CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>, CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService IBookAppService
{ {
public BookAppService(IRepository<Book, Guid> repository) public BookAppService(IRepository<Book, Guid> repository)
: base(repository) : base(repository)
{ {
@ -564,17 +564,15 @@ Open the `Index.cshtml` and change the whole content as shown below:
````html ````html
@page @page
@using Acme.BookStore.Web.Pages.Books @using Acme.BookStore.Web.Pages.Books
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model IndexModel @model IndexModel
<h2>Books</h2> <h2>Books</h2>
```` ````
* This code changes the default inheritance of the Razor View Page Model so it **inherits** from the `BookStorePage` class (instead of `PageModel`). The `BookStorePage` class which comes with the startup template, provides some shared properties/methods used by all pages.
* Set the `IndexModel`'s namespace to `Acme.BookStore.Pages.Books` in `Index.cshtml.cs`. * Set the `IndexModel`'s namespace to `Acme.BookStore.Pages.Books` in `Index.cshtml.cs`.
**Index.cshtml.cs:** **Index.cshtml.cs:**
@ -602,7 +600,7 @@ Open the `BookStoreMenuContributor` class in the `Menus` folder and add the foll
namespace Acme.BookStore.Web.Menus namespace Acme.BookStore.Web.Menus
{ {
public class BookStoreMenuContributor : IMenuContributor public class BookStoreMenuContributor : IMenuContributor
{ {
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{ {
//<-- added the below code //<-- added the below code
@ -681,7 +679,6 @@ Change the `Pages/Books/Index.cshtml` as following:
````html ````html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model Acme.BookStore.Web.Pages.Books.IndexModel @model Acme.BookStore.Web.Pages.Books.IndexModel
@section scripts @section scripts
{ {
@ -749,7 +746,7 @@ It's end of this part. The final UI of this work is shown as below:
{{end}} {{end}}
{{if UI == "NG"}} {{if UI == "NG"}}
### Angular development ### Angular development
#### Create the books page #### Create the books page

22
docs/en/Tutorials/Part-2.md

@ -15,7 +15,7 @@ else if UI == "NG"
DB="mongodb" DB="mongodb"
DB_Text="MongoDB" DB_Text="MongoDB"
UI_Text="angular" UI_Text="angular"
else else
DB ="?" DB ="?"
UI_Text="?" UI_Text="?"
end end
@ -86,7 +86,6 @@ Open the `CreateModal.cshtml` file and paste the code below:
````html ````html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model Acme.BookStore.Web.Pages.Books.CreateModalModel @model Acme.BookStore.Web.Pages.Books.CreateModalModel
@{ @{
@ -203,7 +202,7 @@ namespace Acme.BookStore.Web.Pages.Books
* In the `GetAsync` method, we get `BookDto `from `BookAppService` and this is being mapped to the DTO object `CreateUpdateBookDto`. * In the `GetAsync` method, we get `BookDto `from `BookAppService` and this is being mapped to the DTO object `CreateUpdateBookDto`.
* The `OnPostAsync` uses `BookAppService.UpdateAsync()` to update the entity. * The `OnPostAsync` uses `BookAppService.UpdateAsync()` to update the entity.
#### Mapping from BookDto to CreateUpdateBookDto #### Mapping from BookDto to CreateUpdateBookDto
To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebAutoMapperProfile.cs` in the `Acme.BookStore.Web` project and change it as shown below: To be able to map the `BookDto` to `CreateUpdateBookDto`, configure a new mapping. To do this, open the `BookStoreWebAutoMapperProfile.cs` in the `Acme.BookStore.Web` project and change it as shown below:
@ -230,7 +229,6 @@ Replace `EditModal.cshtml` content with the following content:
````html ````html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@using Acme.BookStore.Web.Pages.Books @using Acme.BookStore.Web.Pages.Books
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EditModalModel @model EditModalModel
@ -256,7 +254,7 @@ This page is very similar to the `CreateModal.cshtml`, except:
#### Add "Actions" dropdown to the table #### Add "Actions" dropdown to the table
We will add a dropdown button to the table named *Actions*. We will add a dropdown button to the table named *Actions*.
Open the `Pages/Books/Index.cshtml` page and change the `<abp-table>` section as shown below: Open the `Pages/Books/Index.cshtml` page and change the `<abp-table>` section as shown below:
@ -525,7 +523,7 @@ export class BookState {
* We imported `CreateUpdateBook` action and defined the `save` method that will listen to a `CreateUpdateBook` action to create a book. * We imported `CreateUpdateBook` action and defined the `save` method that will listen to a `CreateUpdateBook` action to create a book.
When the `SaveBook` action dispatched, the save method is being executed. It calls `createByInput` method of the `BookService`. When the `SaveBook` action dispatched, the save method is being executed. It calls `createByInput` method of the `BookService`.
#### Add a modal to BookListComponent #### Add a modal to BookListComponent
@ -601,7 +599,7 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the
</abp-modal> </abp-modal>
``` ```
* We added the `abp-modal` which renders a modal to allow user to create a new book. * We added the `abp-modal` which renders a modal to allow user to create a new book.
* `abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits. * `abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits.
* We added `New book` button to the `AbpContentToolbar`. * We added `New book` button to the `AbpContentToolbar`.
@ -864,7 +862,7 @@ export class BookListComponent implements OnInit {
} }
``` ```
* We imported ` NgbDateNativeAdapter, NgbDateAdapter` * We imported ` NgbDateNativeAdapter, NgbDateAdapter`
* We added a new provider `NgbDateAdapter` that converts Datepicker value to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details. * We added a new provider `NgbDateAdapter` that converts Datepicker value to `Date` type. See the [datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview) for more details.
@ -971,7 +969,7 @@ Open `book-list.component.html` in `app\book\book-list` folder and add the follo
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpAccount::Close' | abpLocalization }}}%} {%{{{ 'AbpAccount::Close' | abpLocalization }}}%}
</button> </button>
<!--added save button--> <!--added save button-->
<button class="btn btn-primary" (click)="save()" [disabled]="form.invalid"> <button class="btn btn-primary" (click)="save()" [disabled]="form.invalid">
<i class="fa fa-check mr-1"></i> <i class="fa fa-check mr-1"></i>
@ -986,7 +984,7 @@ Find the `<form [formGroup]="form">` tag and replace below content:
<form [formGroup]="form" (ngSubmit)="save()"> <!-- added the ngSubmit --> <form [formGroup]="form" (ngSubmit)="save()"> <!-- added the ngSubmit -->
``` ```
* We added the `(ngSubmit)="save()"` to `<form>` element to save a new book by pressing the enter. * We added the `(ngSubmit)="save()"` to `<form>` element to save a new book by pressing the enter.
* We added `abp-button` to the bottom area of the modal to save a new book. * We added `abp-button` to the bottom area of the modal to save a new book.
@ -1131,7 +1129,7 @@ export class BookListComponent implements OnInit {
* We imported `BookService`. * We imported `BookService`.
* We declared a variable named `selectedBook` as `BookDto`. * We declared a variable named `selectedBook` as `BookDto`.
* We injected `BookService` to the constructor. `BookService` is being used to retrieve the book data which is being edited. * We injected `BookService` to the constructor. `BookService` is being used to retrieve the book data which is being edited.
* We added `editBook` method. This method fetches the book with the given `Id` and sets it to `selectedBook` object. * We added `editBook` method. This method fetches the book with the given `Id` and sets it to `selectedBook` object.
* We replaced the `buildForm` method so that it creates the form with the `selectedBook` data. * We replaced the `buildForm` method so that it creates the form with the `selectedBook` data.
* We replaced the `createBook` method so it sets `selectedBook` to an empty object. * We replaced the `createBook` method so it sets `selectedBook` to an empty object.
* We added `selectedBook.id` to the constructor of the new `CreateUpdateBook`. * We added `selectedBook.id` to the constructor of the new `CreateUpdateBook`.
@ -1291,7 +1289,7 @@ import { ConfirmationService } from '@abp/ng.theme.shared';
//... //...
constructor( constructor(
private store: Store, private store: Store,
private fb: FormBuilder, private fb: FormBuilder,
private bookService: BookService, private bookService: BookService,
private confirmation: ConfirmationService // <== added this line ==> private confirmation: ConfirmationService // <== added this line ==>

4
docs/en/UI/Angular/Component-Replacement.md

@ -543,6 +543,10 @@ The final UI looks like below:
![New nav-items](./images/replaced-nav-items-component.png) ![New nav-items](./images/replaced-nav-items-component.png)
## See Also
- [How to Replace PermissionManagementComponent](./Permission-Management-Component-Replacement.md)
## What's Next? ## What's Next?
- [Custom Setting Page](./Custom-Setting-Page.md) - [Custom Setting Page](./Custom-Setting-Page.md)

500
docs/en/UI/Angular/Permission-Management-Component-Replacement.md

@ -0,0 +1,500 @@
# How to Replace PermissionManagementComponent
![Permission management modal](./images/permission-management-modal.png)
Run the following command in `angular` folder to create a new component called `PermissionManagementComponent`.
```bash
yarn ng generate component permission-management --entryComponent --inlineStyle
# You don't need the --entryComponent option in Angular 9
```
Open the generated `permission-management.component.ts` in `src/app/permission-management` folder and replace the content with the following:
```js
import {
Component,
EventEmitter,
Input,
Output,
Renderer2,
TrackByFunction,
Inject,
Optional,
} from '@angular/core';
import { ReplaceableComponents } from '@abp/ng.core';
import { Select, Store } from '@ngxs/store';
import { Observable } from 'rxjs';
import { finalize, map, pluck, take, tap } from 'rxjs/operators';
import {
GetPermissions,
UpdatePermissions,
PermissionManagement,
PermissionManagementState,
} from '@abp/ng.permission-management';
type PermissionWithMargin = PermissionManagement.Permission & {
margin: number;
};
@Component({
selector: 'app-permission-management',
templateUrl: './permission-management.component.html',
styles: [
`
.overflow-scroll {
max-height: 70vh;
overflow-y: scroll;
}
`,
],
})
export class PermissionManagementComponent
implements
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs {
protected _providerName: string;
@Input()
get providerName(): string {
if (this.replaceableData) return this.replaceableData.inputs.providerName;
return this._providerName;
}
set providerName(value: string) {
this._providerName = value;
}
protected _providerKey: string;
@Input()
get providerKey(): string {
if (this.replaceableData) return this.replaceableData.inputs.providerKey;
return this._providerKey;
}
set providerKey(value: string) {
this._providerKey = value;
}
protected _hideBadges = false;
@Input()
get hideBadges(): boolean {
if (this.replaceableData) return this.replaceableData.inputs.hideBadges;
return this._hideBadges;
}
set hideBadges(value: boolean) {
this._hideBadges = value;
}
protected _visible = false;
@Input()
get visible(): boolean {
return this._visible;
}
set visible(value: boolean) {
if (value === this._visible) return;
if (value) {
this.openModal().subscribe(() => {
this._visible = true;
this.visibleChange.emit(true);
if (this.replaceableData) this.replaceableData.outputs.visibleChange(true);
});
} else {
this.selectedGroup = null;
this._visible = false;
this.visibleChange.emit(false);
if (this.replaceableData) this.replaceableData.outputs.visibleChange(false);
}
}
@Output() readonly visibleChange = new EventEmitter<boolean>();
@Select(PermissionManagementState.getPermissionGroups)
groups$: Observable<PermissionManagement.Group[]>;
@Select(PermissionManagementState.getEntityDisplayName)
entityName$: Observable<string>;
selectedGroup: PermissionManagement.Group;
permissions: PermissionManagement.Permission[] = [];
selectThisTab = false;
selectAllTab = false;
modalBusy = false;
trackByFn: TrackByFunction<PermissionManagement.Group> = (_, item) => item.name;
get selectedGroupPermissions$(): Observable<PermissionWithMargin[]> {
return this.groups$.pipe(
map((groups) =>
this.selectedGroup
? groups.find((group) => group.name === this.selectedGroup.name).permissions
: []
),
map<PermissionManagement.Permission[], PermissionWithMargin[]>((permissions) =>
permissions.map(
(permission) =>
(({
...permission,
margin: findMargin(permissions, permission),
isGranted: this.permissions.find((per) => per.name === permission.name).isGranted,
} as any) as PermissionWithMargin)
)
)
);
}
get isVisible(): boolean {
if (!this.replaceableData) return this.visible;
return this.replaceableData.inputs.visible;
}
constructor(
@Optional()
@Inject('REPLACEABLE_DATA')
public replaceableData: ReplaceableComponents.ReplaceableTemplateData<
PermissionManagement.PermissionManagementComponentInputs,
PermissionManagement.PermissionManagementComponentOutputs
>,
private store: Store
) {}
getChecked(name: string) {
return (this.permissions.find((per) => per.name === name) || { isGranted: false }).isGranted;
}
isGrantedByOtherProviderName(grantedProviders: PermissionManagement.GrantedProvider[]): boolean {
if (grantedProviders.length) {
return grantedProviders.findIndex((p) => p.providerName !== this.providerName) > -1;
}
return false;
}
onClickCheckbox(clickedPermission: PermissionManagement.Permission, value) {
if (
clickedPermission.isGranted &&
this.isGrantedByOtherProviderName(clickedPermission.grantedProviders)
)
return;
setTimeout(() => {
this.permissions = this.permissions.map((per) => {
if (clickedPermission.name === per.name) {
return { ...per, isGranted: !per.isGranted };
} else if (clickedPermission.name === per.parentName && clickedPermission.isGranted) {
return { ...per, isGranted: false };
} else if (clickedPermission.parentName === per.name && !clickedPermission.isGranted) {
return { ...per, isGranted: true };
}
return per;
});
this.setTabCheckboxState();
this.setGrantCheckboxState();
}, 0);
}
setTabCheckboxState() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
const selectedPermissions = permissions.filter((per) => per.isGranted);
const element = document.querySelector('#select-all-in-this-tabs') as any;
if (selectedPermissions.length === permissions.length) {
element.indeterminate = false;
this.selectThisTab = true;
} else if (selectedPermissions.length === 0) {
element.indeterminate = false;
this.selectThisTab = false;
} else {
element.indeterminate = true;
}
});
}
setGrantCheckboxState() {
const selectedAllPermissions = this.permissions.filter((per) => per.isGranted);
const checkboxElement = document.querySelector('#select-all-in-all-tabs') as any;
if (selectedAllPermissions.length === this.permissions.length) {
checkboxElement.indeterminate = false;
this.selectAllTab = true;
} else if (selectedAllPermissions.length === 0) {
checkboxElement.indeterminate = false;
this.selectAllTab = false;
} else {
checkboxElement.indeterminate = true;
}
}
onClickSelectThisTab() {
this.selectedGroupPermissions$.pipe(take(1)).subscribe((permissions) => {
permissions.forEach((permission) => {
if (permission.isGranted && this.isGrantedByOtherProviderName(permission.grantedProviders))
return;
const index = this.permissions.findIndex((per) => per.name === permission.name);
this.permissions = [
...this.permissions.slice(0, index),
{ ...this.permissions[index], isGranted: !this.selectThisTab },
...this.permissions.slice(index + 1),
];
});
});
this.setGrantCheckboxState();
}
onClickSelectAll() {
this.permissions = this.permissions.map((permission) => ({
...permission,
isGranted:
this.isGrantedByOtherProviderName(permission.grantedProviders) || !this.selectAllTab,
}));
this.selectThisTab = !this.selectAllTab;
}
onChangeGroup(group: PermissionManagement.Group) {
this.selectedGroup = group;
this.setTabCheckboxState();
}
submit() {
this.modalBusy = true;
const unchangedPermissions = getPermissions(
this.store.selectSnapshot(PermissionManagementState.getPermissionGroups)
);
const changedPermissions: PermissionManagement.MinimumPermission[] = this.permissions
.filter((per) =>
unchangedPermissions.find((unchanged) => unchanged.name === per.name).isGranted ===
per.isGranted
? false
: true
)
.map(({ name, isGranted }) => ({ name, isGranted }));
if (changedPermissions.length) {
this.store
.dispatch(
new UpdatePermissions({
providerKey: this.providerKey,
providerName: this.providerName,
permissions: changedPermissions,
})
)
.pipe(finalize(() => (this.modalBusy = false)))
.subscribe(() => {
this.visible = false;
});
} else {
this.modalBusy = false;
this.visible = false;
}
}
openModal() {
if (!this.providerKey || !this.providerName) {
throw new Error('Provider Key and Provider Name are required.');
}
return this.store
.dispatch(
new GetPermissions({
providerKey: this.providerKey,
providerName: this.providerName,
})
)
.pipe(
pluck('PermissionManagementState', 'permissionRes'),
tap((permissionRes: PermissionManagement.Response) => {
this.selectedGroup = permissionRes.groups[0];
this.permissions = getPermissions(permissionRes.groups);
})
);
}
initModal() {
this.setTabCheckboxState();
this.setGrantCheckboxState();
}
onVisibleChange(visible: boolean) {
this.visible = visible;
if (this.replaceableData) {
this.replaceableData.inputs.visible = visible;
this.replaceableData.outputs.visibleChange(visible);
}
}
}
function findMargin(
permissions: PermissionManagement.Permission[],
permission: PermissionManagement.Permission
) {
const parentPermission = permissions.find((per) => per.name === permission.parentName);
if (parentPermission && parentPermission.parentName) {
let margin = 20;
return (margin += findMargin(permissions, parentPermission));
}
return parentPermission ? 20 : 0;
}
function getPermissions(groups: PermissionManagement.Group[]): PermissionManagement.Permission[] {
return groups.reduce((acc, val) => [...acc, ...val.permissions], []);
}
```
Open the generated `permission-management.component.html` in `src/app/permission-management` folder and replace the content with the below:
```html
<abp-modal
[visible]="isVisible"
(visibleChange)="onVisibleChange($event)"
(init)="initModal()"
[busy]="modalBusy"
>
<ng-container *ngIf="{ entityName: entityName$ | async } as data">
<ng-template #abpHeader>
<h4>
{%{{{ 'AbpPermissionManagement::Permissions' | abpLocalization }}}%} - {%{{{ data.entityName }}}%}
</h4>
</ng-template>
<ng-template #abpBody>
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="select-all-in-all-tabs"
name="select-all-in-all-tabs"
class="custom-control-input"
[(ngModel)]="selectAllTab"
(click)="onClickSelectAll()"
/>
<label class="custom-control-label" for="select-all-in-all-tabs">{%{{{
'AbpPermissionManagement::SelectAllInAllTabs' | abpLocalization
}}}%}</label>
</div>
<hr class="mt-2 mb-2" />
<div class="row">
<div class="overflow-scroll col-md-4">
<ul class="nav nav-pills flex-column">
<li *ngFor="let group of groups$ | async; trackBy: trackByFn" class="nav-item">
<a
class="nav-link pointer"
[class.active]="selectedGroup?.name === group?.name"
(click)="onChangeGroup(group)"
>{%{{{ group?.displayName }}}%}</a
>
</li>
</ul>
</div>
<div class="col-md-8 overflow-scroll">
<h4>{%{{{ selectedGroup?.displayName }}}%}</h4>
<hr class="mt-2 mb-3" />
<div class="pl-1 pt-1">
<div class="custom-checkbox custom-control mb-2">
<input
type="checkbox"
id="select-all-in-this-tabs"
name="select-all-in-this-tabs"
class="custom-control-input"
[(ngModel)]="selectThisTab"
(click)="onClickSelectThisTab()"
/>
<label class="custom-control-label" for="select-all-in-this-tabs">{%{{{
'AbpPermissionManagement::SelectAllInThisTab' | abpLocalization
}}}%}</label>
</div>
<hr class="mb-3" />
<div
*ngFor="
let permission of selectedGroupPermissions$ | async;
let i = index;
trackBy: trackByFn
"
[style.margin-left]="permission.margin + 'px'"
class="custom-checkbox custom-control mb-2"
>
<input
#permissionCheckbox
type="checkbox"
[checked]="getChecked(permission.name)"
[value]="getChecked(permission.name)"
[attr.id]="permission.name"
class="custom-control-input"
[disabled]="isGrantedByOtherProviderName(permission.grantedProviders)"
/>
<label
class="custom-control-label"
[attr.for]="permission.name"
(click)="onClickCheckbox(permission, permissionCheckbox.value)"
>{%{{{ permission.displayName }}}%}
<ng-container *ngIf="!hideBadges">
<span
*ngFor="let provider of permission.grantedProviders"
class="badge badge-light"
>{%{{{ provider.providerName }}}%}: {%{{{ provider.providerKey }}}%}</span
>
</ng-container>
</label>
</div>
</div>
</div>
</div>
</ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpIdentity::Cancel' | abpLocalization }}}%}
</button>
<abp-button iconClass="fa fa-check" (click)="submit()">{%{{{
'AbpIdentity::Save' | abpLocalization
}}}%}</abp-button>
</ng-template>
</ng-container>
</abp-modal>
```
Open `app.component.ts` in `src/app` folder and modify it as shown below:
```js
import { AddReplaceableComponent } from '@abp/ng.core';
import { ePermissionManagementComponents } from '@abp/ng.permission-management';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngxs/store';
import { PermissionManagementComponent } from './permission-management/permission-management.component';
//...
export class AppComponent implements OnInit {
constructor(private store: Store) {} // injected store
ngOnInit() {
// added dispatching the AddReplaceableComponent action
this.store.dispatch(
new AddReplaceableComponent({
component: PermissionManagementComponent,
key: ePermissionManagementComponents.PermissionManagement,
})
);
}
}
```
## See Also
- [Component Replacement](./Component-Replacement.md)

BIN
docs/en/UI/Angular/images/permission-management-modal.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

6
docs/en/UI/AspNetCore/Customization-User-Interface.md

@ -51,15 +51,13 @@ namespace Acme.BookStore.Web.Pages.Identity.Users
### Overriding a Razor Page (.CSHTML) ### Overriding a Razor Page (.CSHTML)
Overriding a `.cshtml` file (razor page, razor view, view component... etc.) is possible through the [Virtual File System](../../Virtual-File-System.md). Overriding a `.cshtml` file (razor page, razor view, view component... etc.) is possible through creating the same `.cshtml` file under the same path.
Virtual File system allows us to **embed resources into assemblies**. In this way, pre-built modules define the razor pages inside their NuGet packages. When you depend a module, you can override any file added to the virtual file system by that module, including pages/views.
#### Example #### Example
This example overrides the **login page** UI defined by the [Account Module](../../Modules/Account.md). This example overrides the **login page** UI defined by the [Account Module](../../Modules/Account.md).
Physical files override the embedded files defined in the same location. The account module defines a `Login.cshtml` file under the `Pages/Account` folder. So, you can override it by creating a file in the same path: The account module defines a `Login.cshtml` file under the `Pages/Account` folder. So, you can override it by creating a file in the same path:
![overriding-login-cshtml](../../images/overriding-login-cshtml.png) ![overriding-login-cshtml](../../images/overriding-login-cshtml.png)

10
docs/en/Virtual-File-System.md

@ -1,6 +1,6 @@
## Virtual File System ## Virtual File System
The Virtual File System makes it possible to manage files that do not physically exist on the file system (disk). It's mainly used to embed (js, css, image, cshtml...) files into assemblies and use them like physical files at runtime. The Virtual File System makes it possible to manage files that do not physically exist on the file system (disk). It's mainly used to embed (js, css, image..) files into assemblies and use them like physical files at runtime.
### Volo.Abp.VirtualFileSystem Package ### Volo.Abp.VirtualFileSystem Package
@ -147,7 +147,7 @@ The code above assumes that `MyWebAppModule` and `MyModule` are two different pr
The Virtual File System is well integrated to ASP.NET Core: The Virtual File System is well integrated to ASP.NET Core:
* Virtual files can be used just like physical (static) files in a web application. * Virtual files can be used just like physical (static) files in a web application.
* Razor Views, Razor Pages, js, css, image files and all other web content types can be embedded into assemblies and used just like the physical files. * Js, css, image files and all other web content types can be embedded into assemblies and used just like the physical files.
* An application (or another module) can override a virtual file of a module just like placing a file with the same name and extension into the same folder of the virtual file. * An application (or another module) can override a virtual file of a module just like placing a file with the same name and extension into the same folder of the virtual file.
#### Virtual Files Middleware #### Virtual Files Middleware
@ -161,9 +161,3 @@ app.UseVirtualFiles();
Adding virtual files middleware after the static files middleware makes it possible to override a virtual file with a real physical file simply by placing it in the same location as the virtual file. Adding virtual files middleware after the static files middleware makes it possible to override a virtual file with a real physical file simply by placing it in the same location as the virtual file.
>The Virtual File Middleware only serves the virtual wwwroot folder contents - just like the other static files. >The Virtual File Middleware only serves the virtual wwwroot folder contents - just like the other static files.
#### Views & Pages
Embedded razor views/pages are available in the application without any configuration. Simply place them into the standard Views/Pages virtual folders of the module being developed.
An embedded view/page can be overrided if a module/application locates a new file into the same location as mentioned above.

8
docs/en/docs-nav.json

@ -520,6 +520,10 @@
{ {
"text": "Module", "text": "Module",
"path": "Startup-Templates/Module.md" "path": "Startup-Templates/Module.md"
},
{
"text": "Console",
"path": "Startup-Templates/Console.md"
} }
] ]
}, },
@ -555,6 +559,10 @@
{ {
"text": "Contribution Guide", "text": "Contribution Guide",
"path": "Contribution/Index.md" "path": "Contribution/Index.md"
},
{
"text": "API Documentation",
"path": "{ApiDocumentationUrl}"
} }
] ]
} }

11
docs/pt-BR/Tutorials/AspNetCore-Mvc/Part-I.md

@ -222,7 +222,7 @@ using Volo.Abp.Application.Services;
namespace Acme.BookStore namespace Acme.BookStore
{ {
public interface IBookAppService : public interface IBookAppService :
ICrudAppService< //Defines CRUD methods ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books BookDto, //Used to show books
Guid, //Primary key of the book entity Guid, //Primary key of the book entity
@ -251,12 +251,12 @@ using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore namespace Acme.BookStore
{ {
public class BookAppService : public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto, CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>, CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService IBookAppService
{ {
public BookAppService(IRepository<Book, Guid> repository) public BookAppService(IRepository<Book, Guid> repository)
: base(repository) : base(repository)
{ {
@ -338,13 +338,11 @@ Abra `Index.cshtml`e altere o conteúdo, como mostrado abaixo:
```html ```html
@page @page
@using Acme.BookStore.Web.Pages.Books @using Acme.BookStore.Web.Pages.Books
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model IndexModel @model IndexModel
<h2>Books</h2> <h2>Books</h2>
``` ```
- Esse código altera a herança padrão do Razor View Page Model para que ele **herda** da `BookStorePage`classe (em vez de `PageModel`). A `BookStorePage`classe que acompanha o modelo de inicialização e fornece algumas propriedades / métodos compartilhados usados por todas as páginas.
- Verifique se o `IndexModel`( *Index.cshtml.cs)* possui o `Acme.BookStore.Pages.Books`espaço para nome ou atualize-o no `Index.cshtml`. - Verifique se o `IndexModel`( *Index.cshtml.cs)* possui o `Acme.BookStore.Pages.Books`espaço para nome ou atualize-o no `Index.cshtml`.
#### Adicionar página de livros ao menu principal #### Adicionar página de livros ao menu principal
@ -395,7 +393,6 @@ Altere o `Pages/Books/Index.cshtml`seguinte:
```html ```html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model Acme.BookStore.Web.Pages.Books.IndexModel @model Acme.BookStore.Web.Pages.Books.IndexModel
@section scripts @section scripts
{ {
@ -459,4 +456,4 @@ A interface do usuário final é mostrada abaixo:
### Próxima parte ### Próxima parte
Veja a [próxima parte](https://docs.abp.io/en/abp/latest/Tutorials/AspNetCore-Mvc/Part-II) deste tutorial. Veja a [próxima parte](https://docs.abp.io/en/abp/latest/Tutorials/AspNetCore-Mvc/Part-II) deste tutorial.

8
docs/pt-BR/Tutorials/AspNetCore-Mvc/Part-II.md

@ -67,7 +67,6 @@ Abra o `CreateModal.cshtml`arquivo e cole o código abaixo:
```html ```html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model Acme.BookStore.Web.Pages.Books.CreateModalModel @model Acme.BookStore.Web.Pages.Books.CreateModalModel
@{ @{
@ -86,13 +85,13 @@ Abra o `CreateModal.cshtml`arquivo e cole o código abaixo:
- Este modal usa o - Este modal usa o
``` ```
abp-dynamic-form abp-dynamic-form
``` ```
auxiliar de marca para criar automaticamente o formulário a partir da auxiliar de marca para criar automaticamente o formulário a partir da
``` ```
CreateBookViewModel CreateBookViewModel
@ -234,7 +233,6 @@ Substitua o `EditModal.cshtml`conteúdo pelo seguinte:
```html ```html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@using Acme.BookStore.Web.Pages.Books @using Acme.BookStore.Web.Pages.Books
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EditModalModel @model EditModalModel
@ -464,4 +462,4 @@ Execute o aplicativo e tente excluir um livro.
### Próxima parte ### Próxima parte
Veja a [próxima parte](https://docs.abp.io/en/abp/latest/Tutorials/AspNetCore-Mvc/Part-III) deste tutorial. Veja a [próxima parte](https://docs.abp.io/en/abp/latest/Tutorials/AspNetCore-Mvc/Part-III) deste tutorial.

294
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/Post.md

@ -0,0 +1,294 @@
# ABP框架v2.9已经发布
**ABP框架**和**ABP商业版**2.9已经发布,这是3.0之前的最后一个版本!这篇文章将涵盖本次发布中的**新增内容**.
## ABP框架2.9有哪些新增内容?
你可以中[GitHub的发行说明](https://github.com/abpframework/abp/releases/tag/2.9.0)中看到所有的变更.这篇文章将只包括重要特征/变更.
### 预编译Razor Pages
在之前的版本, 预构建的页面(为[应用模块](https://docs.abp.io/en/abp/latest/Modules/Index))和视图组件是在运行时编译. 现在,它们使用了预编译. 我们测量的应用程序启动时间(尤其是MVC UI)已经减少了50%以上.换句话说,它比之前的版本快**两倍**.速度变化也影响你第一次访问某一个页面时.
这是一个v2.8和v2.9启动应用程序模板的对比结果:
````
### v2.8
2020-06-04 22:59:04.891 +08:00 [INF] Starting web host.
2020-06-04 22:59:07.662 +08:00 [INF] Now listening on: https://localhost:44391
2020-06-04 22:59:17.315 +08:00 [INF] Request finished in 7756.6218ms 200 text/html;
Total: 12.42s
### v2.9
2020-06-04 22:59:13.720 +08:00 [INF] Starting web host.
2020-06-04 22:59:16.639 +08:00 [INF] Now listening on: https://localhost:44369
2020-06-04 22:59:18.957 +08:00 [INF] Request finished in 1780.5461ms 200 text/html;
Total: 5.24s
````
你不用做任何改动就能获得新方法带来的益处.[重写UI页/组件](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface)和之前一样也能正常工作.我们将在v3.0中继续致力于性能上的提升.
### 组织单元系统
[Identity模块](https://docs.abp.io/en/abp/latest/Modules/Identity)现在有了呼声最高的功能: 组织单元!
组织单元系统用来在应用程序中创建分层组织树.这样你可以使用该组织树来授权应用程序中的数据和功能.
文档将很快到来......
### 新的Blob存储包
我们创建了一个新的[Blob存储包](https://www.nuget.org/packages/Volo.Abp.BlobStoring)用来存储任意二进制对象.它一般用于在应用程序中存储文件.这个包提供了一个抽象,因此任何应用程序或[模块](https://docs.abp.io/en/abp/latest/Module-Development-Basics)都能以存储提供器无关的方式来保存和获取文件.
目前实现了两个存储提供器:
* [Volo.Abp.BlobStoring.FileSystem](https://www.nuget.org/packages/Volo.Abp.BlobStoring.FileSystem)包, 在本地文件系统中存储对象/文件.
* [Volo.Abp.BlobStoring.Database](https://github.com/abpframework/abp/tree/dev/modules/blob-storing-database)模块, 在数据库中存储对象/文件.目前支持[Entity Framework Core](https://docs.abp.io/en/abp/latest/Entity-Framework-Core)(因此,你可以使用[任何关系数据库](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Other-DBMS)和[MongoDB](https://docs.abp.io/en/abp/latest/MongoDB)).
[Azure BLOB提供器](https://github.com/abpframework/abp/issues/4098)将会在3.0中可用. 你可请求其他的云提供器或在[GitHub库](https://github.com/abpframework/abp/issues/new)上提交你自己的贡献.
Blob存储系统的一个好处是,它允许你创建多个容器(每个容器是一个Blob存储),并为每个容器使用不同的存储提供器.
**示例:使用默认的容器保存和取得一个字节数组**
````csharp
public class MyService : ITransientDependency
{
private readonly IBlobContainer _container;
public MyService(IBlobContainer container)
{
_container = container;
}
public async Task FooAsync()
{
//保存一个BLOB
byte[] bytes = GetBytesFromSomeWhere();
await _container.SaveAsync("my-unique-blob-name", bytes);
//获取一个BLOB
bytes = await _container.GetAllBytesAsync("my-unique-blob-name");
}
}
````
它可以使用`byte[]`和`Stream`对象.
**示例:使用类型化(命名)容器来保存和获取stream**
````csharp
public class MyService : ITransientDependency
{
private readonly IBlobContainer<TestContainer> _container;
public MyService(IBlobContainer<TestContainer> container)
{
_container = container;
}
public async Task FooAsync()
{
//保存一个BLOB
Stream stream = GetStreamFromSomeWhere();
await _container.SaveAsync("my-unique-blob-name", stream);
//获取一个BLOB
stream = await _container.GetAsync("my-unique-blob-name");
}
}
````
`TestContainer`只是一个用来标识容器的空类:
````csharp
[BlobContainerName("test")] //指定容器的名字
public class TestContainer
{
}
````
类型化(命名)容器可被配置为使用不同的存储提供器而不是默认的.在开发可复用的模块时, 始终使用类型化的容器是一个很好的做法,这样最终应用程序可以为这个容器配置提供器,而不影响其他容器.
**示例:为`TestContainer`配置文件系统提供器**
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.Configure<TestContainer>(configuration =>
{
configuration.UseFileSystem(fileSystem =>
{
fileSystem.BasePath = "C:\\MyStorageFolder";
});
});
});
````
查看[blob存储文档](https://docs.abp.io/en/abp/latest/Blob-Storing)以获取更多的信息.
### Entity Framework Core的Oracle集成包
我们创建了一个[Oralce集成包](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart),这样你就可以为EF Core轻松地切换到Oracle.它已经为框架和预构建的模块进行了测试.
[查看文档](https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Oracle)开始使用Oracle集成包.
### 自动判断数据库提供器
当你用EF Core开发一个**可复用的应用程序模块**时,你通常要将你的模块开发为**DBMS无关**的.但是,不同的DBMS有一些微小的(有时是很大的)区别.现在如何你执行基于DBMS的自定义映射,可以使用`ModelBuilder.IsUsingXXX()`扩展方法:
````csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Phone>(b =>
{
//...
if (modelBuilder.IsUsingPostgreSql()) //检查是否在使用PostgreSQL!
{
b.Property(x => x.Number).HasMaxLength(20);
}
else
{
b.Property(x => x.Number).HasMaxLength(32);
}
});
}
````
除了上面这种的傻傻的例子,你可以任意配置你的映射!
### ABP CLI:翻译命令
`abp translate`是一个新的命令,当你的源代码库中包含多个JSON本地化文件时, 它可用来简化翻译[本地化](https://docs.abp.io/en/abp/latest/Localization)文件,
该命令的主要目的是**翻译ABP框架**的本地化文件(因为[abp库](https://github.com/abpframework/abp)在不同的文件中含有成千上万个本地化文件需要翻译).
非常感谢如果你使用这个命令将框架资源翻译**为你的母语**.
查看[文档](https://docs.abp.io/en/abp/latest/CLI#translate)来学习如何使用它.也可查看[贡献指南](https://docs.abp.io/en/abp/latest/Contribution/Index).
### 新的虚拟文件系统浏览器模块
感谢[@liangshiw](https://github.com/liangshiw)创建并贡献了一个新的模块用来浏览[虚拟文件系统](https://docs.abp.io/en/abp/latest/Virtual-File-System)中的文件.它适用于MVC UI并显示所有应用程序中的虚拟文件.示例截图:
![virtual-file-explorer-1](virtual-file-explorer-1.png)
![virtual-file-explorer-2](virtual-file-explorer-2.png)
[查看文档](https://docs.abp.io/en/abp/latest/Modules/Virtual-File-Explorer)学习如何使用它.
### 示例应用程序:SignalR与分层架构
在分布式/分层架构中实施SignalR是具有挑战性的.我们创建了一个示例应用程序演示如何轻松地使用[SignalR集成](https://docs.abp.io/en/abp/latest/SignalR-Integration)和[分布式事件总线](https://docs.abp.io/en/abp/latest/Distributed-Event-Bus)系统
查看示例解决方案的[源代码](https://github.com/abpframework/abp-samples/tree/master/SignalRTieredDemo).
**一篇正在路上的文章**将深入地解释该解决方案.关注[@abpframework](https://twitter.com/abpframework)的Twitter帐号.
![signalr-tiered-demo](signalr-tiered-demo.png)
*一张文章中的图片,显示了该解决方案的通信图*
### 关于gRPC
我们创建了一个示例应用程序来说明如何在基于ABP的应用程序中创建和使用gRPC端点.
查看GitHub上的[源码](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo).
我们本来计划为所有预构建的应用程序模块创建gRPC端点,但我们发现ASP.NET Core gRPC集成还不够成熟,不支持一些常见的部署场景.所以推迟到了下一个版本(更多内容[查看此评论](https://github.com/abpframework/abp/issues/2882#issuecomment-633080242)).但是,在你的应用程序中使用gRPC是非常标准的. ABP框架与gRPC没有问题.看一下[示例应用程序](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo).
### 其它
* [时区系统](https://github.com/abpframework/abp/pull/3933)为应用程序支持不同的时区.
* 在IIS上支持[虚拟路径部署](https://github.com/abpframework/abp/issues/4089).
* 为Angular UI支持RTL.
其它更新请查看[GitHub发行说明](https://github.com/abpframework/abp/releases/tag/2.9.0).
## ABP商业版2.9有哪些新增内容
与往常一样, 除了ABP框架所有这些功能以外,ABP商业版在本次发布还有一些额外的功能.本节介绍[ABP商业版](https://commercial.abp.io/)在2.9版本中的亮点.
### 组织单元管理UI
我们为组织单元创建了UI,管理ABP商业版[Identity模块](https://commercial.abp.io/modules/Volo.Identity.Pro)的成员和角色:
![organization-units](organization-units.png)
OU管理适用于MVC(Razor Pages)和Angular用户界面.
### 聊天模块Angular UI
我们在前一个版本介绍了新的[聊天模块](https://commercial.abp.io/modules/Volo.Chat), 当时它只有ASP.NET Core MVC / Razor Pages UI. 现在它也包含了一个Angular UI选项.
![abp-chat-module](abp-chat-module.png)
*聊天模块的截图 - 两个用户互相发消息*
### Easy CRM Angular UI
Easy CRM是建立在ABP商业版上的一个示例应用程序, 用来为ABP商业版客户提供一个相对复杂的应用程序.在2.7版本中,我们已经发布了MVC / Razor Pages UI. 这次2.9版中, 我们为Easy CRM应用程序发布了Angular UI.
![easy-crm](easy-crm.png)
*Easy CRM应用程序中"订单详细"的截图.*
查看[Easy CRM文档](https://docs.abp.io/en/commercial/latest/samples/easy-crm)学习如何下载并运行它.
### ABP Suite模块代码生成
[ABP Suite](https://commercial.abp.io/tools/suite)是一个工具,主要功能是用来为一个实体[生成代码](https://docs.abp.io/en/commercial/latest/abp-suite/generating-crud-page), 从数据库到UI层具有完整的CRUD功能.
![suite](suite.png)
*ABP Suite的截图: 定义新实体的属性并且为你生成应用程序代码!*
在本次发布之前它只工作于[应用程序模板](https://docs.abp.io/en/commercial/latest/startup-templates/application/index).现在,它支持为[模块项目](https://docs.abp.io/en/commercial/latest/startup-templates/module/index)生成代码.利用代码生成的威力来创建可复用应用程序模块是很棒的一个做法.
除了这个主要功能,我们在这个版本中向ABP Suite添加了许多细微的改进.
>注意:模块模板代码生成目前处于测试阶段.如果你发现任何bug,请告知我们.
### Lepton主题
[Lepton主题](https://commercial.abp.io/themes)是我们为ABP商业版开发的一个商业主题.
* 与Bootstrap 100%兼容 - 让你不写主题特定的HTML!
* 提供不同类型的风格 - 看一下下图中的Material风格.
* 提供不同类型的布局(侧/顶部菜单,流式/盒式布局...).
* 轻量化,响应式和现代化.
* 还有...它是可升级的,没有成本!你只需更新NuGet / NPM包来获得新的功能.
我们创建了它的专属网站:[http://leptontheme.com/](http://leptontheme.com/)
在这里你可以查看所有的组件, 无需单独的应用程序.
![lepton-theme](lepton-theme.png)
这个网站目前正处于一个非常早期的阶段.我们将创建文档和和改进网站, 来为你的开发提供参考和探索主题的功能.
### 即将推出:文件管理模块
基于新的blob存储系统(上面介绍的),我们已经开始构建一个文件管理模块用来管理(浏览/上传/下载)你应用程序中分层文件系统并在用户与客户之间分享文件.
我们计划在ABP商业版v3.0中发行最初版本,并继续进行后续版本的改进.
## 关于下一个版本:3.0
我们在[v2.8](https://blog.abp.io/abp/ABP-v2.8.0-Releases-%26-Road-Map)和v2.9中增加了许多新的功能.在下一个版本中,我们将完全专注于**文档,性能优化**和其它改进,如bug修复.
长期以来,我们每2周发布一个新功能版本.我们在v3.0以后继续这种方式.但是,v3.0是一个例外,开发周期大概为4周.**v3.0的计划发布日期是2020年7月1日**.
## 彩蛋:文章!
除了开发我们的产品,我们的团队都在不断地撰写各种主题的文章/教程.你可以看一下最新的文章:
* [ASP.NET Core 3.1使用Pub/Sub实现WebHook](https://volosoft.com/blog/ASP.NET-CORE-3.1-Webhook-Implementation-Using-Pub-Sub)
* [ASP.NET Core使用Azure Key Vault](https://volosoft.com/blog/Using-Azure-Key-Vault-with-ASP.NET-Core)

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/abp-chat-module.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/easy-crm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/lepton-theme.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/organization-units.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/signalr-tiered-demo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/suite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
docs/zh-Hans/Blog-Posts/2020-06-05 v2_9_Release/virtual-file-explorer-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

18
docs/zh-Hans/Contribution/Index.md

@ -43,18 +43,20 @@ ABP框架具有灵活的[本地化系统](../Localization.md). 你可以为自
除此之外,框架和预构建模块已经本地化了文本.请参阅[Volo.Abp.UI包的本地化文本](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json). 除此之外,框架和预构建模块已经本地化了文本.请参阅[Volo.Abp.UI包的本地化文本](https://github.com/abpframework/abp/blob/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json).
你可以在[相同文件夹](https://github.com/abpframework/abp/tree/master/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi)中创建一个新文件进行翻译. #### 使用 "abp translate" 命令
* 从Github克隆[ABP存储库](https://github.com/abpframework/abp/). 这是推荐的方法,因为它会自动查找所有缺少的文本的特定文化,让你在一个地方翻译.
* 为本地化文本(json)文件(en.json文件同目录下)创建目标语言的新文件.
* 复制en.json文件中的所有文本.
* 翻译文本.
* 在Github上发送拉取请求(Pull request).
* 从Github克隆[ABP存储库](https://github.com/abpframework/abp/).
* 安装[ABP CLI](https://docs.abp.io/en/abp/latest/CLI).
* 在abp仓储的根文件夹为你的语言运行`abp translate -c <culture-name>`命令. 例如对法语使用 `abp translate -c fr`, 检查[文档](https://docs.microsoft.com/en-us/bingmaps/rest-services/common-parameters-and-types/supported-culture-codes)找到你所用语言的文化代码.
* 命令会在同一文件夹下创建 `abp-translation.json` 文件, 使用你喜欢的编辑器打开这个文件并填写缺少的文本值.
* 一旦你完成了翻译,使用 `abp translate -a` 命令应用更改到相关的文件.
* 在GitHub上发送PR.
你还可以使用[ABP CLI](CLI.md)的`abp translation`命令来翻译本地化文本. #### 手动翻译
ABP是一个模块化框架. 所以有很多本地化文本资源, 每个模块都有一个. 要查找所有.json文件,可以在克隆存储库后搜索"en.json". 你还可以检查[此列表](Localization-Text-Files.md)以获取本地化文本文件列表. 如果你想更改特定的资源文件,你可以自己找到这个文件进行必要的更改(或为你的语言创建新文件),并在GitHub上发送PR。
### 博客文章和教程 ### 博客文章和教程

10
docs/zh-Hans/Entity-Framework-Core-Oracle.md

@ -28,14 +28,14 @@ In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFac
使用以下代码替换*YourProjectName*MigrationsDbContextFactory.cs中的 `CreateDbContext()` 方法: 使用以下代码替换*YourProjectName*MigrationsDbContextFactory.cs中的 `CreateDbContext()` 方法:
``` ```csharp
var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>() var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>()
.UseSqlServer(configuration.GetConnectionString("Default")); .UseSqlServer(configuration.GetConnectionString("Default"));
``` ```
与这个 与这个
``` ```csharp
var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>) var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>)
new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle
( (
@ -51,12 +51,6 @@ Oracle连接字符串与SQL Server连接字符串不同. 所以检查你的解
通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构. 通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构.
Oracle连接字符串示例:
```
Data Source=localhost;User Id=myuser;Password=mypassword;
```
## 重新生成迁移 ## 重新生成迁移
启动模板使用[Entity Framework Core的Code First迁移](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). EF Core迁移取决于所选的DBMS提供程序. 因此更改DBMS提供程序会导致迁移失败. 启动模板使用[Entity Framework Core的Code First迁移](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). EF Core迁移取决于所选的DBMS提供程序. 因此更改DBMS提供程序会导致迁移失败.

5
docs/zh-Hans/How-To/Customize-Login-Page-MVC.md

@ -26,7 +26,7 @@ public class CustomLoginModel : LoginModel
## 重写登录页面UI ## 重写登录页面UI
在 **Pages** 目录下创建名为 **Account** 的文件夹,并在这个文件夹中创建 `Login.cshtml` ,借助[虚拟文件系统](../Virtual-File-System.md)它会自动覆盖账户模块的页面文件. 在 **Pages** 目录下创建名为 **Account** 的文件夹,并在这个文件夹中创建 `Login.cshtml` , 它会自动覆盖账户模块的页面文件.
自定义页面一个很好的开始是复制它的源代码. [点击这里](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml)找到登录页面的源码. 在编写本文档时,源代码如下: 自定义页面一个很好的开始是复制它的源代码. [点击这里](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml)找到登录页面的源码. 在编写本文档时,源代码如下:
@ -35,7 +35,6 @@ public class CustomLoginModel : LoginModel
@using Volo.Abp.Account.Settings @using Volo.Abp.Account.Settings
@using Volo.Abp.Settings @using Volo.Abp.Settings
@model Acme.BookStore.Web.Pages.Account.CustomLoginModel @model Acme.BookStore.Web.Pages.Account.CustomLoginModel
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage
@inject Volo.Abp.Settings.ISettingProvider SettingProvider @inject Volo.Abp.Settings.ISettingProvider SettingProvider
@if (Model.EnableLocalLogin) @if (Model.EnableLocalLogin)
{ {
@ -110,4 +109,4 @@ public class CustomLoginModel : LoginModel
## 另请参阅 ## 另请参阅
* [ASP.NET Core (MVC / Razor Pages) 用户界面自定义指南](../UI/AspNetCore/Customization-User-Interface.md). * [ASP.NET Core (MVC / Razor Pages) 用户界面自定义指南](../UI/AspNetCore/Customization-User-Interface.md).

55
docs/zh-Hans/Object-To-Object-Mapping.md

@ -162,6 +162,61 @@ public class MyProfile : Profile
如果两个类都是可扩展对象(实现了 `IHasExtraProperties` 接口),建议使用 `MapExtraProperties` 方法. 更多信息请参阅[对象扩展文档](Object-Extensions.md). 如果两个类都是可扩展对象(实现了 `IHasExtraProperties` 接口),建议使用 `MapExtraProperties` 方法. 更多信息请参阅[对象扩展文档](Object-Extensions.md).
### 其他有用的扩展方法
有一些扩展方法可以简化映射代码.
#### 忽视审计属性
当你将一个对象映射到另一个对象时,通常会忽略审核属性.
假设你需要将 `ProductDto` ([DTO](Data-Transfer-Objects.md))映射到Product[实体](Entities.md),该实体是从 `AuditedEntity` 类继承的(该类提供了 `CreationTime`, `CreatorId`, `IHasModificationTime` 等属性).
从DTO映射时你可能想忽略这些基本属性,可以使用 `IgnoreAuditedObjectPropertie()` 方法忽略所有审计属性(而不是手动逐个忽略它们):
````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<ProductDto, Product>()
.IgnoreAuditedObjectProperties();
}
}
````
还有更多扩展方法, 如 `IgnoreFullAuditedObjectProperties()` 和 `IgnoreCreationAuditedObjectProperties()`,你可以根据实体类型使用.
> 请参阅[实体文档](Entities.md)中的"*基类和接口的审计属性*"部分了解有关审计属性的更多信息。
#### 忽视其他属性
在AutoMapper中,通常可以编写这样的映射代码来忽略属性:
````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<SimpleClass1, SimpleClass2>()
.ForMember(x => x.CreationTime, map => map.Ignore());
}
}
````
我们发现它的长度是不必要的并且创建了 `Ignore()` 扩展方法:
````csharp
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<SimpleClass1, SimpleClass2>()
.Ignore(x => x.CreationTime);
}
}
````
## 高级主题 ## 高级主题
### IObjectMapper<TContext> 接口 ### IObjectMapper<TContext> 接口

26
docs/zh-Hans/Road-Map.md

@ -4,8 +4,28 @@
虽然我们将**继续添加其他令人兴奋的功能**,但我们将在`中期`中处理以下主要项目: 虽然我们将**继续添加其他令人兴奋的功能**,但我们将在`中期`中处理以下主要项目:
* **gRPC 集成**和为所有预构建模块实现它.
* 为所有预构建模块实现 **Blazor UI**. * 为所有预构建模块实现 **Blazor UI**.
* **.NET 5.0**! Microsoft宣布.NET 5.0将在2020年11月发布,我们将为此更改做准备并在Microsoft发布它之后立即迁移到.NET 5.0. 我们希望顺利过渡. * **.NET 5.0**! Microsoft宣布.NET 5.0将在2020年11月发布,我们将为此更改做准备并在Microsoft发布它之后立即迁移到.NET 5.0. 我们希望顺利过渡.虽然已经可以为您的应用程序创建或使用gRPC端点,但我们计划为标准应用程序模块创建端点
请在[GitHub仓库](https://github.com/abpframework/abpork/abp/milestones)为你的功能请求创建issue,但在创建前请先搜索是否已存在类似的issues. 除了中期目录,还有一些[积压](https://github.com/abpframework/abp/milestone/2)的功能, 这里积压中重要功能的列表:
* [#4098](https://github.com/abpframework/abp/issues/4098) / Blob存储Azure提供者.
* [#2882](https://github.com/abpframework/abp/issues/2882) / 提供 **gRPC集成** 基础设施 (虽然[已经可以](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo)为你的应用程序创建和使用gRPC端点,但我们计划为所有[标准应用程序模块](https://docs.abp.io/en/abp/latest/Modules/Index)创建端点)
* [#236](https://github.com/abpframework/abp/issues/236) 基于权限系统的资源
* [#1754](https://github.com/abpframework/abp/issues/1754) / 多语言实体
* [#347](https://github.com/abpframework/abp/issues/347) / 支持MongoDB ACID事务
* [#633](https://github.com/abpframework/abp/issues/633) / 实时通知系统
* [#57](https://github.com/abpframework/abp/issues/57) / 内置CQRS基础设施
* [#4222](https://github.com/abpframework/abp/issues/4222) / 分布式事件总线Kafka集成
* [#336](https://github.com/abpframework/abp/issues/336) / 健康检查抽象
* [#2532](https://github.com/abpframework/abp/issues/2532), [#2564](https://github.com/abpframework/abp/issues/2465) / EF Core 与 MongoDB API 集成CosmosDB
* [#1168](https://github.com/abpframework/abp/issues/1168) / Vue 启动模板
* [#1638](https://github.com/abpframework/abp/issues/1638) React 启动模板
* [#4223](https://github.com/abpframework/abp/issues/4223) / WebHook系统
* [#162](https://github.com/abpframework/abp/issues/162) / 为多租户集成Azure ElasticDB
* [#2296](https://github.com/abpframework/abp/issues/2296) / 功能切换基础架构
积压的项目有可能发生变化. 我们将根据社区反馈和项目目标添加新项目与更改优先级.
在Github相关issue为你感兴趣的功能投票(并写下你的想法)
. 你可以在[GitHub仓库](https://github.com/abpframework/abpork/abp/milestones)为你的功能请求创建issue,但在创建前请先搜索是否已存在类似的issues.

15
docs/zh-Hans/Tutorials/Part-1.md

@ -14,7 +14,7 @@ else if UI == "NG"
DB="mongodb" DB="mongodb"
DB_Text="MongoDB" DB_Text="MongoDB"
UI_Text="angular" UI_Text="angular"
else else
DB ="?" DB ="?"
UI_Text="?" UI_Text="?"
end end
@ -444,7 +444,7 @@ using Volo.Abp.Application.Services;
namespace Acme.BookStore namespace Acme.BookStore
{ {
public interface IBookAppService : public interface IBookAppService :
ICrudAppService< //定义了CRUD方法 ICrudAppService< //定义了CRUD方法
BookDto, //用来展示书籍 BookDto, //用来展示书籍
Guid, //Book实体的主键 Guid, //Book实体的主键
@ -473,12 +473,12 @@ using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore namespace Acme.BookStore
{ {
public class BookAppService : public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto, CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>, CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService IBookAppService
{ {
public BookAppService(IRepository<Book, Guid> repository) public BookAppService(IRepository<Book, Guid> repository)
: base(repository) : base(repository)
{ {
@ -564,13 +564,11 @@ successfully created the book with id: 439b0ea8-923e-8e1e-5d97-39f2c7ac4246
````html ````html
@page @page
@using Acme.BookStore.Web.Pages.Book @using Acme.BookStore.Web.Pages.Book
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model IndexModel @model IndexModel
<h2>Book</h2> <h2>Book</h2>
```` ````
* 此代码更改了Razor View Page Model的默认继承,因此它从`BookStorePage`类(而不是`PageModel`)继承.启动模板附带的`BookStorePage`类,提供所有页面使用的一些共享属性/方法.
* 确保`IndexModel`(Index.cshtml.cs)具有`Acme.BookStore.Web.Pages.Book`命名空间,或者在`Index.cshtml`中更新它. * 确保`IndexModel`(Index.cshtml.cs)具有`Acme.BookStore.Web.Pages.Book`命名空间,或者在`Index.cshtml`中更新它.
**Index.cshtml.cs:** **Index.cshtml.cs:**
@ -599,7 +597,7 @@ namespace Acme.BookStore.Web.Pages.Book
namespace Acme.BookStore.Web.Menus namespace Acme.BookStore.Web.Menus
{ {
public class BookStoreMenuContributor : IMenuContributor public class BookStoreMenuContributor : IMenuContributor
{ {
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) private async Task ConfigureMainMenuAsync(MenuConfigurationContext context)
{ {
//<-- added the below code //<-- added the below code
@ -667,7 +665,6 @@ namespace Acme.BookStore.Web.Menus
````html ````html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@model Acme.BookStore.Web.Pages.Book.IndexModel @model Acme.BookStore.Web.Pages.Book.IndexModel
@section scripts @section scripts
{ {
@ -1060,4 +1057,4 @@ export class BookListComponent implements OnInit {
### 下一章 ### 下一章
参阅[第二章](part-2.md)了解创建,更新和删除图书. 参阅[第二章](part-2.md)了解创建,更新和删除图书.

10
docs/zh-Hans/Tutorials/Part-2.md

@ -15,7 +15,7 @@ else if UI == "NG"
DB="mongodb" DB="mongodb"
DB_Text="MongoDB" DB_Text="MongoDB"
UI_Text="angular" UI_Text="angular"
else else
DB ="?" DB ="?"
UI_Text="?" UI_Text="?"
end end
@ -86,7 +86,6 @@ namespace Acme.BookStore.Web.Pages.Books
````html ````html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model Acme.BookStore.Web.Pages.Books.CreateModalModel @model Acme.BookStore.Web.Pages.Books.CreateModalModel
@{ @{
@ -230,7 +229,6 @@ namespace Acme.BookStore.Web
````html ````html
@page @page
@inherits Acme.BookStore.Web.Pages.BookStorePage
@using Acme.BookStore.Web.Pages.Books @using Acme.BookStore.Web.Pages.Books
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal @using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EditModalModel @model EditModalModel
@ -866,7 +864,7 @@ export class BookListComponent implements OnInit {
} }
``` ```
* 我们导入了 ` NgbDateNativeAdapter, NgbDateAdapter` * 我们导入了 ` NgbDateNativeAdapter, NgbDateAdapter`
* 我们添加了一个新的 `NgbDateAdapter` 提供程序,它将Datepicker值转换为Date类型. 有关更多详细信息,请参见[datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview). * 我们添加了一个新的 `NgbDateAdapter` 提供程序,它将Datepicker值转换为Date类型. 有关更多详细信息,请参见[datepicker adapters](https://ng-bootstrap.github.io/#/components/datepicker/overview).
@ -972,7 +970,7 @@ export class BookListComponent implements OnInit {
<button type="button" class="btn btn-secondary" #abpClose> <button type="button" class="btn btn-secondary" #abpClose>
{%{{{ 'AbpAccount::Close' | abpLocalization }}}%} {%{{{ 'AbpAccount::Close' | abpLocalization }}}%}
</button> </button>
<!--added save button--> <!--added save button-->
<button class="btn btn-primary" (click)="save()" [disabled]="form.invalid"> <button class="btn btn-primary" (click)="save()" [disabled]="form.invalid">
<i class="fa fa-check mr-1"></i> <i class="fa fa-check mr-1"></i>
@ -1290,7 +1288,7 @@ import { ConfirmationService } from '@abp/ng.theme.shared';
//... //...
constructor( constructor(
private store: Store, private store: Store,
private fb: FormBuilder, private fb: FormBuilder,
private bookService: BookService, private bookService: BookService,
private confirmation: ConfirmationService // <== added this line ==> private confirmation: ConfirmationService // <== added this line ==>

6
docs/zh-Hans/UI/AspNetCore/Customization-User-Interface.md

@ -51,15 +51,13 @@ namespace Acme.BookStore.Web.Pages.Identity.Users
### 重写Razor页面 (.CSHTML) ### 重写Razor页面 (.CSHTML)
使用[虚拟文件系统](../../Virtual-File-System.md)可以重写 `.cshtml` 文件(razor page, razor view, view component... 等.) 同一路径下创建相同的`.cshtml`文件可以实现重写功能(razor page, razor view, view component... 等.)
虚拟文件系统允许我们将**资源嵌入到程序集中**. 通过这个方式,预构建的模块在Nuget包中定义了Razor页面. 当你依赖模块时,可以覆盖这个模块向虚拟文件系统添加的任何文件,包括页面/视图.
#### 示例 #### 示例
这个示例重写了[账户模块](../../Modules/Account.md)定义的**登录页面**UI 这个示例重写了[账户模块](../../Modules/Account.md)定义的**登录页面**UI
物理文件可以覆盖相同位置的嵌入文件. 账户模块在 `Pages/Account` 文件夹下定义了 `Login.cshtml` 文件. 所以你可以在同一路径下创建文件覆盖它: 账户模块在 `Pages/Account` 文件夹下定义了 `Login.cshtml` 文件. 所以你可以在同一路径下创建文件覆盖它:
![overriding-login-cshtml](../../images/overriding-login-cshtml.png) ![overriding-login-cshtml](../../images/overriding-login-cshtml.png)
通常你想要拷贝模块的 `.cshtml` 原文件,然后进行需要的更改. 你可以在[这里](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml)找到源文件. 不要拷贝 `Login.cshtml.cs` 文件,它是隐藏razor页面的代码,我们不希望覆盖它(见下节). 通常你想要拷贝模块的 `.cshtml` 原文件,然后进行需要的更改. 你可以在[这里](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml)找到源文件. 不要拷贝 `Login.cshtml.cs` 文件,它是隐藏razor页面的代码,我们不希望覆盖它(见下节).

10
docs/zh-Hans/Virtual-File-System.md

@ -1,6 +1,6 @@
## 虚拟文件系统 ## 虚拟文件系统
虚拟文件系统使得管理物理上不存在于文件系统中(磁盘)的文件成为可能. 它主要用于将(js, css, image, cshtml ...)文件嵌入到程序集中, 并在运行时将它们象物理文件一样使用. 虚拟文件系统使得管理物理上不存在于文件系统中(磁盘)的文件成为可能. 它主要用于将(js, css, image...)文件嵌入到程序集中, 并在运行时将它们象物理文件一样使用.
### Volo.Abp.VirtualFileSystem nuget包 ### Volo.Abp.VirtualFileSystem nuget包
@ -147,7 +147,7 @@ public class MyWebAppModule : AbpModule
虚拟文件系统与 ASP.NET Core 无缝集成: 虚拟文件系统与 ASP.NET Core 无缝集成:
* 虚拟文件可以像Web应用程序上的物理(静态)文件一样使用. * 虚拟文件可以像Web应用程序上的物理(静态)文件一样使用.
* Razor Views, Razor Pages, js, css, 图像文件和所有其他Web内容可以嵌入到程序集中并像物理文件一样使用. * Js, css, 图像文件和所有其他Web内容可以嵌入到程序集中并像物理文件一样使用.
* 应用程序(或其他模块)可以覆盖模块的虚拟文件, 就像将具有相同名称和扩展名的文件放入虚拟文件的同一文件夹中一样. * 应用程序(或其他模块)可以覆盖模块的虚拟文件, 就像将具有相同名称和扩展名的文件放入虚拟文件的同一文件夹中一样.
#### 虚拟文件中间件 #### 虚拟文件中间件
@ -161,9 +161,3 @@ app.UseVirtualFiles();
在静态文件中间件之后添加虚拟文件中间件, 使得通过在虚拟文件相同的位置放置物理文件, 从而用物理文件覆盖虚拟文件成为可能. 在静态文件中间件之后添加虚拟文件中间件, 使得通过在虚拟文件相同的位置放置物理文件, 从而用物理文件覆盖虚拟文件成为可能.
> 虚拟文件中间件可以虚拟wwwroot文件夹中的内容 - 就像静态文件一样. > 虚拟文件中间件可以虚拟wwwroot文件夹中的内容 - 就像静态文件一样.
#### Views & Pages
无需任何配置即可在应用程序中使用嵌入式的 razor Views/pages. 只需要将它们放置在要开发的模块中的标准 Views/Pages 虚拟文件夹即可.
如果模块/应用程序将新文件放置同一位置, 则会覆盖嵌入式的 Views/Pages.

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

@ -24,11 +24,12 @@
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
var rtl = CultureHelper.IsRtl ? "rtl" : string.Empty;
} }
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")> <html lang="@CultureInfo.CurrentCulture.Name" dir="@rtl">
<head> <head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Account) @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Account)
@ -48,7 +49,7 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Account) @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Account)
</head> </head>
<body class="abp-account-layout bg-light"> <body class="abp-account-layout bg-light @rtl">
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Account) @await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Account)
@(await Component.InvokeAsync<MainNavbarViewComponent>()) @(await Component.InvokeAsync<MainNavbarViewComponent>())

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

@ -28,11 +28,13 @@
pageTitle = PageLayout.Content.Title + pageTitle; pageTitle = PageLayout.Content.Title + pageTitle;
} }
var rtl = CultureHelper.IsRtl ? "rtl" : string.Empty;
} }
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")> <html lang="@CultureInfo.CurrentCulture.Name" dir="@rtl">
<head> <head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Application) @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Application)
@ -50,7 +52,7 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Application) @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Application)
</head> </head>
<body class="abp-application-layout bg-light"> <body class="abp-application-layout bg-light @rtl">
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Application) @await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Application)
@(await Component.InvokeAsync<MainNavbarViewComponent>()) @(await Component.InvokeAsync<MainNavbarViewComponent>())

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

@ -27,11 +27,13 @@
pageTitle = PageLayout.Content.Title + pageTitle; pageTitle = PageLayout.Content.Title + pageTitle;
} }
var rtl = CultureHelper.IsRtl ? "rtl" : string.Empty;
} }
<!DOCTYPE html> <!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name" dir=@(CultureHelper.IsRtl ? "rtl" : "")> <html lang="@CultureInfo.CurrentCulture.Name" dir="@rtl">
<head> <head>
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Empty) @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.First, StandardLayouts.Empty)
@ -52,7 +54,7 @@
@await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Empty) @await Component.InvokeLayoutHookAsync(LayoutHooks.Head.Last, StandardLayouts.Empty)
</head> </head>
<body class="abp-empty-layout"> <body class="abp-empty-layout @rtl">
@await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Empty) @await Component.InvokeLayoutHookAsync(LayoutHooks.Body.First, StandardLayouts.Empty)
<div class="@containerClass"> <div class="@containerClass">

13
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/AbpPageSearchBoxViewComponent.cs

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageSearchBox
{
public class AbpPageSearchBoxViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View("~/Pages/Shared/Components/AbpPageSearchBox/Default.cshtml");
}
}
}

13
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Pages/Shared/Components/AbpPageSearchBox/Default.cshtml

@ -0,0 +1,13 @@
@using Localization.Resources.AbpUi
@using Microsoft.Extensions.Localization
@inject IStringLocalizer<AbpUiResource> L
<form method="post" class="page-search-form">
<div class="input-group">
<input class="form-control page-search-filter-text" placeholder="@L["Search"].Value">
<div class="input-group-append">
<abp-button button-type="Primary" type="submit">
<i class="fa fa-search" aria-hidden="true"></i>
</abp-button>
</div>
</div>
</form>

30
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs

@ -28,7 +28,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages
protected TService LazyGetRequiredService<TService>(ref TService reference) protected TService LazyGetRequiredService<TService>(ref TService reference)
=> LazyGetRequiredService(typeof(TService), ref reference); => LazyGetRequiredService(typeof(TService), ref reference);
protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference) protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference)
{ {
if (reference == null) if (reference == null)
@ -45,16 +45,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages
return reference; return reference;
} }
public IClock Clock => LazyGetRequiredService(ref _clock); protected IClock Clock => LazyGetRequiredService(ref _clock);
private IClock _clock; private IClock _clock;
public AlertList Alerts => AlertManager.Alerts; protected AlertList Alerts => AlertManager.Alerts;
public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager); protected IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager);
private IUnitOfWorkManager _unitOfWorkManager; private IUnitOfWorkManager _unitOfWorkManager;
protected Type ObjectMapperContext { get; set; } protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper protected IObjectMapper ObjectMapper
{ {
get get
{ {
@ -76,16 +76,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages
} }
private IObjectMapper _objectMapper; private IObjectMapper _objectMapper;
public IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator); protected IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator);
private IGuidGenerator _guidGenerator; private IGuidGenerator _guidGenerator;
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); protected IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory);
private IStringLocalizerFactory _stringLocalizerFactory; private IStringLocalizerFactory _stringLocalizerFactory;
public IStringLocalizer L protected IStringLocalizer L
{ {
get get
{ {
@ -102,22 +102,22 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages
protected Type LocalizationResourceType { get; set; } protected Type LocalizationResourceType { get; set; }
public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser);
private ICurrentUser _currentUser; private ICurrentUser _currentUser;
public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant);
private ICurrentTenant _currentTenant; private ICurrentTenant _currentTenant;
public ISettingProvider SettingProvider => LazyGetRequiredService(ref _settingProvider); protected ISettingProvider SettingProvider => LazyGetRequiredService(ref _settingProvider);
private ISettingProvider _settingProvider; private ISettingProvider _settingProvider;
public IModelStateValidator ModelValidator => LazyGetRequiredService(ref _modelValidator); protected IModelStateValidator ModelValidator => LazyGetRequiredService(ref _modelValidator);
private IModelStateValidator _modelValidator; private IModelStateValidator _modelValidator;
public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); protected IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService);
private IAuthorizationService _authorizationService; private IAuthorizationService _authorizationService;
public IAlertManager AlertManager => LazyGetRequiredService(ref _alertManager); protected IAlertManager AlertManager => LazyGetRequiredService(ref _alertManager);
private IAlertManager _alertManager; private IAlertManager _alertManager;
protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current;

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

@ -43,11 +43,11 @@ namespace Volo.Abp.AspNetCore.Mvc
return reference; return reference;
} }
public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager); protected IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager);
private IUnitOfWorkManager _unitOfWorkManager; private IUnitOfWorkManager _unitOfWorkManager;
protected Type ObjectMapperContext { get; set; } protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper protected IObjectMapper ObjectMapper
{ {
get get
{ {
@ -69,39 +69,39 @@ namespace Volo.Abp.AspNetCore.Mvc
} }
private IObjectMapper _objectMapper; private IObjectMapper _objectMapper;
public IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator); protected IGuidGenerator GuidGenerator => LazyGetRequiredService(ref _guidGenerator);
private IGuidGenerator _guidGenerator; private IGuidGenerator _guidGenerator;
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
protected ILogger Logger => _lazyLogger.Value; protected ILogger Logger => _lazyLogger.Value;
private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser);
private ICurrentUser _currentUser; private ICurrentUser _currentUser;
public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant);
private ICurrentTenant _currentTenant; private ICurrentTenant _currentTenant;
public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); protected IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService);
private IAuthorizationService _authorizationService; private IAuthorizationService _authorizationService;
protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current; protected IUnitOfWork CurrentUnitOfWork => UnitOfWorkManager?.Current;
public IClock Clock => LazyGetRequiredService(ref _clock); protected IClock Clock => LazyGetRequiredService(ref _clock);
private IClock _clock; private IClock _clock;
public IModelStateValidator ModelValidator => LazyGetRequiredService(ref _modelValidator); protected IModelStateValidator ModelValidator => LazyGetRequiredService(ref _modelValidator);
private IModelStateValidator _modelValidator; private IModelStateValidator _modelValidator;
public IFeatureChecker FeatureChecker => LazyGetRequiredService(ref _featureChecker); protected IFeatureChecker FeatureChecker => LazyGetRequiredService(ref _featureChecker);
private IFeatureChecker _featureChecker; private IFeatureChecker _featureChecker;
public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); protected IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory);
private IStringLocalizerFactory _stringLocalizerFactory; private IStringLocalizerFactory _stringLocalizerFactory;
public IStringLocalizer L protected IStringLocalizer L
{ {
get get
{ {

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

@ -30,7 +30,7 @@ namespace Volo.Abp.AspNetCore.Mvc
} }
protected Type ObjectMapperContext { get; set; } protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper protected IObjectMapper ObjectMapper
{ {
get get
{ {

30
framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpHub.cs

@ -36,28 +36,28 @@ namespace Volo.Abp.AspNetCore.SignalR
return reference; return reference;
} }
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
protected ILogger Logger => _lazyLogger.Value; protected ILogger Logger => _lazyLogger.Value;
private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser);
private ICurrentUser _currentUser; private ICurrentUser _currentUser;
public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant);
private ICurrentTenant _currentTenant; private ICurrentTenant _currentTenant;
public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); protected IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService);
private IAuthorizationService _authorizationService; private IAuthorizationService _authorizationService;
public IClock Clock => LazyGetRequiredService(ref _clock); protected IClock Clock => LazyGetRequiredService(ref _clock);
private IClock _clock; private IClock _clock;
public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); protected IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory);
private IStringLocalizerFactory _stringLocalizerFactory; private IStringLocalizerFactory _stringLocalizerFactory;
public IStringLocalizer L protected IStringLocalizer L
{ {
get get
{ {
@ -99,7 +99,7 @@ namespace Volo.Abp.AspNetCore.SignalR
} }
} }
public abstract class AbpHub<T> : Hub<T> public abstract class AbpHub<T> : Hub<T>
where T : class where T : class
{ {
public IServiceProvider ServiceProvider { get; set; } public IServiceProvider ServiceProvider { get; set; }
@ -124,28 +124,28 @@ namespace Volo.Abp.AspNetCore.SignalR
return reference; return reference;
} }
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
protected ILogger Logger => _lazyLogger.Value; protected ILogger Logger => _lazyLogger.Value;
private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser);
private ICurrentUser _currentUser; private ICurrentUser _currentUser;
public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant);
private ICurrentTenant _currentTenant; private ICurrentTenant _currentTenant;
public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); protected IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService);
private IAuthorizationService _authorizationService; private IAuthorizationService _authorizationService;
public IClock Clock => LazyGetRequiredService(ref _clock); protected IClock Clock => LazyGetRequiredService(ref _clock);
private IClock _clock; private IClock _clock;
public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); protected IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory);
private IStringLocalizerFactory _stringLocalizerFactory; private IStringLocalizerFactory _stringLocalizerFactory;
public IStringLocalizer L protected IStringLocalizer L
{ {
get get
{ {

4
framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs

@ -35,7 +35,7 @@ namespace Volo.Abp.BackgroundWorkers
return reference; return reference;
} }
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
protected ILogger Logger => _lazyLogger.Value; protected ILogger Logger => _lazyLogger.Value;
@ -58,4 +58,4 @@ namespace Volo.Abp.BackgroundWorkers
return GetType().FullName; return GetType().FullName;
} }
} }
} }

1
framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj

@ -16,6 +16,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" /> <ProjectReference Include="..\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" />
<PackageReference Include="Polly" Version="7.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

53
framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs

@ -1,5 +1,7 @@
using System.IO; using System;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Polly;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.IO; using Volo.Abp.IO;
@ -8,12 +10,12 @@ namespace Volo.Abp.BlobStoring.FileSystem
public class FileSystemBlobProvider : BlobProviderBase, ITransientDependency public class FileSystemBlobProvider : BlobProviderBase, ITransientDependency
{ {
protected IBlobFilePathCalculator FilePathCalculator { get; } protected IBlobFilePathCalculator FilePathCalculator { get; }
public FileSystemBlobProvider(IBlobFilePathCalculator filePathCalculator) public FileSystemBlobProvider(IBlobFilePathCalculator filePathCalculator)
{ {
FilePathCalculator = filePathCalculator; FilePathCalculator = filePathCalculator;
} }
public override async Task SaveAsync(BlobProviderSaveArgs args) public override async Task SaveAsync(BlobProviderSaveArgs args)
{ {
var filePath = FilePathCalculator.Calculate(args); var filePath = FilePathCalculator.Calculate(args);
@ -22,22 +24,27 @@ namespace Volo.Abp.BlobStoring.FileSystem
{ {
throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten.");
} }
DirectoryHelper.CreateIfNotExists(Path.GetDirectoryName(filePath)); DirectoryHelper.CreateIfNotExists(Path.GetDirectoryName(filePath));
var fileMode = args.OverrideExisting var fileMode = args.OverrideExisting
? FileMode.Create ? FileMode.Create
: FileMode.CreateNew; : FileMode.CreateNew;
using (var fileStream = File.Open(filePath, fileMode, FileAccess.Write))
{
await args.BlobStream.CopyToAsync(
fileStream,
args.CancellationToken
);
await fileStream.FlushAsync(); await Policy.Handle<IOException>()
} .WaitAndRetryAsync(2, retryCount => TimeSpan.FromSeconds(retryCount))
.ExecuteAsync(async () =>
{
using (var fileStream = File.Open(filePath, fileMode, FileAccess.Write))
{
await args.BlobStream.CopyToAsync(
fileStream,
args.CancellationToken
);
await fileStream.FlushAsync();
}
});
} }
public override Task<bool> DeleteAsync(BlobProviderDeleteArgs args) public override Task<bool> DeleteAsync(BlobProviderDeleteArgs args)
@ -52,21 +59,31 @@ namespace Volo.Abp.BlobStoring.FileSystem
return ExistsAsync(filePath); return ExistsAsync(filePath);
} }
public override Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) public override async Task<Stream> GetOrNullAsync(BlobProviderGetArgs args)
{ {
var filePath = FilePathCalculator.Calculate(args); var filePath = FilePathCalculator.Calculate(args);
if (!File.Exists(filePath)) if (!File.Exists(filePath))
{ {
return Task.FromResult<Stream>(null); return null;
} }
return Task.FromResult<Stream>(File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)); return await Policy.Handle<IOException>()
.WaitAndRetryAsync(2, retryCount => TimeSpan.FromSeconds(retryCount))
.ExecuteAsync(async () =>
{
using (var fileStream = File.OpenRead(filePath))
{
var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream, args.CancellationToken);
return memoryStream;
}
});
} }
protected virtual Task<bool> ExistsAsync(string filePath) protected virtual Task<bool> ExistsAsync(string filePath)
{ {
return Task.FromResult(File.Exists(filePath)); return Task.FromResult(File.Exists(filePath));
} }
} }
} }

21
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/GetSourceCommand.cs

@ -48,16 +48,22 @@ namespace Volo.Abp.Cli.Commands
var outputFolder = GetOutPutFolder(commandLineArgs); var outputFolder = GetOutPutFolder(commandLineArgs);
Logger.LogInformation("Output folder: " + outputFolder); Logger.LogInformation("Output folder: " + outputFolder);
var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long); var gitHubAbpLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubAbpLocalRepositoryPath.Long);
if (gitHubLocalRepositoryPath != null) if (gitHubAbpLocalRepositoryPath != null)
{ {
Logger.LogInformation("GitHub Local Repository Path: " + gitHubLocalRepositoryPath); Logger.LogInformation("GitHub Abp Local Repository Path: " + gitHubAbpLocalRepositoryPath);
}
var gitHubVoloLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubVoloLocalRepositoryPath.Long);
if (gitHubVoloLocalRepositoryPath != null)
{
Logger.LogInformation("GitHub Volo Local Repository Path: " + gitHubVoloLocalRepositoryPath);
} }
commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command); commandLineArgs.Options.Add(CliConsts.Command, commandLineArgs.Command);
await _sourceCodeDownloadService.DownloadAsync( await _sourceCodeDownloadService.DownloadAsync(
commandLineArgs.Target, outputFolder, version, gitHubLocalRepositoryPath, commandLineArgs.Options); commandLineArgs.Target, outputFolder, version, gitHubAbpLocalRepositoryPath, gitHubVoloLocalRepositoryPath, commandLineArgs.Options);
} }
private static string GetOutPutFolder(CommandLineArgs commandLineArgs) private static string GetOutPutFolder(CommandLineArgs commandLineArgs)
@ -117,11 +123,16 @@ namespace Volo.Abp.Cli.Commands
public const string Long = "output-folder"; public const string Long = "output-folder";
} }
public static class GitHubLocalRepositoryPath public static class GitHubAbpLocalRepositoryPath
{ {
public const string Long = "abp-path"; public const string Long = "abp-path";
} }
public static class GitHubVoloLocalRepositoryPath
{
public const string Long = "volo-path";
}
public static class Version public static class Version
{ {
public const string Short = "v"; public const string Short = "v";

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

@ -86,10 +86,16 @@ namespace Volo.Abp.Cli.Commands
Logger.LogInformation("Mobile App: " + mobileApp); Logger.LogInformation("Mobile App: " + mobileApp);
} }
var gitHubLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubLocalRepositoryPath.Long); var gitHubAbpLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubAbpLocalRepositoryPath.Long);
if (gitHubLocalRepositoryPath != null) if (gitHubAbpLocalRepositoryPath != null)
{ {
Logger.LogInformation("GitHub Local Repository Path: " + gitHubLocalRepositoryPath); Logger.LogInformation("GitHub Abp Local Repository Path: " + gitHubAbpLocalRepositoryPath);
}
var gitHubVoloLocalRepositoryPath = commandLineArgs.Options.GetOrNull(Options.GitHubVoloLocalRepositoryPath.Long);
if (gitHubVoloLocalRepositoryPath != null)
{
Logger.LogInformation("GitHub Volo Local Repository Path: " + gitHubVoloLocalRepositoryPath);
} }
var templateSource = commandLineArgs.Options.GetOrNull(Options.TemplateSource.Short, Options.TemplateSource.Long); var templateSource = commandLineArgs.Options.GetOrNull(Options.TemplateSource.Short, Options.TemplateSource.Long);
@ -127,7 +133,8 @@ namespace Volo.Abp.Cli.Commands
databaseProvider, databaseProvider,
uiFramework, uiFramework,
mobileApp, mobileApp,
gitHubLocalRepositoryPath, gitHubAbpLocalRepositoryPath,
gitHubVoloLocalRepositoryPath,
templateSource, templateSource,
commandLineArgs.Options, commandLineArgs.Options,
connectionString connectionString
@ -219,6 +226,7 @@ namespace Volo.Abp.Cli.Commands
sb.AppendLine(" abp new Acme.BookStore -d mongodb -o d:\\my-project"); sb.AppendLine(" abp new Acme.BookStore -d mongodb -o d:\\my-project");
sb.AppendLine(" abp new Acme.BookStore -t module"); sb.AppendLine(" abp new Acme.BookStore -t module");
sb.AppendLine(" abp new Acme.BookStore -t module --no-ui"); sb.AppendLine(" abp new Acme.BookStore -t module --no-ui");
sb.AppendLine(" abp new Acme.BookStore -t console");
sb.AppendLine(" abp new Acme.BookStore -ts \"D:\\localTemplate\\abp\""); sb.AppendLine(" abp new Acme.BookStore -ts \"D:\\localTemplate\\abp\"");
sb.AppendLine(" abp new Acme.BookStore -csf false"); sb.AppendLine(" abp new Acme.BookStore -csf false");
sb.AppendLine(" abp new Acme.BookStore --local-framework-ref --abp-path \"D:\\github\\abp\""); sb.AppendLine(" abp new Acme.BookStore --local-framework-ref --abp-path \"D:\\github\\abp\"");
@ -299,11 +307,16 @@ namespace Volo.Abp.Cli.Commands
public const string Long = "output-folder"; public const string Long = "output-folder";
} }
public static class GitHubLocalRepositoryPath public static class GitHubAbpLocalRepositoryPath
{ {
public const string Long = "abp-path"; public const string Long = "abp-path";
} }
public static class GitHubVoloLocalRepositoryPath
{
public const string Long = "volo-path";
}
public static class Version public static class Version
{ {
public const string Short = "v"; public const string Short = "v";

5
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/Services/SourceCodeDownloadService.cs

@ -25,7 +25,7 @@ namespace Volo.Abp.Cli.Commands.Services
Logger = NullLogger<SourceCodeDownloadService>.Instance; Logger = NullLogger<SourceCodeDownloadService>.Instance;
} }
public async Task DownloadAsync(string moduleName, string outputFolder, string version, string gitHubLocalRepositoryPath, AbpCommandLineOptions options) public async Task DownloadAsync(string moduleName, string outputFolder, string version, string gitHubAbpLocalRepositoryPath, string gitHubVoloLocalRepositoryPath, AbpCommandLineOptions options)
{ {
Logger.LogInformation("Downloading source code of " + moduleName); Logger.LogInformation("Downloading source code of " + moduleName);
Logger.LogInformation("Version: " + version); Logger.LogInformation("Version: " + version);
@ -39,7 +39,8 @@ namespace Volo.Abp.Cli.Commands.Services
DatabaseProvider.NotSpecified, DatabaseProvider.NotSpecified,
UiFramework.NotSpecified, UiFramework.NotSpecified,
null, null,
gitHubLocalRepositoryPath, gitHubAbpLocalRepositoryPath,
gitHubVoloLocalRepositoryPath,
null, null,
options options
) )

25
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Steps/ProjectReferenceReplaceStep.cs

@ -22,10 +22,13 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
return; return;
} }
var localVoloRepoPath = context.BuildArgs.VoloGitHubLocalRepositoryPath;
new ProjectReferenceReplacer.LocalProjectPathReferenceReplacer( new ProjectReferenceReplacer.LocalProjectPathReferenceReplacer(
context.Files, context.Files,
context.Module?.Namespace ?? "MyCompanyName.MyProjectName", context.Module?.Namespace ?? "MyCompanyName.MyProjectName",
localAbpRepoPath localAbpRepoPath,
localVoloRepoPath
).Run(); ).Run();
} }
else else
@ -177,12 +180,14 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
public class LocalProjectPathReferenceReplacer : ProjectReferenceReplacer public class LocalProjectPathReferenceReplacer : ProjectReferenceReplacer
{ {
private readonly string _gitHubLocalRepositoryPath; private readonly string _gitHubAbpLocalRepositoryPath;
private readonly string _gitHubVoloLocalRepositoryPath;
public LocalProjectPathReferenceReplacer(List<FileEntry> entries, string projectName, string gitHubLocalRepositoryPath) public LocalProjectPathReferenceReplacer(List<FileEntry> entries, string projectName, string gitHubAbpLocalRepositoryPath, string gitHubVoloLocalRepositoryPath)
: base(entries, projectName) : base(entries, projectName)
{ {
_gitHubLocalRepositoryPath = gitHubLocalRepositoryPath; _gitHubAbpLocalRepositoryPath = gitHubAbpLocalRepositoryPath;
_gitHubVoloLocalRepositoryPath = gitHubVoloLocalRepositoryPath;
} }
protected override XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue) protected override XmlElement GetNewReferenceNode(XmlDocument doc, string oldNodeIncludeValue)
@ -204,9 +209,17 @@ namespace Volo.Abp.Cli.ProjectBuilding.Building.Steps
includeValue = includeValue.TrimStart('\\'); includeValue = includeValue.TrimStart('\\');
} }
includeValue = _gitHubLocalRepositoryPath.EnsureEndsWith('\\') + includeValue; if (!string.IsNullOrWhiteSpace(_gitHubVoloLocalRepositoryPath))
{
if (includeValue.StartsWith("abp\\", StringComparison.InvariantCultureIgnoreCase))
{
return _gitHubAbpLocalRepositoryPath.EnsureEndsWith('\\') + includeValue.Substring("abp\\".Length);
}
return _gitHubVoloLocalRepositoryPath.EnsureEndsWith('\\') + "abp\\" + includeValue;
}
return includeValue; return _gitHubAbpLocalRepositoryPath.EnsureEndsWith('\\') + includeValue;
} }
} }
} }

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

@ -24,6 +24,9 @@ namespace Volo.Abp.Cli.ProjectBuilding
[CanBeNull] [CanBeNull]
public string AbpGitHubLocalRepositoryPath { get; set; } public string AbpGitHubLocalRepositoryPath { get; set; }
[CanBeNull]
public string VoloGitHubLocalRepositoryPath { get; set; }
[CanBeNull] [CanBeNull]
public string TemplateSource { get; set; } public string TemplateSource { get; set; }
@ -41,6 +44,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
UiFramework uiFramework = UiFramework.NotSpecified, UiFramework uiFramework = UiFramework.NotSpecified,
MobileApp? mobileApp = null, MobileApp? mobileApp = null,
[CanBeNull] string abpGitHubLocalRepositoryPath = null, [CanBeNull] string abpGitHubLocalRepositoryPath = null,
[CanBeNull] string voloGitHubLocalRepositoryPath = null,
[CanBeNull] string templateSource = null, [CanBeNull] string templateSource = null,
Dictionary<string, string> extraProperties = null, Dictionary<string, string> extraProperties = null,
[CanBeNull] string connectionString = null) [CanBeNull] string connectionString = null)
@ -52,6 +56,7 @@ namespace Volo.Abp.Cli.ProjectBuilding
UiFramework = uiFramework; UiFramework = uiFramework;
MobileApp = mobileApp; MobileApp = mobileApp;
AbpGitHubLocalRepositoryPath = abpGitHubLocalRepositoryPath; AbpGitHubLocalRepositoryPath = abpGitHubLocalRepositoryPath;
VoloGitHubLocalRepositoryPath = voloGitHubLocalRepositoryPath;
TemplateSource = templateSource; TemplateSource = templateSource;
ExtraProperties = extraProperties ?? new Dictionary<string, string>(); ExtraProperties = extraProperties ?? new Dictionary<string, string>();
ConnectionString = connectionString; ConnectionString = connectionString;

3
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NuGetPackageTarget.cs

@ -11,6 +11,7 @@
HttpApiClient = 6, HttpApiClient = 6,
Web = 7, Web = 7,
EntityFrameworkCore = 8, EntityFrameworkCore = 8,
MongoDB = 9 MongoDB = 9,
SignalR = 10
} }
} }

4
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/ProjectFinder.cs

@ -41,6 +41,10 @@ namespace Volo.Abp.Cli.ProjectModification
return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi"); return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi");
case NuGetPackageTarget.HttpApiClient: case NuGetPackageTarget.HttpApiClient:
return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Client"); return FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Client");
case NuGetPackageTarget.SignalR:
return FindProjectEndsWith(projectFiles, assemblyNames, ".SignalR") ??
FindProjectEndsWith(projectFiles, assemblyNames, ".Web") ??
FindProjectEndsWith(projectFiles, assemblyNames, ".HttpApi.Host");
default: default:
return null; return null;
} }

1
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs

@ -129,6 +129,7 @@ namespace Volo.Abp.Cli.ProjectModification
targetModuleFolder, targetModuleFolder,
version, version,
null, null,
null,
null null
); );

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

@ -57,11 +57,11 @@ namespace Volo.Abp.Application.Services
public List<string> AppliedCrossCuttingConcerns { get; } = new List<string>(); public List<string> AppliedCrossCuttingConcerns { get; } = new List<string>();
public IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager); protected IUnitOfWorkManager UnitOfWorkManager => LazyGetRequiredService(ref _unitOfWorkManager);
private IUnitOfWorkManager _unitOfWorkManager; private IUnitOfWorkManager _unitOfWorkManager;
protected Type ObjectMapperContext { get; set; } protected Type ObjectMapperContext { get; set; }
public IObjectMapper ObjectMapper protected IObjectMapper ObjectMapper
{ {
get get
{ {
@ -85,31 +85,31 @@ namespace Volo.Abp.Application.Services
public IGuidGenerator GuidGenerator { get; set; } public IGuidGenerator GuidGenerator { get; set; }
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant);
private ICurrentTenant _currentTenant; private ICurrentTenant _currentTenant;
public ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser); protected ICurrentUser CurrentUser => LazyGetRequiredService(ref _currentUser);
private ICurrentUser _currentUser; private ICurrentUser _currentUser;
public ISettingProvider SettingProvider => LazyGetRequiredService(ref _settingProvider); protected ISettingProvider SettingProvider => LazyGetRequiredService(ref _settingProvider);
private ISettingProvider _settingProvider; private ISettingProvider _settingProvider;
public IClock Clock => LazyGetRequiredService(ref _clock); protected IClock Clock => LazyGetRequiredService(ref _clock);
private IClock _clock; private IClock _clock;
public IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService); protected IAuthorizationService AuthorizationService => LazyGetRequiredService(ref _authorizationService);
private IAuthorizationService _authorizationService; private IAuthorizationService _authorizationService;
public IFeatureChecker FeatureChecker => LazyGetRequiredService(ref _featureChecker); protected IFeatureChecker FeatureChecker => LazyGetRequiredService(ref _featureChecker);
private IFeatureChecker _featureChecker; private IFeatureChecker _featureChecker;
public IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory); protected IStringLocalizerFactory StringLocalizerFactory => LazyGetRequiredService(ref _stringLocalizerFactory);
private IStringLocalizerFactory _stringLocalizerFactory; private IStringLocalizerFactory _stringLocalizerFactory;
public IStringLocalizer L protected IStringLocalizer L
{ {
get get
{ {
@ -175,4 +175,4 @@ namespace Volo.Abp.Application.Services
return localizer; return localizer;
} }
} }
} }

12
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Services/DomainService.cs

@ -28,23 +28,23 @@ namespace Volo.Abp.Domain.Services
return reference; return reference;
} }
public IClock Clock => LazyGetRequiredService(ref _clock); protected IClock Clock => LazyGetRequiredService(ref _clock);
private IClock _clock; private IClock _clock;
public IGuidGenerator GuidGenerator { get; set; } public IGuidGenerator GuidGenerator { get; set; }
public ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
private ILoggerFactory _loggerFactory; private ILoggerFactory _loggerFactory;
public ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant); protected ICurrentTenant CurrentTenant => LazyGetRequiredService(ref _currentTenant);
private ICurrentTenant _currentTenant; private ICurrentTenant _currentTenant;
protected ILogger Logger => _lazyLogger.Value; protected ILogger Logger => _lazyLogger.Value;
private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);
protected DomainService() protected DomainService()
{ {
GuidGenerator = SimpleGuidGenerator.Instance; GuidGenerator = SimpleGuidGenerator.Instance;
} }
} }
} }

8
framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/Generators/JQuery/JQueryProxyScriptGenerator.cs

@ -32,6 +32,8 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery
AddModuleScript(script, module); AddModuleScript(script, module);
} }
AddInitializedEventTrigger(script);
return script.ToString(); return script.ToString();
} }
@ -189,6 +191,12 @@ namespace Volo.Abp.Http.ProxyScripting.Generators.JQuery
return result; return result;
} }
private static void AddInitializedEventTrigger(StringBuilder script)
{
script.AppendLine();
script.AppendLine("abp.event.trigger('abp.serviceProxyScriptInitialized');");
}
private static string GetNormalizedTypeName(string typeWithAssemblyName) private static string GetNormalizedTypeName(string typeWithAssemblyName)
{ {

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

@ -17,6 +17,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="IdentityModel" Version="4.1.1" /> <PackageReference Include="IdentityModel" Version="4.1.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.2" /> <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.2" />
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" />
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" /> <ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" />
</ItemGroup> </ItemGroup>

6
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/AbpIdentityModelModule.cs

@ -1,11 +1,13 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.IdentityModel namespace Volo.Abp.IdentityModel
{ {
[DependsOn( [DependsOn(
typeof(AbpThreadingModule) typeof(AbpThreadingModule),
typeof(AbpMultiTenancyModule)
)] )]
public class AbpIdentityModelModule : AbpModule public class AbpIdentityModelModule : AbpModule
{ {
@ -13,7 +15,7 @@ namespace Volo.Abp.IdentityModel
{ {
var configuration = context.Services.GetConfiguration(); var configuration = context.Services.GetConfiguration();
context.Services.AddHttpClient(); context.Services.AddHttpClient(IdentityModelAuthenticationService.HttpClientName);
Configure<AbpIdentityClientOptions>(configuration); Configure<AbpIdentityClientOptions>(configuration);
} }

24
framework/src/Volo.Abp.IdentityModel/Volo/Abp/IdentityModel/IdentityModelAuthenticationService.cs

@ -11,6 +11,7 @@ using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.IdentityModel namespace Volo.Abp.IdentityModel
@ -18,19 +19,23 @@ namespace Volo.Abp.IdentityModel
[Dependency(ReplaceServices = true)] [Dependency(ReplaceServices = true)]
public class IdentityModelAuthenticationService : IIdentityModelAuthenticationService, ITransientDependency public class IdentityModelAuthenticationService : IIdentityModelAuthenticationService, ITransientDependency
{ {
public const string HttpClientName = "IdentityModelAuthenticationServiceHttpClientName";
public ILogger<IdentityModelAuthenticationService> Logger { get; set; } public ILogger<IdentityModelAuthenticationService> Logger { get; set; }
protected AbpIdentityClientOptions ClientOptions { get; } protected AbpIdentityClientOptions ClientOptions { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; } protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IHttpClientFactory HttpClientFactory { get; } protected IHttpClientFactory HttpClientFactory { get; }
protected ICurrentTenant CurrentTenant { get; }
public IdentityModelAuthenticationService( public IdentityModelAuthenticationService(
IOptions<AbpIdentityClientOptions> options, IOptions<AbpIdentityClientOptions> options,
ICancellationTokenProvider cancellationTokenProvider, ICancellationTokenProvider cancellationTokenProvider,
IHttpClientFactory httpClientFactory) IHttpClientFactory httpClientFactory,
ICurrentTenant currentTenant)
{ {
ClientOptions = options.Value; ClientOptions = options.Value;
CancellationTokenProvider = cancellationTokenProvider; CancellationTokenProvider = cancellationTokenProvider;
HttpClientFactory = httpClientFactory; HttpClientFactory = httpClientFactory;
CurrentTenant = currentTenant;
Logger = NullLogger<IdentityModelAuthenticationService>.Instance; Logger = NullLogger<IdentityModelAuthenticationService>.Instance;
} }
@ -46,7 +51,6 @@ namespace Volo.Abp.IdentityModel
SetAccessToken(client, accessToken); SetAccessToken(client, accessToken);
return true; return true;
} }
protected virtual async Task<string> GetAccessTokenOrNullAsync(string identityClientName) protected virtual async Task<string> GetAccessTokenOrNullAsync(string identityClientName)
@ -106,7 +110,7 @@ namespace Volo.Abp.IdentityModel
protected virtual async Task<DiscoveryDocumentResponse> GetDiscoveryResponse( protected virtual async Task<DiscoveryDocumentResponse> GetDiscoveryResponse(
IdentityClientConfiguration configuration) IdentityClientConfiguration configuration)
{ {
using (var httpClient = HttpClientFactory.CreateClient()) using (var httpClient = HttpClientFactory.CreateClient(HttpClientName))
{ {
return await httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest return await httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
{ {
@ -123,8 +127,10 @@ namespace Volo.Abp.IdentityModel
DiscoveryDocumentResponse discoveryResponse, DiscoveryDocumentResponse discoveryResponse,
IdentityClientConfiguration configuration) IdentityClientConfiguration configuration)
{ {
using (var httpClient = HttpClientFactory.CreateClient()) using (var httpClient = HttpClientFactory.CreateClient(HttpClientName))
{ {
AddHeaders(httpClient);
switch (configuration.GrantType) switch (configuration.GrantType)
{ {
case OidcConstants.GrantTypes.ClientCredentials: case OidcConstants.GrantTypes.ClientCredentials:
@ -186,5 +192,15 @@ namespace Volo.Abp.IdentityModel
return Task.CompletedTask; return Task.CompletedTask;
} }
protected virtual void AddHeaders(HttpClient client)
{
//tenantId
if (CurrentTenant.Id.HasValue)
{
//TODO: Use AbpAspNetCoreMultiTenancyOptions to get the key
client.DefaultRequestHeaders.Add(TenantResolverConsts.DefaultTenantKey, CurrentTenant.Id.Value.ToString());
}
}
} }
} }

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

@ -102,7 +102,7 @@ namespace Volo.Abp.UI.Navigation
Name = name; Name = name;
DisplayName = displayName; DisplayName = displayName;
Url = url?.EnsureStartsWith('~'); Url = url;
Icon = icon; Icon = icon;
Order = order; Order = order;
CustomData = customData; CustomData = customData;

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json

@ -58,6 +58,7 @@
"404Message": "الصفحة غير موجودة", "404Message": "الصفحة غير موجودة",
"500Message": "خطأ في الخادم الداخلي", "500Message": "خطأ في الخادم الداخلي",
"GoHomePage": "اذهب الى الصفحة الرئيسية", "GoHomePage": "اذهب الى الصفحة الرئيسية",
"GoBack": "عد" "GoBack": "عد",
"Search": "بحث"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/cs.json

@ -58,6 +58,7 @@
"404Message": "Stránka nenalezena", "404Message": "Stránka nenalezena",
"500Message": "Interní chyba serveru", "500Message": "Interní chyba serveru",
"GoHomePage": "Přejít na domovskou stránku", "GoHomePage": "Přejít na domovskou stránku",
"GoBack": "Jít zpět" "GoBack": "Jít zpět",
"Search": "Vyhledávání"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/en.json

@ -58,6 +58,7 @@
"404Message": "Page not found", "404Message": "Page not found",
"500Message": "Internal Server Error", "500Message": "Internal Server Error",
"GoHomePage": "Go to the homepage", "GoHomePage": "Go to the homepage",
"GoBack": "Go back" "GoBack": "Go back",
"Search" : "Search"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/es.json

@ -54,6 +54,7 @@
"404Message": "La página no existe", "404Message": "La página no existe",
"500Message": "Error interno del servidor", "500Message": "Error interno del servidor",
"GoHomePage": "Ir a la página principal", "GoHomePage": "Ir a la página principal",
"GoBack": "Atras" "GoBack": "Atras",
"Search": "Buscar"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/nl.json

@ -58,6 +58,7 @@
"404Message": "Pagina niet gevonden", "404Message": "Pagina niet gevonden",
"500Message": "Interne Server Fout", "500Message": "Interne Server Fout",
"GoHomePage": "Ga naar de homepage", "GoHomePage": "Ga naar de homepage",
"GoBack": "Ga terug" "GoBack": "Ga terug",
"Search": "Zoeken"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pt-BR.json

@ -54,6 +54,7 @@
"404Message": "Página não encontrada", "404Message": "Página não encontrada",
"500Message": "Erro interno do servidor", "500Message": "Erro interno do servidor",
"GoHomePage": "Voltar à página principal", "GoHomePage": "Voltar à página principal",
"GoBack": "Voltar" "GoBack": "Voltar",
"Search": "Procurar"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ru.json

@ -57,6 +57,7 @@
"404Message": "Страница не найдена", "404Message": "Страница не найдена",
"500Message": "Внутренняя ошибка сервера", "500Message": "Внутренняя ошибка сервера",
"GoHomePage": "Вернуться на главную страницу", "GoHomePage": "Вернуться на главную страницу",
"GoBack": "Вернуться назад" "GoBack": "Вернуться назад",
"Search": "поиск"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/sl.json

@ -58,6 +58,7 @@
"404Message": "Strani ni mogoče najti", "404Message": "Strani ni mogoče najti",
"500Message": "Napaka na strani strežnika", "500Message": "Napaka na strani strežnika",
"GoHomePage": "Pojdi na osnovno stran", "GoHomePage": "Pojdi na osnovno stran",
"GoBack": "Nazaj" "GoBack": "Nazaj",
"Search": "Iskanje"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/tr.json

@ -58,6 +58,7 @@
"404Message": "Sayfa bulunamadı", "404Message": "Sayfa bulunamadı",
"500Message": "Sunucu tarafında hata", "500Message": "Sunucu tarafında hata",
"GoHomePage": "Ana sayfaya git", "GoHomePage": "Ana sayfaya git",
"GoBack": "Geri dön" "GoBack": "Geri dön",
"Search": "Arama"
} }
} }

7
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hans.json

@ -44,7 +44,7 @@
"PagerInfoEmpty": "显示0个条目中的0到0", "PagerInfoEmpty": "显示0个条目中的0到0",
"PagerInfoFiltered": "(从 _MAX_ 总条目中过滤掉)", "PagerInfoFiltered": "(从 _MAX_ 总条目中过滤掉)",
"NoDataAvailableInDatatable": "表中没有数据", "NoDataAvailableInDatatable": "表中没有数据",
"PagerShowMenuEntries": "显示 _MENU_ 实体", "PagerShowMenuEntries": "显示 _MENU_ 条数据",
"DatatableActionDropdownDefaultText": "操作", "DatatableActionDropdownDefaultText": "操作",
"ChangePassword": "修改密码", "ChangePassword": "修改密码",
"PersonalInfo": "个人信息", "PersonalInfo": "个人信息",
@ -55,6 +55,7 @@
"404Message": "网页未找到", "404Message": "网页未找到",
"500Message": "内部服务器错误", "500Message": "内部服务器错误",
"GoHomePage": "返回主页", "GoHomePage": "返回主页",
"GoBack": "返回" "GoBack": "返回",
"Search" : "搜索"
} }
} }

3
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/zh-Hant.json

@ -54,6 +54,7 @@
"404Message": "網頁未找到", "404Message": "網頁未找到",
"500Message": "內部伺服器錯誤", "500Message": "內部伺服器錯誤",
"GoHomePage": "返回首頁", "GoHomePage": "返回首頁",
"GoBack": "返回" "GoBack": "返回",
"Search": "搜索"
} }
} }

12
framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs

@ -5,12 +5,18 @@ namespace Volo.Abp.MongoDB
{ {
public class MongoDbFixture : IDisposable public class MongoDbFixture : IDisposable
{ {
private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); private static readonly MongoDbRunner MongoDbRunner;
public static readonly string ConnectionString = MongoDbRunner.ConnectionString; public static readonly string ConnectionString;
static MongoDbFixture()
{
MongoDbRunner = MongoDbRunner.Start();
ConnectionString = MongoDbRunner.ConnectionString;
}
public void Dispose() public void Dispose()
{ {
MongoDbRunner?.Dispose(); MongoDbRunner?.Dispose();
} }
} }
} }

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/_ViewImports.cshtml

@ -1,2 +0,0 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap

4
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/_ViewImports.cshtml

@ -0,0 +1,4 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling

4
modules/account/src/Volo.Abp.Account.Web/AbpAccountUserMenuContributor.cs

@ -19,8 +19,8 @@ namespace Volo.Abp.Account.Web
var uiResource = context.GetLocalizer<AbpUiResource>(); var uiResource = context.GetLocalizer<AbpUiResource>();
var accountResource = context.GetLocalizer<AccountResource>(); var accountResource = context.GetLocalizer<AccountResource>();
context.Menu.AddItem(new ApplicationMenuItem("Account.Manage", accountResource["ManageYourProfile"], url: "/Account/Manage", icon: "fa fa-cog", order: 1000, null)); context.Menu.AddItem(new ApplicationMenuItem("Account.Manage", accountResource["ManageYourProfile"], url: "~/Account/Manage", icon: "fa fa-cog", order: 1000, null));
context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", uiResource["Logout"], url: "/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000)); context.Menu.AddItem(new ApplicationMenuItem("Account.Logout", uiResource["Logout"], url: "~/Account/Logout", icon: "fa fa-power-off", order: int.MaxValue - 1000));
return Task.CompletedTask; return Task.CompletedTask;
} }

13
modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPage.cs

@ -1,13 +0,0 @@
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Volo.Abp.Account.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
namespace Volo.Abp.Account.Web.Pages.Account
{
public abstract class AccountPage : AbpPage
{
[RazorInject]
public IHtmlLocalizer<AccountResource> L { get; set; }
}
}

4
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml

@ -1,8 +1,10 @@
@page @page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@using Volo.Abp.Account.Settings @using Volo.Abp.Account.Settings
@using Volo.Abp.Settings @using Volo.Abp.Settings
@model Volo.Abp.Account.Web.Pages.Account.LoginModel @model Volo.Abp.Account.Web.Pages.Account.LoginModel
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage @inject IHtmlLocalizer<AccountResource> L
@inject Volo.Abp.Settings.ISettingProvider SettingProvider @inject Volo.Abp.Settings.ISettingProvider SettingProvider
@if (Model.EnableLocalLogin) @if (Model.EnableLocalLogin)
{ {

3
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml

@ -1,3 +1,2 @@
@page "/Account/Logout" @page "/Account/Logout"
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage @model Volo.Abp.Account.Web.Pages.Account.LogoutModel
@model Volo.Abp.Account.Web.Pages.Account.LogoutModel

6
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml

@ -1,11 +1,13 @@
@page @page
@using Microsoft.AspNetCore.Mvc.Localization
@using Volo.Abp.Account.Localization
@using Volo.Abp.Account.Web.Pages.Account @using Volo.Abp.Account.Web.Pages.Account
@using Volo.Abp.Identity.Settings @using Volo.Abp.Identity.Settings
@using Volo.Abp.Settings @using Volo.Abp.Settings
@using Volo.Abp.AspNetCore.Mvc.UI.Theming @using Volo.Abp.AspNetCore.Mvc.UI.Theming
@inject ISettingProvider SettingManager @inject ISettingProvider SettingManager
@inject IThemeManager ThemeManager @inject IThemeManager ThemeManager
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage @inject IHtmlLocalizer<AccountResource> L
@model ManageModel @model ManageModel
@{ @{
Layout = ThemeManager.CurrentTheme.GetApplicationLayout(); Layout = ThemeManager.CurrentTheme.GetApplicationLayout();
@ -54,4 +56,4 @@
</abp-tab> </abp-tab>
</abp-tabs> </abp-tabs>
</abp-card-body> </abp-card-body>
</abp-card> </abp-card>

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

Loading…
Cancel
Save