Browse Source

Merge branch 'dev' into liangshiwei/Eventbus-kafka

pull/5034/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
296fee3931
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/workflows/angular.yml
  2. 3
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  3. 33
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json
  4. 64
      abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json
  5. 32
      abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json
  6. 10
      docs/en/Authentication/Social-External-Logins.md
  7. 38
      docs/en/Background-Jobs-Quartz.md
  8. 263
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/Post.md
  9. BIN
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/email-phone-verification.png
  10. BIN
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/forgot-password.png
  11. BIN
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/ldap-settings-ui.png
  12. BIN
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/my-security-logs.png
  13. BIN
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/reset-password.png
  14. BIN
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/security-logs-ui.png
  15. BIN
      docs/en/Blog-Posts/2020-08-20 v3_1_Release/user-lock.png
  16. 36
      docs/en/CLI.md
  17. 212
      docs/en/Community-Articles/2020-08-12-Patch-Chrome-Login-Issue-For-IdentityServer4/POST.md
  18. 302
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/Using-DevExtreme-In-ABP-Based-Application.md
  19. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/both-example-result.png
  20. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-app-contract.png
  21. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-application.png
  22. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-final.png
  23. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-web.png
  24. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devexp-result.gif
  25. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devextreme-js.png
  26. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/gulp.png
  27. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/initial-project.png
  28. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-app-contract.png
  29. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-application.png
  30. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-final.png
  31. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-web.png
  32. BIN
      docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/wwwroot-lib.png
  33. 12
      docs/en/CurrentUser.md
  34. 5
      docs/en/Customizing-Application-Modules-Extending-Entities.md
  35. 251
      docs/en/Emailing.md
  36. 7
      docs/en/Entity-Framework-Core-Migrations.md
  37. 7
      docs/en/Entity-Framework-Core.md
  38. 11
      docs/en/Exception-Handling.md
  39. 2
      docs/en/Getting-Started-React-Native.md
  40. 9
      docs/en/Getting-Started.md
  41. 3
      docs/en/Global-Features.md
  42. 48
      docs/en/MailKit.md
  43. 2
      docs/en/Module-Development-Basics.md
  44. 3
      docs/en/Module-Entity-Extensions.md
  45. 32
      docs/en/Nightly-Builds.md
  46. 40
      docs/en/Previews.md
  47. 6
      docs/en/Repositories.md
  48. 29
      docs/en/Samples/Index.md
  49. 19
      docs/en/Startup-Templates/Application.md
  50. 4
      docs/en/Startup-Templates/Module.md
  51. 106
      docs/en/Text-Templating.md
  52. 4
      docs/en/Timing.md
  53. 4
      docs/en/UI/Angular/Multi-Tenancy.md
  54. 25
      docs/en/UI/Angular/Permission-Management.md
  55. 2
      docs/en/UI/AspNetCore/Customization-User-Interface.md
  56. 133
      docs/en/Virtual-File-System.md
  57. 17
      docs/en/docs-nav.json
  58. BIN
      docs/en/images/replace-email-layout.png
  59. BIN
      docs/en/images/solution-files-non-mvc.png
  60. 38
      docs/zh-Hans/Background-Jobs-Quartz.md
  61. 4
      docs/zh-Hans/CLI.md
  62. 12
      docs/zh-Hans/CurrentUser.md
  63. 11
      docs/zh-Hans/Exception-Handling.md
  64. 4
      docs/zh-Hans/Getting-Started-React-Native.md
  65. 9
      docs/zh-Hans/Getting-Started.md
  66. 16
      docs/zh-Hans/Startup-Templates/Application.md
  67. 4
      docs/zh-Hans/Startup-Templates/Module.md
  68. 14
      framework/Volo.Abp.sln
  69. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj
  70. 1
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Packages/Volo/Abp/AspNetCore/Mvc/UI/Packages/Luxon/LuxonScriptContributor.cs
  71. 15
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/AbpMvcUiOptions.cs
  72. 1
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  73. 8
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs
  74. 5
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs
  75. 74
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs
  76. 47
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs
  77. 52
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs
  78. 4
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/ObjectExtending/ObjectExtendingPropertyInfoExtensions.cs
  79. 51
      framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs
  80. 7
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs
  81. 9
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs
  82. 3
      framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs
  83. 0
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs
  84. 4
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/NewCommand.cs
  85. 13
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs
  86. 14
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs
  87. 31
      framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs
  88. 269
      framework/src/Volo.Abp.Core/System/Linq/PredicateOperator.cs
  89. 18
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs
  90. 6
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJobArgs.cs
  91. 22
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs
  92. 40
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs
  93. 1
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs
  94. 2
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionInfoExtensions.cs
  95. 2
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionManagerExtensions.cs
  96. 3
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionPropertyInfoExtensions.cs
  97. 17
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoEfCoreMappingOptions.cs
  98. 3
      framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xml
  99. 30
      framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xsd
  100. 3
      framework/src/Volo.Abp.GlobalFeatures/Properties/AssemblyInfo.cs

1
.github/workflows/angular.yml

@ -3,6 +3,7 @@ on:
pull_request:
paths:
- 'npm/ng-packs/**'
- '!npm/ng-packs/scripts/**'
jobs:
build-test-lint:
runs-on: ubuntu-18.04

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

@ -190,6 +190,7 @@
"Enum:Status:1": "Rejected",
"Enum:Status:2": "Approved",
"Summary": "Summary",
"AuthorName": "Author name"
"AuthorName": "Author name",
"CoverImage": "Cover Image"
}
}

33
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json

@ -23,7 +23,7 @@
"UrlContentNotFound": "Url content not found.",
"Summary": "Summary",
"MostRead": "Most Read",
"LatestArticles": "Latest Articles",
"Latest": "Latest",
"ContributeAbpCommunity": "Contribute to the ABP Community",
"SubmitYourArticle": "Submit Your Article",
"ContributionGuide": "Contribution Guide",
@ -36,6 +36,35 @@
"FeatureRequest": "Feature Request",
"CreateArticleTitleInfo": "Title of the article to be shown on the article list.",
"CreateArticleUrlInfo": "Original GitHub/External URL of the article.",
"CreateArticleSummaryInfo": "A short summary of the article to be shown on the article list."
"CreateArticleSummaryInfo": "A short summary of the article to be shown on the article list.",
"CreateArticleCoverInfo": "For creating an effective article, add a cover photo. Only 16:9 aspect ratio pictures will be accepted!",
"ThisExtensionIsNotAllowed": "This extension is not allowed.",
"TheFileIsTooLarge": "The file is too large.",
"GoToTheArticle": "Go to the Article",
"Contribute": "Contribute",
"OverallProgress": "Overall Progress",
"Done": "Done",
"Open": "Open",
"Closed": "Closed",
"LatestQuestionOnThe": "Latest Question On The",
"Stackoverflow": "Stackoverflow",
"Votes": "votes",
"Answer": "Answer",
"Views": "views",
"Answered": "Answered",
"WaitingForYourAnswer": "Waiting for your answer",
"Asked": "asked",
"AllQuestions": "All Questions",
"NextVersion": "Next Version",
"MilestoneErrorMessage": "Could not get the current milestone details from Github.",
"QuestionItemErrorMessage": "Could not get the latest question details from Stackoverflow.",
"Oops": "Oops!",
"CreateArticleSuccessMessage": "The Article has been successfully submitted. It will be published after a review from the site admin.",
"ChooseCoverImage": "Choose a cover image...",
"PictureUploadedIsNotInExpectedAspectRatio": "The picture you uploaded is not in 16:9 aspect ratio!",
"HeightAndWidthMustNotExceed": "Height and Width must not exceed 1920*1080.",
"CoverImage": "Cover Image",
"ShareYourExperiencesWithTheABPFramework": "Share your experiences with the ABP Framework!",
"Optional": "Optional"
}
}

64
abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/zh-Hans.json

@ -0,0 +1,64 @@
{
"culture": "zh-Hans",
"texts": {
"Permission:CommunityArticle": "社区文章",
"Permission:Edit": "修改",
"Waiting": "等待中",
"Approved": "已批准",
"Rejected": "已拒绝",
"Wait": "等待",
"Approve": "批准",
"Reject": "拒绝",
"ReadArticle": "阅读文章",
"Status": "状态",
"ContentSource": "内容来源",
"Details": "详情",
"Url": "url",
"Title": "标题",
"CreationTime": "创建时间",
"Save": "保存",
"SameUrlAlreadyExist": "url已存在,如果你想要添加这篇文章,你需要更改url!",
"UrlIsNotValid": "Url无效.",
"UrlNotFound" : "Url未找到.",
"UrlContentNotFound": "Url内容未找到.",
"Summary": "摘要",
"MostRead": "阅读最多",
"Latest": "最新",
"ContributeAbpCommunity": "为ABP社区做贡献",
"SubmitYourArticle": "提交你的文章",
"ContributionGuide": "贡献指南",
"BugReport": "Bug报告",
"SeeAllArticles": "查看所有的文章",
"WelcomeToABPCommunity!": "欢迎来到ABP社区!",
"MyProfile": "我的资料",
"MyOrganizations": "我的组织",
"EmailNotValid": "请输入有效的电子邮箱地址.",
"FeatureRequest": "功能请求",
"CreateArticleTitleInfo": "文章标题显示在文章列表中.",
"CreateArticleUrlInfo": "文章的原始GitHub/外部URL.",
"CreateArticleSummaryInfo": "文章的简短摘要将显示在文章列表中.",
"CreateArticleCoverInfo": "为了创建有效的文章,请添加封面图. 仅支持16:9的图片!",
"ThisExtensionIsNotAllowed": "不允许此扩展名.",
"TheFileIsTooLarge": "文件过大.",
"GoToTheArticle": "转到文章",
"Contribute": "贡献",
"OverallProgress": "总体流程",
"Done": "完成",
"Open": "打开",
"Closed": "关闭",
"LatestQuestionOnThe": "有关的最新问题",
"Stackoverflow": "Stackoverflow",
"Votes": "票数",
"Answer": "回答",
"Views": "观看次数",
"Answered": "已回答",
"WaitingForYourAnswer": "等待你的回答",
"Asked": "提问",
"AllQuestions": "所有的问题",
"NextVersion": "下一个版本",
"MilestoneErrorMessage": "无法从Github获取当前的里程碑详细信息.",
"QuestionItemErrorMessage": "无法从Stackoverflow获取最新的问题详细信息.",
"Oops": "哎呀!"
}
}

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

@ -15,7 +15,7 @@
"Tutorial": "Tutorial",
"UsingCLI": "Using CLI",
"SeeDetails": "See Details",
"AbpShortDescription": "ABP is a complete architecture and strong infrastructure to create modern web applications! Follows best practices and conventions to provide you a SOLID development experience.",
"AbpShortDescription": "ABP Framework is a complete infrastructure to create modern web applications by following the software development best practices and conventions.",
"SourceCodeUpper": "SOURCE CODE",
"LatestReleaseLogs": "Latest release logs",
"Infrastructure": "Infrastructure",
@ -31,31 +31,31 @@
"MultiTenancy": "Multi-Tenancy",
"MultiTenancyExplanationShort": "SaaS applications made easy! Integrated multi-tenancy from database to UI.",
"CrossCuttingConcerns": "Cross Cutting Concerns",
"CrossCuttingConcernsExplanationShort": "Complete infrastructure for authorization, validation, exception handling, caching, audit logging, transaction management and so on.",
"CrossCuttingConcernsExplanationShort": "Complete infrastructure for authorization, validation, exception handling, caching, audit logging, transaction management and more.",
"BuiltInBundlingMinification": "Built-In Bundling & Minification",
"BuiltInBundlingMinificationExplanation": "Stop to use external tools for bundling & minification. ABP offers a simpler, dynamic, powerful, modular and built-in way!",
"BuiltInBundlingMinificationExplanation": "No need to use external tools for bundling & minification. ABP offers a simpler, dynamic, powerful, modular and built-in way!",
"VirtualFileSystem": "Virtual File System",
"VirtualFileSystemExplanation": "Embed views, scripts, styles, images... into packages/libraries and reuse in different applications.",
"VirtualFileSystemExplanation": "Embed views, scripts, styles, images... into packages/libraries and reuse them in different applications.",
"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.",
"BootstrapTagHelpersDynamicForms": "Bootstrap Tag Helpers & Dynamic Forms",
"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.",
"BootstrapTagHelpersDynamicFormsExplanation": "Instead of manually writing the repeating details of bootstrap components, Use ABP's tag helpers to simplify it and take advantage of intellisense. Quickly build UI forms based on a C# model using the dynamic form tag helper.",
"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 them with dynamic JavaScript and C# proxies.",
"CompleteArchitectureInfo": "Modern architecture to create maintainable software solutions.",
"DomainDrivenDesignBasedLayeringModelExplanation": "Helps you to implement a DDD based layered architecture and build a maintainable code base.",
"DomainDrivenDesignBasedLayeringModelExplanationCont": "Provides startup templates, abstractions, base classes, services, documentation and guides to help you to develop your application based on DDD patterns & principles.",
"MicroserviceCompatibleModelExplanation": "The core framework & pre-build modules are designed the microservice architecture in mind.",
"MicroserviceCompatibleModelExplanationCont": "Provides infrastructure, integrations, samples and documentation to implement microservice solutions easier, while it doesn\u2019t bring additional complexity if you want a monolithic application.",
"ModularInfo": "ABP provides complete modularity system to allow you to develop reusable application modules.",
"ModularInfo": "ABP provides a module system that allows you to develop reusable application modules, tie into application lifecycle events, and express dependencies between core parts of your system.",
"PreBuiltModulesThemes": "Pre-Built Modules & Themes",
"PreBuiltModulesThemesExplanation": "Open source and commercial modules & themes are ready to use in your business application.",
"NuGetNPMPackages": "NuGet & NPM Packages",
"NuGetNPMPackagesExplanation": "Distributed as NuGet & NPM packages. Easy to install and upgrade.",
"ExtensibleReplaceable": "Extensible/Replaceable",
"ExtensibleReplaceableExplanation": "All services & modules are designed extensibility in mind. You can replace services, pages, styles, components...",
"CrossCuttingConcernsExplanation2": "Keep your code cleaner and focus on your own business code.",
"CrossCuttingConcernsExplanation3": "Don\u2019t send time to implement common application requirements again and again.",
"ExtensibleReplaceableExplanation": "All services & modules are designed extensibility in mind. You can replace services, pages, styles and components.",
"CrossCuttingConcernsExplanation2": "Keep your codebase smaller so you can maintain focus on the code that’s specific to your business.",
"CrossCuttingConcernsExplanation3": "Don\u2019t send time implementing common application requirements on multiple projects.",
"AuthenticationAuthorization": "Authentication & Authorization",
"ExceptionHandling": "Exception Handling",
"Validation": "Validation",
@ -76,14 +76,14 @@
"BaseClasses": "Base Classes",
"BaseClassesExplanation": "Pre-built base classes for common application patterns.",
"DeveloperFocusedExplanation": "ABP is for developers.",
"DeveloperFocusedExplanationCont": "It aims to simplify your daily software development while not restricting you to work low level when you need it.",
"DeveloperFocusedExplanationCont": "It aims to simplify your daily software development while not restricting you from writing low level code.",
"SeeAllFeatures": "See All Features",
"CLI_CommandLineInterface": "CLI (Command Line Interface)",
"CLI_CommandLineInterfaceExplanation": "CLI automates to create new projects and add modules to your application.",
"CLI_CommandLineInterfaceExplanation": "Includes a CLI to help you automate the creation of new projects and the addition of new modules.",
"StartupTemplates": "Startup Templates",
"StartupTemplatesExplanation": "Various startup templates provide you fully configured solution to jump start your development.",
"StartupTemplatesExplanation": "Various startup templates provide a fully configured solution to jump start your development.",
"BasedOnFamiliarTools": "Based on Familiar Tools",
"BasedOnFamiliarToolsExplanation": "Built on and integrated to popular tools you already know. Low learning curve, easy adaptation, comfortable development.",
"BasedOnFamiliarToolsExplanation": "Built on and integrated with popular tools you already know. Low learning curve, easy adaptation, comfortable development.",
"ORMIndependent": "ORM Independent",
"ORMIndependentExplanation": "The core framework is ORM/database independent and can work with any data source. Entity Framework Core and MongoDB providers are already available.",
"Features": "Explore the ABP Framework Features",
@ -158,4 +158,4 @@
"Mobile": "Mobile",
"ReactNative": "React Native"
}
}
}

10
docs/en/Authentication/Social-External-Logins.md

@ -1,10 +1,8 @@
# Social/External Logins
## ASP.NET Core MVC / Razor Pages UI
The [Account Module](../Modules/Account.md) has already configured to handle social or external logins out of the box. You can follow the ASP.NET Core documentation to add a social/external login provider to your application.
### Example: Facebook Authentication
## Example: Facebook Authentication
Follow the [ASP.NET Core Facebook integration document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to support the Facebook login for your application.
@ -27,4 +25,8 @@ context.Services.AddAuthentication()
});
````
> It would be a better practice to use the `appsettings.json` or the ASP.NET Core User Secrets system to store your credentials, instead of a hard-coded value like that. Follow the [Microsoft's document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to learn the user secrets usage.
> It would be a better practice to use the `appsettings.json` or the ASP.NET Core User Secrets system to store your credentials, instead of a hard-coded value like that. Follow the [Microsoft's document](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/facebook-logins) to learn the user secrets usage.
## Angular UI
Beginning from the v3.1, the Angular UI uses authorization code flow (as a best practice) to authenticate the user by redirecting to the MVC UI login page. So, even if you are using the Angular UI, social/external login integration is same as explained above and it will work out of the box.

38
docs/en/Background-Jobs-Quartz.md

@ -70,7 +70,43 @@ public class YourModule : AbpModule
}
````
Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html).
Starting from ABP 3.1 version, we have added `Configurator` to `AbpQuartzOptions` to configure Quartz. For example:
````csharp
[DependsOn(
//...other dependencies
typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
PreConfigure<AbpQuartzOptions>(options =>
{
options.Configurator = configure =>
{
configure.UsePersistentStore(storeOptions =>
{
storeOptions.UseProperties = true;
storeOptions.UseJsonSerializer();
storeOptions.UseSqlServer(configuration.GetConnectionString("Quartz"));
storeOptions.UseClustering(c =>
{
c.CheckinMisfireThreshold = TimeSpan.FromSeconds(20);
c.CheckinInterval = TimeSpan.FromSeconds(10);
});
});
};
});
}
}
````
> You can choose the way you favorite to configure Quaratz.
Quartz stores job and scheduling information **in memory by default**. In the example, we use the pre-configuration of [options pattern](Options.md) to change it to the database. For more configuration of Quartz, please refer to the Quartz's [documentation](https://www.quartz-scheduler.net/).
## Exception handling

263
docs/en/Blog-Posts/2020-08-20 v3_1_Release/Post.md

@ -0,0 +1,263 @@
# ABP Framework v3.1 RC Has Been Released
Today, we are releasing the **ABP Framework version 3.1 Release Candidate** (RC). The development cycle for this version was **~7 weeks**. It was the longest development cycle for a feature version release ever. We have completed **~150 issues**, merged **~150 PRs** and made **~1,000 commits** only in the main [abp repository](https://github.com/abpframework/abp). See the related [milestone](https://github.com/abpframework/abp/milestone/38?closed=1) on GitHub.
There were two main reasons of this long development cycle;
* We've switched to **4-weeks** release cycle (was discussed in [this issue](https://github.com/abpframework/abp/issues/4692)).
* We've [re-written](https://github.com/abpframework/abp/issues/4881) the Angular service proxy generation system using the Angular schematics to make it more stable. There were some problems with the previous implementation.
This long development cycle brings a lot of new features, improvements and bug fixes. I will highlight the fundamental features and changes in this blog post.
## About the Preview/Stable Version Cycle
As mentioned above, it is planned to release a new stable feature version (like 3.1, 3.2, 3.3...) in every 4-weeks.
In addition, we are starting to deploy a **preview version** 2-weeks before the stable versions for every feature/major releases.
Today, we've released `3.1.0-rc.1` as the first preview version. We may release more previews if it is needed until the stable 3.1.0 version.
**The stable `3.1.0` version will be released on September 3, 2020.** Next RC version, `3.2.0-rc.1`, is planned for September 17, 2020 (2 weeks after the stable 3.1.0 and 2 weeks before the stable 3.2.0).
We **won't add new features** to a version after publishing the preview version. We only will make **bug fixes** until the stable version. The new features being developed in this period will be available in the next version.
> We will use `-rc.x` suffix (like `3.1.0-rc.1` and `3.1.0-rc.2`) for preview releases. However, we may also publish with `-preview.x` suffix before RC (Release Candidate) releases, especially for major versions (like 4.0, 5.0...).
### About the Nightly Builds
Don't confuse preview versions vs nightly builds. When we say preview, we are mentioning the preview system explained above.
We will continue to publish **nightly builds** for all the [ABP Framework packages](https://abp.io/packages). Nightly pages are built from the development branch. You can refer to [this document](https://docs.abp.io/en/abp/latest/Nightly-Builds) to learn how to use the nightly packages.
## Get Started with the RC Versions
Please try the preview versions and provide feedback to us to release more stable versions. Please open an issue on the [GitHub repository](https://github.com/abpframework/abp/issues/new) if you find a bug or want to give feedback.
### Update the ABP CLI to the 3.1.0-rc.1
Since this is the first preview version, you need to upgrade the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) to the `3.1.0-rc.1` to be able to use the preview features:
````bash
dotnet tool update Volo.Abp.Cli -g --version 3.1.0-rc.1
````
### New Solutions
The [ABP.IO](https://abp.io/) platform and the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) are compatible with the RC system. You can select the "preview" option on the [download page](https://abp.io/get-started) or use the "**--preview**" parameter with the ABP CLI [new](https://docs.abp.io/en/abp/latest/CLI?_ga=2.106435654.411298747.1597771169-1910388957.1594128976#new) command:
````bash
abp new Acme.BookStore --preview
````
This command will create a new project with the latest RC/Preview version. Whenever the stable version is released, you can switch to the stable version for your solution using the `abp switch-to-stable` command in the root folder of your solution.
### Existing Solutions
If you already have a solution and want to use/test the latest RC/Preview version, use the `abp switch-to-preview` command in the root folder of your solution. You can return back to the latest stable using the `abp switch-to-stable ` command later.
> Note that the `abp switch-to-preview` command was being used to switch to nightly builds before the v3.1. Now, you should use the `abp switch-to-nightly` for [nightly builds](https://docs.abp.io/en/abp/latest/Nightly-Builds).
## Breaking Changes / Special Notes
### ABP & ABP Commercial
* If you are using **EF Core**, you may need to **add a new migration** after upgrading the packages. Just run the standard "Add-Migration" command, check the generated migration code and execute the "Update-Database" command to apply changes to the database.
* If you have implemented **social/external logins** for your MVC / Razor Page UI application before, you may want to check [this issue](https://github.com/abpframework/abp/issues/4981). We made some improvements and changes that you may want to take action for your application. Beginning from v3.1, the users created their accounts via social login can still set a local password to login with local username/email & password.
### ABP Commercial Only
* We've **moved favicons** into `/wwwroot/images/favicon/` folder for the ASP.NET Core **MVC / Razor Page UI** applications. There are 10 favicon related files (including the `favicon.ico`) under this directory to better work with different browser and cases. You can create a new application to check this folder and copy the files into your own application. Then you can customize the icons for your own brand (hint: you can use a tool [like that](https://realfavicongenerator.net/) to create the favicons with various formats).
* Removed direct **Twitter & Facebook social login integrations** from the [account module](https://commercial.abp.io/modules/Volo.Account.Pro), for **MVC / Razor Pages UI**. Follow [this documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Authentication/Social-External-Logins.md) to easily add social logins to your applications if you need. The account module provides all the infrastructure to handle social/external logins, you just need to configure it.
## What's New with the ABP Framework 3.1 RC.1
### Angular Service Proxies
ABP provides a system to generate Angular service proxies (with TypeScript) to consume the HTTP APIs of your application. Service proxy generation system **has been completely re-written** with the ABP Framework 3.1. The main goal was to build more stable and feature rich system that is better aligned with other ABP Framework features (like [modularity](https://docs.abp.io/en/abp/latest/Module-Development-Basics)).
[See the documentation](https://docs.abp.io/en/abp/latest/UI/Angular/Service-Proxies) to learn more about the service proxy generation for Angular applications.
### Authorization Code Flow for the Angular UI
We were using the **resource owner password authentication** flow for the Angular UI login page. We've implemented **Authorization Code Flow** for the Angular account module and made it **default for new projects**. With this change, the Angular application now redirects to the login page of the MVC UI which was implemented using the Identity Server 4. We also removed the client secret from the Angular side with this change.
Old behavior remains exist. If you want to switch to the new flow (which is recommended), follow the steps below:
1) Add `authorization_code` to the `IdentityServerClientGrantTypes` table in the database, for the client used by the Angular UI (the `ClientId` is `YourProjectName_App` by default, in the `IdentityServerClients` table).
2) Add `http://localhost:4200` to `IdentityServerClientRedirectUris` and `IdentityServerClientPostLogoutRedirectUris` tables for the same client.
3) Set `RequireClientSecret` to `false` in the `IdentityServerClients` table for the same client.
> [ABP Commercial](https://commercial.abp.io/) users can make these changes on the [Identity Server Management UI](https://commercial.abp.io/modules/Volo.Identityserver.Ui).
4) Change the `oAuthConfig` section in the `src/environments/environment.ts` file of the Angular application.
You can take [this new configuration](https://gist.github.com/hikalkan/e7f6ae7f507b201783682dccaeadc5e3) as a reference. Main changes are;
* Added `responseType` as `code`.
* Added `redirectUri`
* Added `offline_access` to the `scope`.
* Removed `oidc: false` option.
* Removed the client secret option.
### Global Feature System
The new "Global Features" system allows to **enable/disable features of an application or a module** in a central point. It is especially useful if you want to use a module but don't want to bring all its features into your application. If the module was so designed, you can enable only the features you need.
When you disable a feature;
* The **database tables** related to that feature should not be created in the database.
* The **HTTP APIs** related to that feature should not be exposed. They returns 404 if they are directly requested.
So, the goal is that; when you disable a feature, it should behave like that feature doesn't exists in your system at all.
There is **no way to enable/disable a global feature on runtime**. You should decide it in the development time (remember, even database tables are not being created for disabled global features, so you can't enable it on runtime).
> "Global Features" system is different than [SaaS/multi-tenancy features](https://docs.abp.io/en/abp/latest/Features), where you can enable/disable features for your tenants on runtime.
Assume that you are using the [CMS Kit module](https://github.com/abpframework/abp/tree/dev/modules/cms-kit) (this module is in a very early stage) where you only want to enable the comment feature:
````csharp
GlobalFeatureManager.Instance.Modules.CmsKit().Comments.Enable();
````
You can check if a feature was enabled:
```csharp
GlobalFeatureManager.Instance.IsEnabled<CommentsFeature>();
```
Or you can add `[RequiresGlobalFeature(...)]` attribute to a controller/page to disable it if the related feature was disabled:
```csharp
//...
[RequiresGlobalFeature(typeof(CommentsFeature))]
public class CommentController : AbpController
{
//...
}
```
See the issue [#5061](https://github.com/abpframework/abp/issues/5061) until this is fully documented.
### Social/External Logins
Implemented the infrastructure for social/external logins in the account module. So, now you can easily configure your application to support social/external logins by [following the documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Authentication/Social-External-Logins.md). Once you configure a provider, a button will appear on the login page to use this provider.
The social logins will work as expected even if you are using the Angular UI, since the Angular UI uses the MVC login using the authorization code flow implemented with this new version (as explained above).
### Forgot/Reset Password
Implemented forgot password / password reset for the account module.
You can now enter your email address to get an email containing a **password reset link**:
![forgot-password](forgot-password.png)
When you click to the link, you are redirected to a password reset page to determine your new password:
![reset-password](reset-password.png)
### External Login System
The standard Social/External Login system (like Facebook login) works via OpenID Connect. That means the user is redirected to the login provider, logins there and redirected to your application.
While this is pretty nice for most scenarios, sometimes you want a simpler external login mechanism: User enters username & password in your own application's login form and you check the username & password from another source, not from your own database.
ABP v3.1 introduces an External Login System to check username & password from any source (from an external database, a REST service or from an LDAP / Active Directory server).
You can check the [issue #4977](https://github.com/abpframework/abp/issues/4977#issuecomment-670006297) until it is fully documented.
We've implemented LDAP authentication for the ABP Commercial, using this new login extension system (see the ABP Commercial section below).
### User Security Logs
The new [Security Log System](https://github.com/abpframework/abp/issues/4492) (of the Identity module) automatically logs all authentication related operations (login, logout, change password...) to a `AbpSecurityLogs` table in the database.
### New BLOB Storage Providers
Implemented [AWS](https://github.com/abpframework/abp/blob/dev/docs/en/Blob-Storing-Aws.md) and [Aliyun](https://github.com/abpframework/abp/blob/dev/docs/en/Blob-Storing-Aliyun.md) providers for the [BLOB storing](https://docs.abp.io/en/abp/latest/Blob-Storing) system with this version.
### Module Entity Extensibility
We had introduced a entity extension system that allows to add new properties to existing entities of depended modules by a simple configuration. When you add a new property, it appears on the create, edit and list views on the UI and created a new field in the related database table. We've implemented this system for the identity and tenant management modules, so you can extend entities of these modules. See [the documentation](https://github.com/abpframework/abp/blob/dev/docs/en/Module-Entity-Extensions.md).
### Other Features / Highlights
Here, some other highlights from this release;
* UOW level caching system [#4796](https://github.com/abpframework/abp/issues/4796)
* Refactored the console application template to better integrate to the host builder [#5006](https://github.com/abpframework/abp/issues/5006)
* [Volo.Abp.Ldap](https://www.nuget.org/packages/Volo.Abp.Ldap) package now supports multi-tenancy.
* Introduce `BasicAggregateRoot` base class [#4808](https://github.com/abpframework/abp/issues/4808)
* Sets GUID Id in the `InsertAsync` method of the EF Core repository if it was not set by the developer [#4634](https://github.com/abpframework/abp/pull/4634)
* Added `GetPagedListAsync` methods to the repository to simplify paging [#4617](https://github.com/abpframework/abp/pull/4617)
* Configured [Prettier](https://prettier.io/) for the startup template [#4318](https://github.com/abpframework/abp/issues/4318)
* Defined new layout hooks for the MVC UI: before page content & after page content [#4008](https://github.com/abpframework/abp/issues/4008)
* Allow to put static resources (js, css... files) under the Components folder for ASP.NET Core MVC UI.
* Upgraded to AutoMapper 10 and Quartz 3.1 for the related integration packages.
## What's New with the ABP Commercial v3.1 RC.1
### Security Logs UI
We've created a UI to report user security logs for authentication related operations, under the Identity Management menu:
![security-logs-ui](security-logs-ui.png)
Also, every user can see his/her own security logs by selecting the "My security logs" under the user menu:
![my-security-logs](my-security-logs.png)
### LDAP Authentication
We've implemented LDAP authentication using the new external login system explained above. Also, created a UI to configure the server settings:
![ldap-settings-ui](ldap-settings-ui.png)
In this way, you can simply check passwords of the users from LDAP in the login page. If given username / password doesn't exists on LDAP, then it fallbacks to the local database, just like before.
Since it supports **multi-tenancy**, you can enable, disable and configure it for your tenants.
### Email / Phone Number Verification
User profile management page now supports to Email & Phone Number verification flow:
![email-phone-verification](email-phone-verification.png)
When user clicks to the **verify** button, a verification email/SMS (that has a verification code) sent to the user and the UI waits to submit this code.
### User Lock
Implemented to **lock a user** for a given period of time. Locked users can not login to the application for the given period of time:
![user-lock](user-lock.png)
### ABP Suite: Angular UI Code Generation Revisited
Angular UI code generation has been re-written using the Angular Schematics for the ABP Suite. It is now more stable and produces a better application code.
ABP Suite also supports code generation on module development.
### Others
* **Social logins** and **authorization code flow** are also implemented for the ABP Commercial, just as described above.
* Added breadcrumb and file icons for the **file management module**.
## The ABP Community
We've lunched the [community.abp.io](https://community.abp.io/) ~two weeks ago with its initial version. It only has "Article submission" system for now. We are developing new exciting features. There will be an update in a few days and we'll publish a new blog post for it.
## Conclusion
The main goals of the 3.1 version were;
* Complete the missing **authentication features** (like social logins, LDAP authentication, authorization code flow for the Angular UI...) for the ABP Framework & ABP Commercial.
* Re-write a stable and feature complete **Angular service proxy generation** system for the ABP Framework and CRUD UI generation system for the ABP Commercial.
* Develop a system to lunch **preview versions** of the platform. `3.1.0-rc.1` is the first preview version that has been published with this new system.
* Complete the fundamental **documentation & tutorials** (we've even created a [video tutorial series](https://www.youtube.com/watch?v=cJzyIFfAlp8&list=PLsNclT2aHJcPNaCf7Io3DbMN6yAk_DgWJ)).
ABP.IO platform will be more mature & stable with the v3.1. Enjoy Coding!

BIN
docs/en/Blog-Posts/2020-08-20 v3_1_Release/email-phone-verification.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/en/Blog-Posts/2020-08-20 v3_1_Release/forgot-password.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
docs/en/Blog-Posts/2020-08-20 v3_1_Release/ldap-settings-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
docs/en/Blog-Posts/2020-08-20 v3_1_Release/my-security-logs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/en/Blog-Posts/2020-08-20 v3_1_Release/reset-password.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/en/Blog-Posts/2020-08-20 v3_1_Release/security-logs-ui.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

BIN
docs/en/Blog-Posts/2020-08-20 v3_1_Release/user-lock.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

36
docs/en/CLI.md

@ -26,7 +26,8 @@ Here, the list of all available commands before explaining their details:
* **`add-package`**: Adds an ABP package to a project.
* **`add-module`**: Adds a [multi-package application module](https://docs.abp.io/en/abp/latest/Modules/Index) to a solution.
* **`generate-proxy`**: Generates client side proxies to use HTTP API endpoints on the server.
* **`switch-to-preview`**: Switches to the latest [nightly builds](Nightly-Builds.md) of the ABP related packages on a solution.
* **`switch-to-preview`**: Switches to the latest preview version of the ABP Framework.
* **`switch-to-nightly`**: Switches to the latest [nightly builds](Nightly-Builds.md) of the ABP related packages on a solution.
* **`switch-to-stable`**: Switches to the latest stable versions of the ABP related packages on a solution.
* **`translate`**: Simplifies to translate localization files when you have multiple JSON [localization](Localization.md) files in a source control repository.
* **`login`**: Authenticates on your computer with your [abp.io](https://abp.io/) username and password.
@ -79,8 +80,7 @@ abp new Acme.BookStore
* `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `none`: Without UI. There are some additional options for this template:
* `--separate-identity-server`: Separates the identity server application from the API host application. If not specified, you will have a single endpoint in the server side.
* `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks:
* `none`: no mobile application.
* `--mobile` or `-m`: Specifies the mobile application framework. If not specified, no mobile application will be created. Available options:
* `react-native`: React Native.
* `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers:
* `ef`: Entity Framework Core.
@ -90,7 +90,7 @@ abp new Acme.BookStore
* **`console`**: [Console template](Startup-Templates/Console.md).
* `--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.
* `--preview`: Use latest pre-release version (Only if `--version ` is not specified and there is at least one pre-release after latest stable version).
* `--preview`: Use latest preview 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:\local-template` or `https://.../my-template-file.zip`).
* `--create-solution-folder` or `-csf`: Specifies if the project will be in a new folder in the output folder or directly the output folder.
* `--connection-string` or `-cs`: Overwrites the default connection strings in all `appsettings.json` files. The default connection string is `Server=localhost;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true` for EF Core and it is configured to use the SQL Server. If you want to use the EF Core, but need to change the DBMS, you can change it as [described here](Entity-Framework-Core-Other-DBMS.md) (after creating the solution).
@ -111,7 +111,6 @@ abp update [options]
#### Options
* `--include-previews` or `-p`: Includes preview, beta and rc packages while checking the latest versions.
* `--npm`: Only updates NPM packages.
* `--nuget`: Only updates NuGet packages.
* `--solution-path` or `-sp`: Specify the solution path. Use the current directory by default
@ -197,7 +196,22 @@ abp generate-proxy --apiUrl https://localhost:44305 --ui angular --module all
### switch-to-preview
You can use this command to switch your project to latest **nightly** preview version of the ABP framework packages.
You can use this command to switch your project to latest preview version of the ABP framework.
Usage:
````bash
abp switch-to-preview [options]
````
#### Options
`--solution-directory` or `-sd`: Specifies the directory. The solution should be in that directory or in any of its sub directories. If not specified, default is the current directory.
### switch-to-nightly
You can use this command to switch your project to latest [nightly](Nightly-Builds.md) preview version of the ABP framework packages.
Usage:
@ -211,7 +225,7 @@ abp switch-to-nightly [options]
### switch-to-stable
If you're using the ABP Framework preview packages, you can switch back to latest stable version using this command.
If you're using the ABP Framework preview packages (including nightly previews), you can switch back to latest stable version using this command.
Usage:
@ -274,11 +288,13 @@ Then review changes on your source control system to be sure that it has changed
Some features of the CLI requires to be logged in to abp.io platform. To login with your username write:
```bash
abp login <username> # Asks password separately
abp login <username> -p <password> # Specify the password as a parameter
abp login <username> # Allows you to enter your password hidden
abp login <username> -p <password> # Specify the password as a parameter (password is visible)
abp login <username> --organization <organization> # If you have multiple organizations, you need set your active organization
abp login <username> -p <password> -o <organization> # You can enter both your password and organization in the same command
```
> Using `-p` parameter might not be safe if someone is watching your screen :) It can be useful for automation purposes.
> When using the -p parameter, be careful as your password will be visible. It's useful for CI/CD automation pipelines.
A new login with an already active session overwrites the previous session.

212
docs/en/Community-Articles/2020-08-12-Patch-Chrome-Login-Issue-For-IdentityServer4/POST.md

@ -0,0 +1,212 @@
# How to fix the Chrome login issue for the IdentityServer4
## Introduction
When you use HTTP on your Identity Server 4 enabled website, users may not login because of the changes made by Chrome in the version 8x. This occurs when you use HTTP schema in your website. The issue is explained here https://docs.microsoft.com/en-gb/dotnet/core/compatibility/3.0-3.1#http-browser-samesite-changes-impact-authentication
## How to solve it?
### Step-1
Create the below extension in your ***.Web** project.
```csharp
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace Microsoft.Extensions.DependencyInjection
{
public static class SameSiteCookiesServiceCollectionExtensions
{
/// <summary>
/// -1 defines the unspecified value, which tells ASPNET Core to NOT
/// send the SameSite attribute. With ASPNET Core 3.1 the
/// <seealso cref="SameSiteMode" /> enum will have a definition for
/// Unspecified.
/// </summary>
private const SameSiteMode Unspecified = (SameSiteMode)(-1);
/// <summary>
/// Configures a cookie policy to properly set the SameSite attribute
/// for Browsers that handle unknown values as Strict. Ensure that you
/// add the <seealso cref="Microsoft.AspNetCore.CookiePolicy.CookiePolicyMiddleware" />
/// into the pipeline before sending any cookies!
/// </summary>
/// <remarks>
/// Minimum ASPNET Core Version required for this code:
/// - 2.1.14
/// - 2.2.8
/// - 3.0.1
/// - 3.1.0-preview1
/// Starting with version 80 of Chrome (to be released in February 2020)
/// cookies with NO SameSite attribute are treated as SameSite=Lax.
/// In order to always get the cookies send they need to be set to
/// SameSite=None. But since the current standard only defines Lax and
/// Strict as valid values there are some browsers that treat invalid
/// values as SameSite=Strict. We therefore need to check the browser
/// and either send SameSite=None or prevent the sending of SameSite=None.
/// Relevant links:
/// - https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1
/// - https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
/// - https://www.chromium.org/updates/same-site
/// - https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
/// - https://bugs.webkit.org/show_bug.cgi?id=198181
/// </remarks>
/// <param name="services">The service collection to register <see cref="CookiePolicyOptions" /> into.</param>
/// <returns>The modified <see cref="IServiceCollection" />.</returns>
public static IServiceCollection ConfigureNonBreakingSameSiteCookies(this IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
return services;
}
private static void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
if (options.SameSite == SameSiteMode.None)
{
var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
if (DisallowsSameSiteNone(userAgent))
{
options.SameSite = Unspecified;
}
}
}
/// <summary>
/// Checks if the UserAgent is known to interpret an unknown value as Strict.
/// For those the <see cref="CookieOptions.SameSite" /> property should be
/// set to <see cref="Unspecified" />.
/// </summary>
/// <remarks>
/// This code is taken from Microsoft:
/// https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
/// </remarks>
/// <param name="userAgent">The user agent string to check.</param>
/// <returns>Whether the specified user agent (browser) accepts SameSite=None or not.</returns>
private static bool DisallowsSameSiteNone(string userAgent)
{
// Cover all iOS based browsers here. This includes:
// - Safari on iOS 12 for iPhone, iPod Touch, iPad
// - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
// - Chrome on iOS 12 for iPhone, iPod Touch, iPad
// All of which are broken by SameSite=None, because they use the
// iOS networking stack.
// Notes from Thinktecture:
// Regarding https://caniuse.com/#search=samesite iOS versions lower
// than 12 are not supporting SameSite at all. Starting with version 13
// unknown values are NOT treated as strict anymore. Therefore we only
// need to check version 12.
if (userAgent.Contains("CPU iPhone OS 12")
|| userAgent.Contains("iPad; CPU OS 12"))
{
return true;
}
// Cover Mac OS X based browsers that use the Mac OS networking stack.
// This includes:
// - Safari on Mac OS X.
// This does not include:
// - Chrome on Mac OS X
// because they do not use the Mac OS networking stack.
// Notes from Thinktecture:
// Regarding https://caniuse.com/#search=samesite MacOS X versions lower
// than 10.14 are not supporting SameSite at all. Starting with version
// 10.15 unknown values are NOT treated as strict anymore. Therefore we
// only need to check version 10.14.
if (userAgent.Contains("Safari")
&& userAgent.Contains("Macintosh; Intel Mac OS X 10_14")
&& userAgent.Contains("Version/"))
{
return true;
}
// Cover Chrome 50-69, because some versions are broken by SameSite=None
// and none in this range require it.
// Note: this covers some pre-Chromium Edge versions,
// but pre-Chromium Edge does not require SameSite=None.
// Notes from Thinktecture:
// We can not validate this assumption, but we trust Microsofts
// evaluation. And overall not sending a SameSite value equals to the same
// behavior as SameSite=None for these old versions anyways.
if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
{
return true;
}
if (GetChromeVersion(userAgent) >= 80)
{
return true;
}
return false;
}
private static int GetChromeVersion(string userAgent)
{
try
{
return Convert.ToInt32(userAgent.Split("Chrome/")[1].Split('.')[0]);
}
catch (Exception)
{
return 0;
}
}
}
}
```
### Step-2
Assume that your project name is *Acme.BookStore*. Then open `AcmeBookStoreWebModule.cs` class.
Add the following line to `ConfigureServices()` method.
```csharp
context.Services.ConfigureNonBreakingSameSiteCookies();
```
### Step-3
Go to`OnApplicationInitialization()` method in `AcmeBookStoreWebModule.cs` add `app.UseCookiePolicy();`
```csharp
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseErrorPage();
app.UseHsts();
}
app.UseCookiePolicy(); //<--- added this --->
//....
}
```
It's all! You are ready to go!
---
Referenced from https://www.thinktecture.com/en/identity/samesite/prepare-your-identityserver/

302
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/Using-DevExtreme-In-ABP-Based-Application.md

@ -0,0 +1,302 @@
## Using DevExtreme with ABP Based Applications
Hi, in this step by step article, I will show you how to integrate DevExtreme components into ABP Framework based applications.
![both-example-result](both-example-result.png)
## Create the Project
ABP Framework offers startup templates to get into the business faster. We can download a new startup template using [ABP CLI](https://docs.abp.io/en/abp/latest/CLI):
````bash
abp new DevExtremeSample
````
After the download is finished, open the solution in the Visual Studio (or your favorite IDE):
![initial-project](initial-project.png)
Run the `DevExtremeSample.DbMigrator` application to create the database and seed initial data (which creates the admin user, admin role, related permissions, etc). Then we can run the `DevExtremeSample.Web` project to see our application working.
> _Default admin username is **admin** and password is **1q2w3E\***_
## Install DevExtreme
You can follow [this documentation](https://js.devexpress.com/Documentation/17_1/Guide/ASP.NET_MVC_Controls/Prerequisites_and_Installation/) to install DevExpress packages into your computer.
> Don't forget to add _"DevExpress NuGet Feed"_ to your **Nuget Package Sources**.
### Adding DevExtreme NuGet Packages
Add the `DevExtreme.AspNet.Core` NuGet package to the `DevExtremeSample.Application.Contracts` project.
```
Install-Package DevExtreme.AspNet.Core
```
Add the `DevExtreme.AspNet.Data` package to your `DevExtremeSample.Web` project.
```
Install-Package DevExtreme.AspNet.Data
```
### Adding DevExtreme NPM Dependencies
Open your `DevExtremeSample.Web` project folder with a command line and add `devextreme` and `devextreme-aspnet-data` NPM packages:
````bash
npm install devextreme
````
````bash
npm install devextreme-aspnet-data
````
### Adding Resource Mappings
The `devextreme` and `devextreme-aspnet-data` NPM packages are saved under `node_modules` folder. We need to move the needed files in our `wwwroot/libs` folder to use them in our web project. We can do it using the ABP [client side resource mapping](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Client-Side-Package-Management) system.
Open the `abp.resourcemapping.js` file in your `DevExtremeSample.Web` project and add the following definitions to inside `mappings` object.
````json
"@node_modules/devextreme/dist/**/*": "@libs/devextreme/",
"@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js": "@libs/devextreme/js/"
````
The final `abp.resourcemapping.js` file should look like below:
```
module.exports = {
aliases: {},
mappings: {
"@node_modules/devextreme/dist/**/*": "@libs/devextreme/",
"@node_modules/devextreme-aspnet-data/js/dx.aspnet.data.js": "@libs/devextreme/"
},
};
```
Open your `DevExtremeSample.Web` project folder with a command line and run the `gulp` command. This command will copy the needed library files into the ``/wwwroot/libs/devextreme/` folder.
![gulp](gulp.png)
You can see `devextreme` folder inside the `wwwroot/libs`:
![wwwroot-lib](wwwroot-lib.png)
### Adding DevExtremeStyleContributor
We will add DevExtreme CSS files to the global bundle by creating a [bundle contributor](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification).
Create a `Bundling` folder in the `DevExtremeSample.Web` project and a `DevExtremeStyleContributor.cs` file with the following content:
```csharp
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace DevExtremeSample.Web.Bundling
{
public class DevExtremeStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/devextreme/css/dx.common.css");
context.Files.AddIfNotContains("/libs/devextreme/css/dx.light.css");
}
}
}
```
> You can choose another theme than the light theme. Check the `/libs/devextreme/css/` folder and the DevExtreme documentation for other themes.
Open your `DevExtremeSampleWebModule.cs` file in your `DevExtremeSample.Web` project and add following code into the `ConfigureServices` method:
```csharp
Configure<AbpBundlingOptions>(options =>
{
options
.StyleBundles
.Get(StandardBundles.Styles.Global)
.AddContributors(typeof(DevExtremeStyleContributor));
});
```
### Adding DevExtremeScriptContributor
We can not add DevExtreme js packages to Global Script Bundles, just like done for the CSS files. Because DevExtreme requires to add its JavaScript files into the `<head>` section of the HTML document, while ABP Framework adds all JavaScript files to the end of the `<body>` (as a best practice).
Fortunately, ABP Framework has a [layout hook system](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Customization-User-Interface#layout-hooks) that allows you to add any code into some specific positions in the HTML document. All you need to do is to create a `ViewComponent` and configure the layout hooks.
Let's begin by creating a `DevExtremeScriptContributor.cs` file in the `Bundling` folder by copying the following code inside it:
```csharp
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery;
using Volo.Abp.Modularity;
namespace DevExtremeSample.Web.Bundling
{
[DependsOn(
typeof(JQueryScriptContributor)
)]
public class DevExtremeScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/devextreme/js/dx.all.js");
context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.mvc.js");
context.Files.AddIfNotContains("/libs/devextreme/js/dx.aspnet.data.js");
}
}
}
```
As you see, the `DevExtremeScriptContributor` is depends on `JQueryScriptContributor` which adds JQuery related files before the DevExpress packages (see the [bundling system](https://docs.abp.io/en/abp/latest/UI/AspNetCore/Bundling-Minification) for details).
#### Create DevExtremeJsViewComponent
Create a new view component, named `DevExtremeJsViewComponent` inside the `/Components/DevExtremeJs` folder of the Web project, by following the steps below:
1) Create a `DevExtremeJsViewComponent` class inside the `/Components/DevExtremeJs` (create the folders first):
```csharp
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
namespace DevExtremeSample.Web.Components.DevExtremeJs
{
public class DevExtremeJsViewComponent : AbpViewComponent
{
public IViewComponentResult Invoke()
{
return View("/Components/DevExtremeJs/Default.cshtml");
}
}
}
```
2) Create `Default.cshtml` file in the same folder with the following content:
```csharp
@using DevExtremeSample.Web.Bundling
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling
<abp-script type="typeof(DevExtremeScriptContributor)" />
```
Your final Web project should be like as the following:
![devextreme-js](devextreme-js.png)
3) Now, we can add this view component to `<head>` section by using the layout hooks.
Open your `DevExtremeSampleWebModule.cs` file in your `DevExtremeSample.Web` project and add following code into the `ConfigureServices` method:
```csharp
Configure<AbpLayoutHookOptions>(options =>
{
options.Add(
LayoutHooks.Head.Last, //The hook name
typeof(DevExtremeJsViewComponent) //The component to add
);
});
```
#### Known Issue: Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.
> This issue does exists in the ABP Framework v3.0 and earlier versions. If you are using ABP Framework v3.1 or a latter version, you can skip this section.
When you run your `*.Web` project, you will see an exception (`Uncaught TypeError: MutationObserver.observe: Argument 1 is not an object.`) at your console.
To fix that issue, download this file [abp.jquery.js](https://github.com/abpframework/abp/blob/dev/npm/packs/jquery/src/abp.jquery.js) and replace with the `wwwroot/libs/abp/jquery/abp.jquery.js` file of your Web project.
### Result
The installation step was done. You can use any DevExtreme component in your application.
Example: A button and a progress bar component:
![devexp-result](devexp-result.gif)
This example has been created by following [this documentation](https://js.devexpress.com/Demos/WidgetsGallery/Demo/ProgressBar/Overview/NetCore/Light/).
## The Sample Application
We have created a sample application with [Tree List](https://demos.devexpress.com/ASPNetCore/Demo/TreeList/Overview/) and [Data Grid](https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/Overview/) examples.
### The Source Code
You can download the source code from [here](https://github.com/abpframework/abp-samples/tree/master/DevExtreme-Mvc).
### Data Grid
You can see the full working example of [Data Grid](https://demos.devexpress.com/ASPNetCore/Demo/DataGrid/Overview/).
![data-grid-final](data-grid-final.png)
The related files for this example are highlighted at the following screenshots.
![data-grid-app-contract](data-grid-app-contract.png)
![data-grid-application](data-grid-application.png)
![data-grid-web](data-grid-web.png)
### Tree List
You can see the full working example of [Tree List](https://demos.devexpress.com/ASPNetCore/Demo/TreeList/Overview/).
![tree-list-final](tree-list-final.png)
The related files for this example are highlighted at the following screenshots.
![tree-list-app-contract](tree-list-app-contract.png)
![tree-list-application](tree-list-application.png)
![tree-list-web](tree-list-web.png)
### Additional Notes
#### Data Storage
I've used an in-memory list to store data for this example, instead of a real database. Because it is not related to DevExpress usage. There is a `SampleDataService.cs` file in `Data` folder at `.Application.Contracts` project. All the data is stored here.
#### JSON Serialization
You can see some `JsonProperty` attributes on the DTO properties. I uses these attributes because DevExtreme example expects `PascalCase` property names in the serialized JSON that is sent to the client. But ABP Framework & ASP.NET Core conventionally uses `camelCase` property names on JSON serialization. Adding these `JsonProperty` attributes ensures that the related properties are serialized as `PascalCase`.
#### DevExtreme Components vs Application Service Methods
ABP Framework conventionally converts application services to API Controllers. For example, see the application service below:
````csharp
public class OrderAppService : DevExtremeSampleAppService, IOrderAppService
{
public async Task<LoadResult> GetOrdersAsync(DataSourceLoadOptions loadOptions)
{
...
}
public async Task<Order> InsertOrder(string values)
{
...
}
...
}
````
You can use these service methods for your DevExtreme components as shown below:
```csharp
Html.DevExtreme().DataGrid<Order>()
.DataSource(d => d.Mvc()
.Controller("Order") // Application Service Name without 'AppService'
.LoadAction("GetOrders") // Method Name without 'Async'
.InsertAction("InsertOrder")
.UpdateAction("UpdateOrder")
.DeleteAction("DeleteOrder")
.Key("OrderID")
)
```

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/both-example-result.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-app-contract.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-application.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-final.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/data-grid-web.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devexp-result.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/devextreme-js.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/gulp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/initial-project.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-app-contract.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-application.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-final.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/tree-list-web.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
docs/en/Community-Articles/2020-08-18-DevExtreme-With-ABP/wwwroot-lib.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

12
docs/en/CurrentUser.md

@ -1,4 +1,4 @@
# Current User
# Current User
It is very common to retrieve the information about the logged in user in a web application. The current user is the active user related to the current request in a web application.
@ -86,7 +86,7 @@ Beside these standard methods, there are some extension methods:
## 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.
`ICurrentPrincipalAccessor` is the service that should be used (by the ABP Framework and your application code) whenever the current principal 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`.
@ -114,9 +114,9 @@ public class MyService : ITransientDependency
}
````
### Changing the Current Principle
### Changing the Current Principal
Current principle is not something you want to set or change, except at 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.
Current principal is not something you want to set or change, except at some advanced scenarios. If you need it, use the `Change` method of the `ICurrentPrincipalAccessor`. It takes a `ClaimsPrincipal` object and makes it "current" for a scope.
Example:
@ -132,7 +132,7 @@ public class MyAppService : ApplicationService
public void Foo()
{
var newPrinciple = new ClaimsPrincipal(
var newPrincipal = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[]
{
@ -143,7 +143,7 @@ public class MyAppService : ApplicationService
)
);
using (_currentPrincipalAccessor.Change(newPrinciple))
using (_currentPrincipalAccessor.Change(newPrincipal))
{
var userName = CurrentUser.UserName; //returns "john"
//...

5
docs/en/Customizing-Application-Modules-Extending-Entities.md

@ -43,7 +43,10 @@ Assume that you want to add a `SocialSecurityNumber` to the `IdentityUser` entit
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityUser, string>(
"SocialSecurityNumber",
b => { b.HasMaxLength(32); }
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(32);
}
);
````

251
docs/en/Emailing.md

@ -1,3 +1,250 @@
# Emailing
# Email Sending
TODO!
ABP Framework provides various services, settings and integrations for sending emails;
* Provides `IEmailSender` service that is used to send emails.
* Defines [settings](Settings.md) to configure email sending.
* Integrates to the [background job system](Background-Jobs.md) to send emails via background jobs.
* Provides [MailKit integration](MailKit.md) package.
## Installation
> This package is already installed if you are using the [application startup template](Startup-Templates/Application.md).
It is suggested to use the [ABP CLI](CLI.md) to install this package. Open a command line window in the folder of the project (.csproj file) and type the following command:
````bash
abp add-package Volo.Abp.Emailing
````
If you haven't done it yet, you first need to install the ABP CLI. For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.Emailing).
## Sending Emails
### IEmailSender
[Inject](Dependency-Injection.md) the `IEmailSender` into any service and use the `SendAsync` method to send emails.
**Example**
````csharp
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
namespace MyProject
{
public class MyService : ITransientDependency
{
private readonly IEmailSender _emailSender;
public MyService(IEmailSender emailSender)
{
_emailSender = emailSender;
}
public async Task DoItAsync()
{
await _emailSender.SendAsync(
"target@domain.com", // target email address
"Email subject", // subject
"This is email body..." // email body
);
}
}
}
````
`SendAsync` method has overloads to supply more parameters like;
* **from**: You can set this as the first argument to set a sender email address. If not provided, the default sender address is used (see the email settings below).
* **isBodyHtml**: Indicates whether the email body may contain HTML tags. **Default: true**.
> `IEmailSender` is the suggested way to send emails, since it makes your code provider independent.
#### MailMessage
In addition to primitive parameters, you can pass a **standard `MailMessage` object** ([see](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail.mailmessage)) to the `SendAsync` method to set more options, like adding attachments.
### ISmtpEmailSender
Sending emails is implemented by the standard `SmtpClient` class ([see](https://docs.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient)) by default. The implementation class is the `SmtpEmailSender`. This class also expose the `ISmtpEmailSender` service (in addition to the `IEmailSender`).
Most of the time you want to directly use the `IEmailSender` to make your code provider independent. However, if you want to create an `SmtpClient` object with the same email settings, you can inject the `ISmtpEmailSender` and use its `BuildClientAsync` method to obtain a `SmtpClient` object and send the email yourself.
## Queueing Emails / Background Jobs
`IEmailSender` has a `QueueAsync` method that can be used to add emails to the background job queue to send them in a background thread. In this way, you don't take time of the user by waiting to send the email. `QueueAsync` method gets the same arguments with the `SendAsync` method.
Queueing emails tolerates errors since the background job system has re-try mechanism to overcome temporary network/server problems.
See the [background jobs document](Background-Jobs.md) for more about the background job system.
## Email Settings
Email sending uses the [setting system](Settings.md) to define settings and get the values of these settings on the runtime. `Volo.Abp.Emailing.EmailSettingNames` defines constants for the setting names, just listed below:
* **Abp.Mailing.DefaultFromAddress**: Used as the sender's email address when you don't specify a sender when sending emails (just like in the example above).
* **Abp.Mailing.DefaultFromDisplayName**: Used as the sender's display name when you don't specify a sender when sending emails (just like in the example above).
* **Abp.Mailing.Smtp.Host**: The IP/Domain of the SMTP server (default: 127.0.0.1).
* **Abp.Mailing.Smtp.Port**: The Port of the SMTP server (default: 25).
* **Abp.Mailing.Smtp.UserName**: Username, if the SMTP server requires authentication.
* **Abp.Mailing.Smtp.Password**: Password, if the SMTP server requires authentication. **This value is encrypted **(see the section below).
* **Abp.Mailing.Smtp.Domain**: Domain for the username, if the SMTP server requires authentication.
* **Abp.Mailing.Smtp.EnableSsl**: A value that indicates if the SMTP server uses SSL or not ("true" or "false". Default: "false").
* **Abp.Mailing.Smtp.UseDefaultCredentials**: If true, uses default credentials instead of the provided username and password ("true" or "false". Default: "true").
The easiest way to define these settings it to add them to the `appsettings.json` file. The [application startup template](Startup-Templates/Application.md) already has these settings in the `appsettings.json`:
````json
"Settings": {
"Abp.Mailing.Smtp.Host": "127.0.0.1",
"Abp.Mailing.Smtp.Port": "25",
"Abp.Mailing.Smtp.UserName": "",
"Abp.Mailing.Smtp.Password": "",
"Abp.Mailing.Smtp.Domain": "",
"Abp.Mailing.Smtp.EnableSsl": "false",
"Abp.Mailing.Smtp.UseDefaultCredentials": "true",
"Abp.Mailing.DefaultFromAddress": "noreply@abp.io",
"Abp.Mailing.DefaultFromDisplayName": "ABP application"
}
````
You can set/change these settings using the `ISettingManager` and store values in a database. See the [setting system document](Settings.md) to understand the setting system better.
### Encrypt the SMTP Password
*Abp.Mailing.Smtp.Password* must be an **encrypted** value. If you use the `ISettingManager` to set the password, you don't have to worry. It internally encrypts the values on set and decrypts on get.
If you use the `appsettings.json` to store the password, you should manually inject the `ISettingEncryptionService` and use its `Encrypt` method to obtain an encrypted value. This can be done by creating a simple code in your application. Then you can delete the code. As better, you can create a UI in your application to configure the email settings. In this case, you can directly use the `ISettingManager` without worrying the encryption.
### ISmtpEmailSenderConfiguration
If you don't want to use the setting system to store the email sending configuration, you can replace the `ISmtpEmailSenderConfiguration` service with your own implementation to get the configuration from any other source. `ISmtpEmailSenderConfiguration` is implemented by the `SmtpEmailSenderConfiguration` by default, which gets the configuration from the setting system as explained above.
## Text Template Integration
ABP Framework provides a strong and flexible [text templating system](Text-Templating.md). You can use the text templating system to create dynamic email contents. Inject the `ITemplateRenderer` and use the `RenderAsync` to render a template. Then use the result as the email body.
While you can define and use your own text templates, email sending system provides two simple built-in text templates.
**Example: Use the standard and simple message template to send emails**
````csharp
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.TextTemplating;
namespace Acme.BookStore.Web
{
public class MyService : ITransientDependency
{
private readonly IEmailSender _emailSender;
private readonly ITemplateRenderer _templateRenderer;
public MyService(
IEmailSender emailSender,
ITemplateRenderer templateRenderer)
{
_emailSender = emailSender;
_templateRenderer = templateRenderer;
}
public async Task DoItAsync()
{
var body = await _templateRenderer.RenderAsync(
StandardEmailTemplates.Message,
new
{
message = "This is email body..."
}
);
await _emailSender.SendAsync(
"target-address@domain.com",
"Email subject",
body
);
}
}
}
````
The resulting email body will be shown below:
````html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
This is email body...
</body>
</html>
````
Emailing system defines the built-in text templates with the given names:
"**Abp.StandardEmailTemplates.Message**" is simplest template that has a text message:
````html
{%{{{model.message}}}%}
````
This template uses the "Abp.StandardEmailTemplates.Layout" as its layout.
"**Abp.StandardEmailTemplates.Layout**" is a simple template to provide an HTML document layout:
````html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
{%{{{content}}}%}
</body>
</html>
````
The final rendered message was shown above.
> These template names are contants defined in the `Volo.Abp.Emailing.Templates.StandardEmailTemplates` class.
### Overriding/Replacing the Standard Templates
You typically want to replace the standard templates with your own ones, so you can prepare a branded email messages. To do that, you can use the power of the [virtual file system](Virtual-File-System.md) (VFS) or replace them in your own template definition provider.
Pathes of the templates in the virtual file system are shown below:
* `/Volo/Abp/Emailing/Templates/Layout.tpl`
* `/Volo/Abp/Emailing/Templates/Message.tpl`
If you add files to the same localization in the virtual file system, your files will override them.
Templates are inline localized, that means you can take the power of the [localization system](Localization.md) to make your templates multi-cultural.
See the [text templating system](Text-Templating.md) document for details.
> Notice that you can define and use your own templates for your application, rather than using the standard simple templates. These standard templates are mostly for reusable modules where they don't define their own templates but rely on the built-in ones. This makes easy to customize emails sent by the used modules, by just overriding the standard email layout template.
## NullEmailSender
`NullEmailSender` is a built-in class that implements the `IEmailSender`, but writes email contents to the [standard log system](Logging.md), rathen than actually sending the emails.
This class can be useful especially in development time where you generally don't want to send real emails. The [application startup template](Startup-Templates/Application.md) already uses this class in the **DEBUG mode** with the following configuration in the domain layer:
````csharp
#if DEBUG
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
#endif
````
So, don't confuse if you don't receive emails on DEBUG mode. Emails will be sent as expected on production (RELEASE mode). Remove these lines if you want to send real emails on DEBUG too.
## See Also
* [MailKit integration for sending emails](MailKit.md)

7
docs/en/Entity-Framework-Core-Migrations.md

@ -398,7 +398,7 @@ builder.Entity<AppRole>(b =>
You've configured the custom property for your `DbContext` that is used by your application on the runtime. We also need to configure the `MigrationsDbContext`.
Instead of directly changing the `MigrationsDbContext`, we should use the entity extension system of the ABP Framework. Find the `YourProjectNameEntityExtensions` class in the `.EntityFrameworkCore` project of your solution (`BookStoreEntityExtensions` for this example) and change it as shown below:
Instead of directly changing the `MigrationsDbContext`, we **should** use the entity extension system of the ABP Framework. Find the `YourProjectNameEfCoreEntityExtensionMappings` class in the `.EntityFrameworkCore` project of your solution (`BookStoreEfCoreEntityExtensionMappings` for this example) and change it as shown below:
````csharp
public static class MyProjectNameEntityExtensions
@ -412,7 +412,10 @@ public static class MyProjectNameEntityExtensions
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityRole, string>(
"Title",
builder => { builder.HasMaxLength(64); }
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(128);
}
);
});
}

7
docs/en/Entity-Framework-Core.md

@ -325,13 +325,16 @@ This section only explains the EF Core related usage of the `ObjectExtensionMana
ObjectExtensionManager.Instance
.MapEfCoreProperty<IdentityRole, string>(
"Title",
builder => { builder.HasMaxLength(64); }
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(64);
}
);
````
If the related module has implemented this feature (by using the `ConfigureEfCoreEntity` explained below), then the new property is added to the model. Then you need to run the standard `Add-Migration` and `Update-Database` commands to update your database to add the new field.
>`MapEfCoreProperty` method must be called before using the related `DbContext`. It is a static method. The best way is to use it in your application as earlier as possible. The application startup template has a `YourProjectNameEntityExtensions` class that is safe to use this method inside.
>`MapEfCoreProperty` method must be called before using the related `DbContext`. It is a static method. The best way is to use it in your application as earlier as possible. The application startup template has a `YourProjectNameEfCoreEntityExtensionMappings` class that is safe to use this method inside.
### ConfigureEfCoreEntity

11
docs/en/Exception-Handling.md

@ -320,3 +320,14 @@ Some exception types are automatically thrown by the framework:
- `EntityNotFoundException` is thrown if the requested entity is not available. This is mostly thrown by [repositories](Repositories.md).
You can also throw these type of exceptions in your code (although it's rarely needed).
## Send exception details to the client
You can send exceptions to the client via the `SendExceptionsDetailsToClients` property of the `AbpExceptionHandlingOptions` class:
````csharp
services.Configure<AbpExceptionHandlingOptions>(options =>
{
options.SendExceptionsDetailsToClients = true;
});
````

2
docs/en/Getting-Started-React-Native.md

@ -2,7 +2,7 @@
ABP platform provide basic [React Native](https://reactnative.dev/) startup template to develop mobile applications **integrated to your ABP based backends**.
When you **create a new application** as described in the [getting started document](Getting-Started.md), the solution includes the React Native application in the `react-native` folder as default.
When you **create a new application** as described in the [getting started document](Getting-Started.md), you have to add `-m react-native` option to include `react-native` project in your solution.
## Configure Your Local IP Address

9
docs/en/Getting-Started.md

@ -65,8 +65,6 @@ Use the `new` command of the ABP CLI to create a new project:
abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
````
> This command also creates a React Native mobile application inside the solution folder. If you don't want it, you can safely delete it or specify the `-m none` option to the `abp new` command to not include it in the solution at all.
{{ if UI == "NG" }}
* `-u` argument specifies the UI framework, `angular` in this case.
@ -126,7 +124,6 @@ There are three folders in the created solution:
* `angular` folder contains the Angular UI application.
* `aspnet-core` folder contains the backend solution.
* `react-native` folder contains the React Native UI application.
Open the `.sln` (Visual Studio solution) file under the `aspnet-core` folder:
@ -339,11 +336,7 @@ Enter **admin** as the username and **1q2w3E*** as the password to login to the
## Mobile Development
When you create a new application, the solution includes `react-native` folder by default. This is a basic [React Native](https://reactnative.dev/) startup template to develop mobile applications integrated to your ABP based backends.
If you don't plan to develop a mobile application with React Native, you can safely delete the `react-native` folder.
> You can specifying the `-m none` option to the ABP CLI to not create the `react-native` folder in the beginning.
If you want to include a [React Native](https://reactnative.dev/) project in your solution, add `-m react-native` (or `--mobile react-native`) argument to project creation command. This is a basic React Native startup template to develop mobile applications integrated to your ABP based backends.
See the "[Getting Started with the React Native](Getting-Started-React-Native.md)" document to learn how to configure and run the React Native application.

3
docs/en/Global-Features.md

@ -0,0 +1,3 @@
# Global Features
TODO (see [#5061](https://github.com/abpframework/abp/issues/5061) until this is documented).

48
docs/en/MailKit.md

@ -0,0 +1,48 @@
# MailKit Integration
[MailKit](http://www.mimekit.net/) is a cross-platform, popular open source mail client library for .net. ABP Framework provides an integration package to use the MailKit as the [email sender](Emailing.md).
## Installation
It is suggested to use the [ABP CLI](CLI.md) to install this package. Open a command line window in the folder of the project (.csproj file) and type the following command:
````bash
abp add-package Volo.Abp.MailKit
````
If you haven't done it yet, you first need to install the ABP CLI. For other installation options, see [the package description page](https://abp.io/package-detail/Volo.Abp.MailKit).
## Sending Emails
### IEmailSender
[Inject](Dependency-Injection.md) the standard `IEmailSender` into any service and use the `SendAsync` method to send emails. See the [email sending document](Emailing.md) for details.
> `IEmailSender` is the suggested way to send emails even if you use MailKit, since it makes your code provider independent.
### IMailKitSmtpEmailSender
MailKit package also exposes the `IMailKitSmtpEmailSender` service that extends the `IEmailSender` by adding the `BuildClientAsync()` method. This method can be used to obtain a `MailKit.Net.Smtp.SmtpClient` object that can be used to perform MailKit specific operations.
## Configuration
MailKit integration package uses the same settings defined by the email sending system. So, refer to the [email sending document](Emailing.md) for the settings.
In addition to the standard settings, this package defines `AbpMailKitOptions` as a simple [options](Options.md) class. This class defines only one options:
* **SecureSocketOption**: Used to set one of the `SecureSocketOptions`. Default: `null` (uses the defaults).
**Example: Use *SecureSocketOptions.SslOnConnect***
````csharp
Configure<AbpMailKitOptions>(options =>
{
options.SecureSocketOption = SecureSocketOptions.SslOnConnect;
});
````
Refer to the [MailKit documentation](http://www.mimekit.net/) to learn more about this option.
## See Also
* [Email sending](Emailing.md)

2
docs/en/Module-Development-Basics.md

@ -1,4 +1,4 @@
# Module Development
# Modularity
## Introduction

3
docs/en/Module-Entity-Extensions.md

@ -0,0 +1,3 @@
# Module Entity Extensions
See https://docs.abp.io/en/commercial/latest/guides/module-entity-extensions (it will be moved here soon).

32
docs/en/Nightly-Builds.md

@ -2,40 +2,18 @@
All framework & module packages are deployed to MyGet every night in weekdays. So, you can use or test the latest code without waiting the next release.
## Configure Visual Studio
## Install & Uninstall Nightly Preview Packages
> Requires Visual Studio 2017+
1. Go to `Tools > Options > NuGet Package Manager > Package Source`.
2. Click the green `+` icon.
3. Set `ABP Nightly` as *Name* and `https://www.myget.org/F/abp-nightly/api/v3/index.json` as the *Source* as shown below:
![night-build-add-nuget-source](images/night-build-add-nuget-source.png)
4. Click the `Update` button.
5. Click the `OK` button to save changes.
## Install Package
Now, you can install preview / nightly packages to your project from Nuget Browser or Package Manager Console.
![night-build-add-nuget-package](images/night-build-add-nuget-package.png)
1. In the nuget browser, select "Include prereleases".
2. Change package source to "All".
3. Search a package. You will see prereleases of the package formatted as `(VERSION)-preview(DATE)` (like *v0.16.0-preview20190401* in this sample).
4. You can click to the `Install` button to add package to your project.
## Install & Uninstall Preview NPM Packages
The latest version of preview NPM packages can be installed by the running below command in the root folder of application:
The latest version of nightly preview packages can be installed by the running below command in the root folder of application:
```bash
abp switch-to-preview --npm
abp switch-to-nightly
```
If you're using the ABP Framework preview packages, you can switch back to stable version using this command:
If you're using the ABP Framework nightly preview packages, you can switch back to stable version using this command:
```bash
abp switch-to-stable --npm
abp switch-to-stable
```
See the [ABP CLI documentation](./CLI.md) for more information.

40
docs/en/Previews.md

@ -0,0 +1,40 @@
# Preview Releases
The preview versions are released **~2 weeks before** releasing a major or feature version of the ABP Framework. They are released for developers to try and provide feedback to have more stable versions.
Versioning of a preview release is like that:
* 3.1.0-rc.1
* 4.0.0-rc.1
More than one preview releases (like 3.1.0-rc.2 and 3.1.0-rc.3) might be published until the stable version (like 3.1.0).
## Using the Preview Versions
### New Solutions
To create a project for testing the preview version, you can select the "**preview**" option on the [download page](https://abp.io/get-started) or use the "**--preview**" parameter with the [ABP CLI](CLI.md) new command:
````bash
abp new Acme.BookStore --preview
````
This command will create a new project using the latest preview NuGet packages, NPM packages and the solution template. Whenever the stable version is released, you can switch to the stable version for your solution using the `abp switch-to-stable` command in the root folder of your solution.
### Existing Solutions
If you already have a solution and want to use/test the latest preview version, use the following [ABP CLI](CLI.md) command in the root folder of your solution.
````bash
abp switch-to-preview
````
You can return back to the latest stable using the `abp switch-to-stable ` command later.
````bash
abp switch-to-stable
````
## Providing Feedback
You can open an issue on the [GitHub repository](https://github.com/abpframework/abp/issues/new), if you find a bug or want to provide any kind of feedback.

6
docs/en/Repositories.md

@ -6,7 +6,11 @@ Repositories, in practice, are used to perform database operations for domain ob
## Generic Repositories
ABP can provide a **default generic repository** for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository<TEntity, TKey>` into your service and perform standard **CRUD** operations. Example usage:
ABP can provide a **default generic repository** for each aggregate root or entity. You can [inject](Dependency-Injection.md) `IRepository<TEntity, TKey>` into your service and perform standard **CRUD** operations.
> Database provider layer should be properly configured to be able to use the default generic repositories. It is **already done** if you've created your project using the startup templates. If not, refer to the database provider documents ([EF Core](Entity-Framework-Core.md) / [MongoDB](MongoDB.md)) to configure it.
**Example usage of a default generic repository:**
````C#
public class PersonAppService : ApplicationService

29
docs/en/Samples/Index.md

@ -15,17 +15,12 @@ A complete solution to demonstrate how to build systems based on the microservic
A simple CRUD application to show basic principles of developing an application with the ABP Framework. The same sample was implemented with different technologies:
* **Book Store: Razor Pages UI & Entity Framework Core**
* [Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore)
* [Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC&DB=EF)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore-Mvc-EfCore)
* **Book Store: Angular UI & MongoDB**
* [Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=NG)
* [Tutorial](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=NG&DB=Mongo)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb)
* **Book Store: Modular application (Razor Pages UI & EF Core)**
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BookStore-Modular)
While there is no Razor Pages & MongoDB combination, you can check both documents to understand it since DB & UI selection don't effect each other.
@ -33,11 +28,14 @@ While there is no Razor Pages & MongoDB combination, you can check both document
### Other Samples
* **Entity Framework Migrations**: A solution to demonstrate how to split your application into multiple databases each database contains different modules.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/EfCoreMigrationDemo)
* [EF Core database migrations document](../Entity-Framework-Core-Migrations.md)
* **SignalR Demo**: A simple chat application that allows to send and receive messages among authenticated users.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo)
* [SignalR Integration document](../SignalR-Integration.md)
* **Real Time Messaging In A Distributed Architecture** (using SingalR & RabbitMQ)
* [Source code](https://github.com/abpframework/abp-samples/tree/master/SignalRTieredDemo)
* [Article](https://community.abp.io/articles/real-time-messaging-in-a-distributed-architecture-using-abp-framework-singalr-rabbitmq-daf47e17)
* **Dashboard Demo**: A simple application to show how to use the widget system for the ASP.NET Core MVC UI.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo)
* [Widget documentation](../UI/AspNetCore/Widgets.md)
@ -50,12 +48,17 @@ While there is no Razor Pages & MongoDB combination, you can check both document
* [Text templating documentation](../Text-Templating.md)
* **Stored Procedure Demo**: Demonstrates how to use stored procedures, database views and functions with best practices.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/StoredProcedureDemo)
* **Passwordless Authentication**: Shows how to add a custom token provider to authenticate a user with a link, instead of entering a password.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/PasswordlessAuthentication)
* [Article](https://community.abp.io/articles/implementing-passwordless-authentication-with-asp.net-core-identity-c25l8koj)
* **Authentication Customization**: A solution to show how to customize the authentication for ASP.NET Core MVC / Razor Pages applications.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/Authentication-Customization)
* Related "[How To](../How-To/Index.md)" documents:
* [Azure Active Directory Authentication](../How-To/Azure-Active-Directory-Authentication-MVC.md)
* [Customize the Login Page](../How-To/Customize-Login-Page-MVC.md)
* [Customize the SignIn Manager](../How-To/Customize-SignIn-Manager.md)
* Related articles:
* [Azure Active Directory Authentication](https://community.abp.io/articles/how-to-use-the-azure-active-directory-authentication-for-mvc-razor-page-applications-4603b9cf)
* [Customize the Login Page](https://community.abp.io/articles/how-to-customize-the-login-page-for-mvc-razor-page-applications-9a40f3cd)
* [Customize the SignIn Manager](https://community.abp.io/articles/how-to-customize-the-signin-manager-3e858753)
* **GRPC Demo**: Shows how to add a gRPC service to an ABP Framework based web application and consume it from a console application.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo)
* **Empty ASP.NET Core Application**: The most basic ASP.NET Core application with the ABP Framework installed.
* [Source code](https://github.com/abpframework/abp-samples/tree/master/BasicAspNetCoreApplication)
* [Documentation](../Getting-Started-AspNetCore-Application.md)

19
docs/en/Startup-Templates/Application.md

@ -54,6 +54,20 @@ Use `-d` (or `--database-provider`) option to specify the database provider:
abp new Acme.BookStore -d mongodb
````
### Specify the Mobile Application Framework
This template supports the following mobile application frameworks:
- `react-native`: React Native
Use `-m` (or `--mobile`) option to specify the mobile application framework:
````bash
abp new Acme.BookStore -m react-native
````
If not specified, no mobile application will be created.
## Solution Structure
Based on the options you've specified, you will get a slightly different solution structure.
@ -261,11 +275,10 @@ You should run the application with the given order:
### Angular UI
If you choose `Angular` as the UI framework (using the `-u angular` option), the solution is being separated into three folders:
If you choose `Angular` as the UI framework (using the `-u angular` option), the solution is being separated into two folders:
* `angular` folder contains the Angular UI application, the client-side code.
* `aspnet-core` folder contains the ASP.NET Core solution, the server-side code.
* `react-native` folder contains the React Native UI application, the client-side code for mobile.
The server-side is similar to the solution described above. `*.HttpApi.Host` project serves the API, so the `Angular` application consumes it.
@ -356,7 +369,7 @@ See the [testing document](https://angular.io/guide/testing).
### React Native
The solution includes the [React Native](https://reactnative.dev/) application in the `react-native` folder as default.
if `-m react-native` option is spesified in new project command, the solution includes the [React Native](https://reactnative.dev/) application in the `react-native` folder.
The server-side is similar to the solution described above. `*.HttpApi.Host` project serves the API, so the React Native application consumes it.

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

@ -149,6 +149,10 @@ The diagram below shows the relation of the applications:
`.Web.Host` project uses OpenId Connect Authentication to get identity and access tokens for the current user from the `.IdentityServer`. Then uses the access token to call the `.HttpApi.Host`. HTTP API server uses bearer token authentication to obtain claims from the access token to authorize the current user.
##### Pre-requirements
* [Redis](https://redis.io/): The applications use Redis as as distributed cache. So, you need to have Redis installed & running.
##### How to Run?
You should run the application with the given order:

106
docs/en/Text-Templating.md

@ -380,6 +380,112 @@ The rendering result will be:
A global object value: TEST VALUE
````
## Replacing the Existing Templates
It is possible to replace a template defined by a module that used in your application. In this way, you can customize the templates based on your requirements without changing the module code.
### Option-1: Using the Virtual File System
The [Virtual File System](Virtual-File-System.md) allows you to override any file by placing the same file into the same path in your project.
#### Example: Replace the Standard Email Layout Template
ABP Framework provides an [email sending system](Emailing.md) that internally uses the text templating to render the email content. It defines a standard email layout template in the `/Volo/Abp/Emailing/Templates/Layout.tpl` path. The unique name of the template is `Abp.StandardEmailTemplates.Layout` and this string is defined as a constant on the `Volo.Abp.Emailing.Templates.StandardEmailTemplates` static class.
Do the following steps to replace the template file with your own;
**1)** Add a new file into the same location (`/Volo/Abp/Emailing/Templates/Layout.tpl`) in your project:
![replace-email-layout](images/replace-email-layout.png)
**2)** Prepare your email layout template:
````html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
<h1>This my header</h1>
{%{{{content}}}%}
<footer>
This is my footer...
</footer>
</body>
</html>
````
This example simply adds a header and footer to the template and renders the content between them (see the *Layout Templates* section above to understand it).
**3)** Configure the embedded resources in the `.csproj` file
* Add [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet package to the project.
* Add `<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>` into the `<PropertyConfig>...</PropertyConfig>` section of your `.csproj` file.
* Add the following code into your `.csproj` file:
````xml
<ItemGroup>
<None Remove="Volo\Abp\Emailing\Templates\*.tpl" />
<EmbeddedResource Include="Volo\Abp\Emailing\Templates\*.tpl" />
</ItemGroup>
````
This makes the template files "embedded resource".
**4)** Configure the virtual file system
Configure the `AbpVirtualFileSystemOptions` in the `ConfigureServices` method of your [module](Module-Development-Basics.md) to add the embedded files into the virtual file system:
```csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<BookStoreDomainModule>();
});
```
`BookStoreDomainModule` should be your module name, in this example code.
> Be sure that your module (directly or indirectly) [depends on](Module-Development-Basics.md) the `AbpEmailingModule`. Because the VFS can override files based on the dependency order.
Now, your template will be used when you want to render the email layout template.
### Option-2: Using the Template Definition Provider
You can create a template definition provider class that gets the email layout template and changes the virtual file path for the template.
**Example: Use the `/MyTemplates/EmailLayout.tpl` file instead of the standard template**
```csharp
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.TextTemplating;
namespace MyProject
{
public class MyTemplateDefinitionProvider
: TemplateDefinitionProvider, ITransientDependency
{
public override void Define(ITemplateDefinitionContext context)
{
var emailLayoutTemplate = context.GetOrNull(StandardEmailTemplates.Layout);
emailLayoutTemplate
.WithVirtualFilePath(
"/MyTemplates/EmailLayout.tpl",
isInlineLocalized: true
);
}
}
}
```
You should still add the file `/MyTemplates/EmailLayout.tpl` to the virtual file system as explained before. This approach allows you to locate templates in any folder instead of the folder defined by the depended module.
Beside the template content, you can manipulate the template definition properties, like `DisplayName`, `Layout` or `LocalizationSource`.
## Advanced Features
This section covers some internals and more advanced usages of the text templating system.

4
docs/en/Timing.md

@ -10,7 +10,7 @@ ABP provides a basic infrastructure to make it easy and handle automatically whe
`DateTime.Now` returns a `DateTime` object with the **local date & time of the server**. A `DateTime` object **doesn't store the time zone information**. So, you can not know the **absolute date & time** stored in this object. You can only make **assumptions**, like assuming that it was created in UTC+05 time zone. The things especially gets complicated when you save this value to a database and read later, or send it to a client in a **different time zone**.
One solution to this problem is always use `DateTime.UtcNow` and assume all `DateTime` objects as UTC time. In this was, you can convert it to the time zone of the target client when needed.
One solution to this problem is always use `DateTime.UtcNow` and assume all `DateTime` objects as UTC time. In this way, you can convert it to the time zone of the target client when needed.
`IClock` provides an abstraction while getting the current time, so you can control the kind of the date time (UTC or local) in a single point in your application.
@ -110,4 +110,4 @@ See the [setting documentation](Settings.md) to learn more about the setting sys
`ITimezoneProvider` is a service to simple convert [Windows Time Zone Id](https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values) values to [Iana Time Zone Name](https://www.iana.org/time-zones) values and vice verse. It also provides methods to get list of these time zones and get a `TimeZoneInfo` with a given name.
It has been implemented using the [TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter) library.
It has been implemented using the [TimeZoneConverter](https://github.com/mj1856/TimeZoneConverter) library.

4
docs/en/UI/Angular/Multi-Tenancy.md

@ -1,4 +1,4 @@
# Multi Tenancy in Angular UI
# Multi Tenancy in Angular UI
ABP Angular UI supports the multi-tenancy. The following features related to multi-tenancy are available in the startup templates.
@ -17,7 +17,7 @@ On the page above, you can;
![Tenant Switching Component](./images/tenant-switching-box.png)
<p style="font-size:small;text-align:center;">Tenant Switching Component</p>
You can switch between existing tenants by using the tenant switching component in the child pages of the `AccountLayoutComponent` (like Login page). Angular UI sends the selected tenant id sends to the backend as `__tenant` header on each request.
You can switch between existing tenants by using the tenant switching component in the child pages of the `AccountLayoutComponent` (like Login page). Angular UI sends the selected tenant id to the backend as `__tenant` header on each request.
## Domain Tenant Resolver

25
docs/en/UI/Angular/Permission-Management.md

@ -4,36 +4,17 @@ A permission is a simple policy that is granted or prohibited for a particular u
You can get permission of authenticated user using `getGrantedPolicy` selector of `ConfigState`.
You can get permission as boolean value from store:
```js
import { Store } from '@ngxs/store';
import { ConfigState } from '@abp/ng.core';
export class YourComponent {
constructor(private store: Store) {}
ngOnInit(): void {
const canCreate = this.store.selectSnapshot(ConfigState.getGrantedPolicy('AbpIdentity.Roles.Create'));
}
// ...
}
```
Or you can get it via `ConfigStateService`:
You can get permission as boolean value:
```js
import { ConfigStateService } from '@abp/ng.core';
export class YourComponent {
constructor(private configStateService: ConfigStateService) {}
constructor(private config: ConfigStateService) {}
ngOnInit(): void {
const canCreate = this.configStateService.getGrantedPolicy('AbpIdentity.Roles.Create');
const canCreate = this.config.getGrantedPolicy('AbpIdentity.Roles.Create');
}
// ...
}
```

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

@ -158,7 +158,7 @@ Just as explained above, you can replace any component, layout or c# class of th
## Overriding Static Resources
Overriding a static embedded resource (like JavaScript, Css or image files) of a module is pretty easy. Just place a file in the same path in your solution and let the Virtual File System to handle it.
Overriding a static embedded resource (like JavaScript, Css or image files) of a module is pretty easy. Just place a file in the same path in your solution and let the [Virtual File System](../../Virtual-File-System.md) to handle it.
## Manipulating the Bundles

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

@ -6,7 +6,7 @@ The Virtual File System makes it possible to manage files that do not physically
> Most of the times you don't need to manually install this package since it comes pre-installed with the [application startup template](Startup-Templates/Application.md).
[Volo.Abp.VirtualFileSystem](https://www.nuget.org/packages/Volo.Abp.VirtualFileSystem) is the main page of the Virtual File System.
[Volo.Abp.VirtualFileSystem](https://www.nuget.org/packages/Volo.Abp.VirtualFileSystem) is the main package of the Virtual File System.
Use the ABP CLI to add this package to your project:
@ -18,9 +18,9 @@ If you want to do it manually, install the [Volo.Abp.VirtualFileSystem](https://
## Working with the Embedded Files
### Embed the Files
### Embedding the Files
A file should be first marked as an **embedded resource** to embed the file into the assembly. The easiest way to do it is to select the file from the **Solution Explorer** and set **Build Action** to **Embedded Resource** from the **Properties** window. Example:
A file should be first marked as **embedded resource** to embed the file into the assembly. The easiest way to do it is to select the file from the **Solution Explorer** and set **Build Action** to **Embedded Resource** from the **Properties** window. Example:
![build-action-embedded-resource-sample](images/build-action-embedded-resource-sample.png)
@ -55,21 +55,21 @@ Configure<AbpVirtualFileSystemOptions>(options =>
});
````
The `AddEmbedded` extension method takes a class, finds all embedded files from the **assembly of the given class** and registers them to the virtual file system. It is common to pass the module class as the generic argument.
The `AddEmbedded` extension method takes a class, finds all embedded files from the **assembly of the given class** and registers them to the virtual file system.
`AddEmbedded` can get two optional parameters;
* `baseNamespace`: This may only needed if you didn't configure the `GenerateEmbeddedFilesManifest` step explained above and your root namespace is not empty. In this case, set your root namespace here.
* `baseFolder`: If you don't want to expose all embedded files in the project, but only want to expose a specific folder (and sub folders/files), then you can set the base folder relative to your project root page.
* `baseFolder`: If you don't want to expose all embedded files in the project, but only want to expose a specific folder (and sub folders/files), then you can set the base folder relative to your project root folder.
**Example: Add files under the `MyFiles` folder in the project**
**Example: Add files under the `MyResources` folder in the project**
````csharp
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyModule>(
baseNamespace: "Acme.BookStore.MyFiles",
baseFolder: "/MyFiles"
baseNamespace: "Acme.BookStore",
baseFolder: "/MyResources"
);
});
````
@ -77,56 +77,15 @@ Configure<AbpVirtualFileSystemOptions>(options =>
This example assumes;
* Your project root (default) namespace is `Acme.BookStore`.
* Your project has a folder, named `MyFiles`
* You only want to add `MyFiles` folder to the virtual file system.
* Your project has a folder, named `MyResources`
* You only want to add `MyResources` folder to the virtual file system.
### Dealing With Embedded Files During Development
### IVirtualFileProvider
Embedding a file into an assembly and being able to use it from another project just by referencing the assembly (or adding a NuGet package) is invaluable for creating a re-usable module. However, it makes it a little bit harder to develop the module itself.
Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious.
What is needed is the ability for the application to directly use the physical file at development time and a have a browser refresh reflect any change made in the JavaScript file. The `ReplaceEmbeddedByPhysical` method makes all this possible.
The example below shows an application that depends on a module (`MyModule`) that itself contains embedded files. The application can reach the source code of the module at development time.
After embedding a file into an assembly and registering it to the virtual file system, the `IVirtualFileProvider` interface can be used to get the file or directory contents:
````C#
[DependsOn(typeof(MyModule))]
public class MyWebAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
if (hostingEnvironment.IsDevelopment()) //only for development time
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPhysical<MyModule>(
Path.Combine(
hostingEnvironment.ContentRootPath,
string.Format(
"..{0}MyModuleProject",
Path.DirectorySeparatorChar
)
)
);
});
}
}
}
````
The code above assumes that `MyWebAppModule` and `MyModule` are two different projects in a Visual Studio solution and `MyWebAppModule` depends on the `MyModule`.
> The [application startup template](Startup-Templates/Application.md) already uses this technique for the localization files. So, when you change a localization file it automatically detects the change.
## IVirtualFileProvider
After embedding a file into an assembly and registering it to the virtual file system, the `IVirtualFileProvider` interface can be used to get files or directory contents:
````C#
public class MyService
public class MyService : ITransientDependency
{
private readonly IVirtualFileProvider _virtualFileProvider;
@ -135,7 +94,7 @@ public class MyService
_virtualFileProvider = virtualFileProvider;
}
public void Foo()
public void Test()
{
//Getting a single file
var file = _virtualFileProvider
@ -160,14 +119,14 @@ The Virtual File System is well integrated to ASP.NET Core:
### UseVirtualFiles Middleware
The Virtual Files Middleware is used to serve embedded (js, css, image...) files to clients/browsers just like physical files in the **wwwroot** folder. Add it just after the static file middleware as shown below:
The Virtual Files Middleware is used to serve embedded (js, css, image...) files to clients/browsers just like physical files in the **wwwroot** folder. It also covers the physical files.
Replace the `app.UseStaticFiles()` with the `app.UseVirtualFiles()` in your ASP.NET Core middleware configuration:
````C#
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.
> `UseVirtualFiles()` is already configured for the [application startup template](Startup-Templates/Application.md).
#### Static Virtual File Folders
@ -176,6 +135,62 @@ By default, ASP.NET Core only allows the `wwwroot` folder to contain the static
* Pages
* Views
* Components
* Themes
This allows to add `.js`, `.css`... files near to your `.cshtml` file that is easier to develop and maintain your project.
This allows to add `.js`, `.css`... files near to your `.cshtml` file that is easier to develop and maintain your project.
## Dealing With Embedded Files During Development
Embedding a file into an assembly and being able to use it from another project just by referencing the assembly (or adding a NuGet package) is invaluable for creating a re-usable module. However, it makes it a little bit harder to develop the module itself.
Let's assume that you're developing a module that contains an embedded JavaScript file. Whenever you change this file you must re-compile the project, re-start the application and refresh the browser page to take the change. Obviously, this is very time consuming and tedious.
What is needed is the ability for the application to directly use the physical file at development time and a browser refresh reflects any change made in the JavaScript file. The `ReplaceEmbeddedByPhysical` method makes all this possible.
The example below shows an application that depends on a module (`MyModule`) that contains embedded files. The application can access to the source code of the module at development time.
````C#
[DependsOn(typeof(MyModule))]
public class MyWebAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var hostingEnvironment = context.Services.GetHostingEnvironment();
if (hostingEnvironment.IsDevelopment()) //only for development time
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.ReplaceEmbeddedByPhysical<MyModule>(
Path.Combine(
hostingEnvironment.ContentRootPath,
string.Format(
"..{0}MyModuleProject",
Path.DirectorySeparatorChar
)
)
);
});
}
}
}
````
The code above assumes that `MyWebAppModule` and `MyModule` are two different projects in a Visual Studio solution and `MyWebAppModule` depends on the `MyModule`.
> The [application startup template](Startup-Templates/Application.md) already uses this technique for the localization files. So, when you change a localization file it automatically detects the change.
## Replacing/Overriding Virtual Files
Virtual File System creates a unified file system on runtime, where the actual files are distributed into different modules in the development time.
If two modules adds a file to the same virtual path (like `my-path/my-file.css`), the one added later overrides/replaces the previous one ([module dependency](Module-Development-Basics.md) order determines the order of the files being added).
This feature allows your application to override/replace any virtual file defined a module that is used by your application. This is one of the fundamental extensibility features of the ABP Framework.
So, if you need to replace a file of a module, just create the file in the exactly same path in your module/application
### Physical Files
Physical files always override the virtual files. That means if you put a file under the `/wwwroot/my-folder/my-file.css`, it will override the file in the same location of the virtual file system. So, you need to know the file paths defined in the modules to override them.

17
docs/en/docs-nav.json

@ -214,6 +214,19 @@
"text": "Object to object mapping",
"path": "Object-To-Object-Mapping.md"
},
{
"text": "Email Sending",
"items": [
{
"text": "Email Sending System",
"path": "Emailing.md",
},
{
"text": "MailKit Integration",
"path": "MailKit.md",
}
]
},
{
"text": "BLOB Storing",
"items": [
@ -651,6 +664,10 @@
"text": "Microservice Architecture",
"path": "Microservice-Architecture.md"
},
{
"text": "Preview Releases",
"path": "Previews.md"
},
{
"text": "Nightly Builds",
"path": "Nightly-Builds.md"

BIN
docs/en/images/replace-email-layout.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
docs/en/images/solution-files-non-mvc.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

38
docs/zh-Hans/Background-Jobs-Quartz.md

@ -70,7 +70,43 @@ public class YourModule : AbpModule
}
````
Quartz**默认**将作业与调度信息存储在**内存**中,示例中我们使用[选项模式](Options.md)的预配置将其更改为存储到数据库中. 有关Quartz的更多配置请参阅[Quartz文档](https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/index.html).
从ABP3.1版本开始,我们在 `AbpQuartzOptions` 添加了 `Configurator` 用于配置Quartz. 例:
````csharp
[DependsOn(
//...other dependencies
typeof(AbpBackgroundJobsQuartzModule) //Add the new module dependency
)]
public class YourModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
PreConfigure<AbpQuartzOptions>(options =>
{
options.Configurator = configure =>
{
configure.UsePersistentStore(storeOptions =>
{
storeOptions.UseProperties = true;
storeOptions.UseJsonSerializer();
storeOptions.UseSqlServer(configuration.GetConnectionString("Quartz"));
storeOptions.UseClustering(c =>
{
c.CheckinMisfireThreshold = TimeSpan.FromSeconds(20);
c.CheckinInterval = TimeSpan.FromSeconds(10);
});
});
};
});
}
}
````
> 你可以选择你喜爱的方式来配置Quaratz.
Quartz**默认**将作业与调度信息存储在**内存**中,示例中我们使用[选项模式](Options.md)的预配置将其更改为存储到数据库中. 有关Quartz的更多配置请参阅[Quartz文档](https://www.quartz-scheduler.net/).
## 异常处理

4
docs/zh-Hans/CLI.md

@ -79,7 +79,7 @@ abp new Acme.BookStore
* `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `none`: 无UI. 这个模板还有一些额外的选项:
* `--separate-identity-server`: 将Identity Server应用程序与API host应用程序分开. 如果未指定,则服务器端将只有一个端点.
* `--mobile` 或者 `-m`: 指定移动应用程序框架. 默认框架是 `react-native`. 其他选项:
* `--mobile` 或者 `-m`: 指定移动应用程序框架. 如果未指定,则不会创建任何移动应用程序,其他选项:
* `none`: 不包含移动应用程序.
* `react-native`: React Native.
* `--database-provider` 或者 `-d`: 指定数据库提供程序.默认是 `ef`.其他选项:
@ -251,4 +251,4 @@ abp help [命令名]
````bash
abp help # 显示常规帮助.
abp help new # 显示有关 "New" 命令的帮助.
````
````

12
docs/zh-Hans/CurrentUser.md

@ -86,7 +86,7 @@ namespace AbpDemo
## ICurrentPrincipalAccessor
`ICurrentPrincipalAccessor` 是当需要当前用户的principle时使用的服务(由ABP框架和你的应用程序代码使用).
`ICurrentPrincipalAccessor` 是当需要当前用户的Principal时使用的服务(由ABP框架和你的应用程序代码使用).
对于Web应用程序, 它获取当前 `HttpContext``User` 属性,对于非Web应用程序它将返回 `Thread.CurrentPrincipal`.
@ -114,9 +114,9 @@ public class MyService : ITransientDependency
}
````
### 更改当前Principle
### 更改当前Principal
除了某些高级场景外,你不需要设置或更改当前principle. 如果需要可以使用 `ICurrentPrincipalAccessor``Change` 方法. 它接受一个 `ClaimsPrinciple` 对象并使其成为作用域的"当前"对象.
除了某些高级场景外,你不需要设置或更改当前Principal. 如果需要可以使用 `ICurrentPrincipalAccessor``Change` 方法. 它接受一个 `ClaimsPrincipal` 对象并使其成为作用域的"当前"对象.
示例:
@ -132,7 +132,7 @@ public class MyAppService : ApplicationService
public void Foo()
{
var newPrinciple = new ClaimsPrincipal(
var newPrincipal = new ClaimsPrincipal(
new ClaimsIdentity(
new Claim[]
{
@ -143,7 +143,7 @@ public class MyAppService : ApplicationService
)
);
using (_currentPrincipalAccessor.Change(newPrinciple))
using (_currentPrincipalAccessor.Change(newPrincipal))
{
var userName = CurrentUser.UserName; //returns "john"
//...
@ -164,4 +164,4 @@ public class MyAppService : ApplicationService
* 其他属性,如 `EmailVerified`, `PhoneNumber`, `TenantId` ...是由ABP框架通过尽可能遵循标准名称来定义的.
建议使用这个类的属性来代替声明名称的魔术字符串.
建议使用这个类的属性来代替声明名称的魔术字符串.

11
docs/zh-Hans/Exception-Handling.md

@ -300,3 +300,14 @@ services.Configure<AbpExceptionHttpStatusCodeOptions>(options =>
- 如果请求的实体不存在,则抛出`EntityNotFoundException` 异常. 此异常大多数由 [repositories](Repositories.md) 抛出.
你同样可以在代码中抛出这些类型的异常(虽然很少需要这样做)
## 发送异常详情到客户端
你可以通过 `AbpExceptionHandlingOptions` 类的 `SendExceptionsDetailsToClients` 属性异常发送到客户端:
````csharp
services.Configure<AbpExceptionHandlingOptions>(options =>
{
options.SendExceptionsDetailsToClients = true;
});
````

4
docs/zh-Hans/Getting-Started-React-Native.md

@ -2,7 +2,7 @@
ABP平台提供了[React Native](https://reactnative.dev/)模板用于开发移动应用程序.
当你按照[入门文档](Getting-Started.md)中所述**创建新应用程序**时,解决方案默认将React Native应用程序包含在 `react-native` 文件夹中.
当你按照[入门文档](Getting-Started.md)中所述**创建新应用程序**时, 你应该使用`-m react-native`选项以在解决方案中包含`react-native`项目.
## 配置你的本地IP地址
@ -67,4 +67,4 @@ yarn start
输入用户名 **admin**,密码 **1q2w3E*** 登录到应用程序.
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.
应用程序已经启动并执行,你可以基于该启动模板开发应用程序.

9
docs/zh-Hans/Getting-Started.md

@ -62,10 +62,10 @@ dotnet tool update -g Volo.Abp.Cli
使用ABP CLI的 `new` 命令创建新项目:
````shell
abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}}
abp new Acme.BookStore{{if UI == "NG"}} -u angular {{end}}{{if DB == "Mongo"}} -d mongodb{{end}}{{if Tiered == "Yes" && UI != "NG"}} --tiered {{else if Tiered == "Yes" && UI == "NG"}}--separate-identity-server{{end}} --mobile react-native
````
* 此命令还会在解决方案文件夹内创建一个React Native移动应用程序. 如果你不想要它,可以安全地删除它,或者在 `abp new` 命令中指定 `-m none` 选项,以使其完全不包含在解决方案中.
* 此命令还会在解决方案文件夹内创建一个React Native移动应用程序. 如果你不想要它,可以安全地删除它或从`abp new`命令中删除`--mobile react-native`选项, 以使其完全不包含在解决方案中.
{{ if UI == "NG" }}
@ -351,11 +351,8 @@ yarn start
#### 移动开发
当你创建一个新的应用程序时.该解决方案默认包含 `react-native`文件夹. 这是一个基础的[React Native](https://reactnative.dev/)启动模板,用于开发与基于ABP的后端集成的移动应用程序.
当你创建一个新的应用程序时. 可以添加`-m react-native`选项以在解决方案中包含 `react-native`项目. 这是一个基础的[React Native](https://reactnative.dev/)启动模板,用于开发与基于ABP的后端集成的移动应用程序.
如果你不计划使用React Native开发移动应用程序,你可以忽略并删除 `react-native` 文件夹.
> 你可以在ABP CLI中指定 `-m none` 选项,以使 `react-native` 目录完全不包含在解决方案中
请参阅"[React Native入门](Getting-Started-React-Native.md)"文档了解如何配置和运行React Native应用程序.

16
docs/zh-Hans/Startup-Templates/Application.md

@ -57,6 +57,20 @@ abp new Acme.BookStore -u angular
abp new Acme.BookStore -d mongodb
````
### 指定移动应用程序框架
该模板支持以下移动应用程序框架:
- `react-native`: React Native
使用 `-m` (or `--mobile`) 选项来指定移动应用程序框架:
````bash
abp new Acme.BookStore -m react-native
````
如果未指定, 则不会创建任何移动应用程序.
## 解决方案结构
根据命令的选项,会创建略有不同的解决方案结构.
@ -342,7 +356,7 @@ Home模块是一个可延迟加载的模块, 它加载应用程序的根地址.
### React Native
解决方案将[React Native](https://reactnative.dev/)应用程序作为默认值包含在 `react-native` 文件夹中.
如果使用 `-m react-native` 选项解决方案将[React Native](https://reactnative.dev/)应用程序作为默认值包含在 `react-native` 文件夹中.
服务器端类似于上面描述的解决方案. `*.HttpApi.Host` 的项目提供 API, 所以 React 本机应用程序使用它.

4
docs/zh-Hans/Startup-Templates/Module.md

@ -149,6 +149,10 @@ abp new Acme.IssueManagement -t module --no-ui
`.Web.Host` 项目使用OpenId Connect身份认证从`.IdentityServer`获取当前用户的身份和访问令牌. 然后使用访问令牌调用 `.HttpApi.Host`. HTTP API 服务器使用bearer token验证访问令牌获取当前用户声明并授权用户.
##### 前置条件
* [Redis](https://redis.io/): 应用程序使用Redis做分布式缓存,你需要安装并运行Redis.
##### 如何运行?
你需要按照以下顺序运行应用程序:

14
framework/Volo.Abp.sln

@ -327,6 +327,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Kafka", "src\Volo.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus.Kafka", "src\Volo.Abp.EventBus.Kafka\Volo.Abp.EventBus.Kafka.csproj", "{C1D891B0-AE83-42CB-987D-425A2787DE78}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.GlobalFeatures", "src\Volo.Abp.GlobalFeatures\Volo.Abp.GlobalFeatures.csproj", "{04F44063-C952-403A-815F-EFB778BDA125}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.GlobalFeatures.Tests", "test\Volo.Abp.GlobalFeatures.Tests\Volo.Abp.GlobalFeatures.Tests.csproj", "{231F1581-AA21-44C3-BF27-51EB3AD5355C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -973,6 +977,14 @@ Global
{C1D891B0-AE83-42CB-987D-425A2787DE78}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C1D891B0-AE83-42CB-987D-425A2787DE78}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C1D891B0-AE83-42CB-987D-425A2787DE78}.Release|Any CPU.Build.0 = Release|Any CPU
{04F44063-C952-403A-815F-EFB778BDA125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04F44063-C952-403A-815F-EFB778BDA125}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04F44063-C952-403A-815F-EFB778BDA125}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04F44063-C952-403A-815F-EFB778BDA125}.Release|Any CPU.Build.0 = Release|Any CPU
{231F1581-AA21-44C3-BF27-51EB3AD5355C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{231F1581-AA21-44C3-BF27-51EB3AD5355C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{231F1581-AA21-44C3-BF27-51EB3AD5355C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{231F1581-AA21-44C3-BF27-51EB3AD5355C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1138,6 +1150,8 @@ Global
{2CD3B26A-CA81-4279-8D5D-6A594517BB3F} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{2A864049-9CD5-4493-8CDB-C408474D43D4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{C1D891B0-AE83-42CB-987D-425A2787DE78} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{04F44063-C952-403A-815F-EFB778BDA125} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{231F1581-AA21-44C3-BF27-51EB3AD5355C} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy.csproj

@ -20,8 +20,12 @@
<ItemGroup>
<EmbeddedResource Include="Pages\**\*.js" />
<EmbeddedResource Include="Volo\Abp\AspNetCore\Mvc\UI\MultiTenancy\Localization\*.json" />
<EmbeddedResource Include="Components\**\*.js" />
<EmbeddedResource Include="Components\**\*.css" />
<Content Remove="Pages\**\*.js" />
<Content Remove="Volo\Abp\AspNetCore\Mvc\UI\MultiTenancy\Localization\*.json" />
<Content Remove="Components\**\*.js" />
<Content Remove="Components\**\*.css" />
</ItemGroup>
<ItemGroup>

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

@ -8,6 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Luxon
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/libs/luxon/luxon.min.js");
context.Files.AddIfNotContains("/libs/abp/luxon/abp.luxon.js");
}
}
}

15
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/AbpMvcUiOptions.cs

@ -0,0 +1,15 @@
namespace Volo.Abp.AspNetCore.Mvc.UI
{
public class AbpMvcUiOptions
{
/// <summary>
/// Default value: "/Account/Login".
/// </summary>
public string LoginUrl { get; set; } = "/Account/Login";
/// <summary>
/// Default value: "/Account/Logout".
/// </summary>
public string LogoutUrl { get; set; } = "/Account/Logout";
}
}

1
framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -21,6 +21,7 @@
<ProjectReference Include="..\Volo.Abp.ApiVersioning.Abstractions\Volo.Abp.ApiVersioning.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.Contracts\Volo.Abp.AspNetCore.Mvc.Contracts.csproj" />
<ProjectReference Include="..\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj" />
<ProjectReference Include="..\Volo.Abp.GlobalFeatures\Volo.Abp.GlobalFeatures.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
<ProjectReference Include="..\Volo.Abp.UI\Volo.Abp.UI.csproj" />
</ItemGroup>

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

@ -10,14 +10,12 @@ using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Routing;
@ -34,6 +32,7 @@ using Volo.Abp.AspNetCore.VirtualFileSystem;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http;
using Volo.Abp.DynamicProxy;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
@ -46,7 +45,8 @@ namespace Volo.Abp.AspNetCore.Mvc
typeof(AbpLocalizationModule),
typeof(AbpApiVersioningAbstractionsModule),
typeof(AbpAspNetCoreMvcContractsModule),
typeof(AbpUiModule)
typeof(AbpUiModule),
typeof(AbpGlobalFeaturesModule)
)]
public class AbpAspNetCoreMvcModule : AbpModule
{
@ -168,7 +168,7 @@ namespace Volo.Abp.AspNetCore.Mvc
context.Services.Replace(ServiceDescriptor.Singleton<IValidationAttributeAdapterProvider, AbpValidationAttributeAdapterProvider>());
context.Services.AddSingleton<ValidationAttributeAdapterProvider>();
Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(context.Services);

5
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs

@ -4,6 +4,7 @@ using Volo.Abp.AspNetCore.Mvc.Auditing;
using Volo.Abp.AspNetCore.Mvc.Conventions;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.AspNetCore.Mvc.Features;
using Volo.Abp.AspNetCore.Mvc.GlobalFeatures;
using Volo.Abp.AspNetCore.Mvc.ModelBinding;
using Volo.Abp.AspNetCore.Mvc.Response;
using Volo.Abp.AspNetCore.Mvc.Uow;
@ -29,6 +30,7 @@ namespace Volo.Abp.AspNetCore.Mvc
private static void AddActionFilters(MvcOptions options)
{
options.Filters.AddService(typeof(GlobalFeatureActionFilter));
options.Filters.AddService(typeof(AbpAuditActionFilter));
options.Filters.AddService(typeof(AbpNoContentActionFilter));
options.Filters.AddService(typeof(AbpFeatureActionFilter));
@ -39,6 +41,7 @@ namespace Volo.Abp.AspNetCore.Mvc
private static void AddPageFilters(MvcOptions options)
{
options.Filters.AddService(typeof(GlobalFeaturePageFilter));
options.Filters.AddService(typeof(AbpExceptionPageFilter));
options.Filters.AddService(typeof(AbpAuditPageFilter));
options.Filters.AddService(typeof(AbpFeaturePageFilter));
@ -58,4 +61,4 @@ namespace Volo.Abp.AspNetCore.Mvc
);
}
}
}
}

74
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Conventions/AbpServiceConvention.cs

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Http;
using Volo.Abp.Http.Modeling;
using Volo.Abp.Http.ProxyScripting.Generators;
@ -142,18 +143,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
if (controller.ApiExplorer.IsVisible == null)
{
var controllerType = controller.ControllerType.AsType();
var remoteServiceAtt = ReflectionHelper.GetSingleAttributeOrDefault<RemoteServiceAttribute>(controllerType.GetTypeInfo());
if (remoteServiceAtt != null)
{
controller.ApiExplorer.IsVisible =
remoteServiceAtt.IsEnabledFor(controllerType) &&
remoteServiceAtt.IsMetadataEnabledFor(controllerType);
}
else
{
controller.ApiExplorer.IsVisible = true;
}
controller.ApiExplorer.IsVisible = IsVisibleRemoteService(controller.ControllerType);
}
foreach (var action in controller.Actions)
@ -164,16 +154,18 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
protected virtual void ConfigureApiExplorer(ActionModel action)
{
if (action.ApiExplorer.IsVisible == null)
if (action.ApiExplorer.IsVisible != null)
{
var remoteServiceAtt = ReflectionHelper.GetSingleAttributeOrDefault<RemoteServiceAttribute>(action.ActionMethod);
if (remoteServiceAtt != null)
{
action.ApiExplorer.IsVisible =
remoteServiceAtt.IsEnabledFor(action.ActionMethod) &&
remoteServiceAtt.IsMetadataEnabledFor(action.ActionMethod);
}
return;
}
var visible = IsVisibleRemoteServiceMethod(action.ActionMethod);
if (visible == null)
{
return;
}
action.ApiExplorer.IsVisible = visible;
}
protected virtual void ConfigureSelector(ControllerModel controller, [CanBeNull] ConventionalControllerSetting configuration)
@ -397,5 +389,45 @@ namespace Volo.Abp.AspNetCore.Mvc.Conventions
{
return typeof(IRemoteService).GetTypeInfo().IsAssignableFrom(controllerType);
}
protected virtual bool IsVisibleRemoteService(Type controllerType)
{
if (!IsGlobalFeatureEnabled(controllerType))
{
return false;
}
var attribute = ReflectionHelper.GetSingleAttributeOrDefault<RemoteServiceAttribute>(controllerType);
if (attribute == null)
{
return true;
}
return attribute.IsEnabledFor(controllerType) &&
attribute.IsMetadataEnabledFor(controllerType);
}
protected virtual bool? IsVisibleRemoteServiceMethod(MethodInfo method)
{
var attribute = ReflectionHelper.GetSingleAttributeOrDefault<RemoteServiceAttribute>(method);
if (attribute == null)
{
return null;
}
return attribute.IsEnabledFor(method) &&
attribute.IsMetadataEnabledFor(method);
}
protected virtual bool IsGlobalFeatureEnabled(Type controllerType)
{
var attribute = ReflectionHelper.GetSingleAttributeOrDefault<RequiresGlobalFeatureAttribute>(controllerType);
if (attribute == null)
{
return true;
}
return GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName());
}
}
}
}

47
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeatureActionFilter.cs

@ -0,0 +1,47 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures
{
public class GlobalFeatureActionFilter : IAsyncActionFilter, ITransientDependency
{
public ILogger<GlobalFeatureActionFilter> Logger { get; set; }
public GlobalFeatureActionFilter()
{
Logger = NullLogger<GlobalFeatureActionFilter>.Instance;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.ActionDescriptor.IsControllerAction())
{
await next();
return;
}
if (!IsGlobalFeatureEnabled(context.Controller.GetType(), out var attribute))
{
Logger.LogWarning($"The '{context.Controller.GetType().FullName}' controller needs to enable '{attribute.Name}' feature.");
context.Result = new NotFoundResult();
return;
}
await next();
}
protected virtual bool IsGlobalFeatureEnabled(Type controllerType, out RequiresGlobalFeatureAttribute attribute)
{
attribute = ReflectionHelper.GetSingleAttributeOrDefault<RequiresGlobalFeatureAttribute>(controllerType);
return attribute == null || GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName());
}
}
}

52
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/GlobalFeatures/GlobalFeaturePageFilter.cs

@ -0,0 +1,52 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Volo.Abp.DependencyInjection;
using Volo.Abp.GlobalFeatures;
using Volo.Abp.Reflection;
namespace Volo.Abp.AspNetCore.Mvc.GlobalFeatures
{
public class GlobalFeaturePageFilter: IAsyncPageFilter, ITransientDependency
{
public ILogger<GlobalFeaturePageFilter> Logger { get; set; }
public GlobalFeaturePageFilter()
{
Logger = NullLogger<GlobalFeaturePageFilter>.Instance;
}
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
{
return Task.CompletedTask;
}
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
{
if (context.HandlerInstance == null || !context.ActionDescriptor.IsPageAction())
{
await next();
return;
}
if (!IsGlobalFeatureEnabled(context.HandlerInstance.GetType(), out var attribute))
{
Logger.LogWarning($"The '{context.HandlerInstance.GetType().FullName}' page needs to enable '{attribute.Name}' feature.");
context.Result = new NotFoundResult();
return;
}
await next();
}
protected virtual bool IsGlobalFeatureEnabled(Type controllerType, out RequiresGlobalFeatureAttribute attribute)
{
attribute = ReflectionHelper.GetSingleAttributeOrDefault<RequiresGlobalFeatureAttribute>(controllerType);
return attribute == null || GlobalFeatureManager.Instance.IsEnabled(attribute.GetFeatureName());
}
}
}

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

@ -9,7 +9,9 @@ namespace Volo.Abp.ObjectExtending
private static readonly Type[] DateTimeTypes =
{
typeof(DateTime),
typeof(DateTimeOffset)
typeof(DateTime?),
typeof(DateTimeOffset),
typeof(DateTimeOffset?)
};
public static bool IsDate(this IBasicObjectExtensionPropertyInfo property)

51
framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/RequestLocalization/DefaultAbpRequestLocalizationOptionsProvider.cs

@ -50,35 +50,38 @@ namespace Microsoft.AspNetCore.RequestLocalization
{
using (await _syncSemaphore.LockAsync())
{
using (var serviceScope = _serviceProviderFactory.CreateScope())
if (_requestLocalizationOptions == null)
{
var languageProvider = serviceScope.ServiceProvider.GetRequiredService<ILanguageProvider>();
var settingProvider = serviceScope.ServiceProvider.GetRequiredService<ISettingProvider>();
using (var serviceScope = _serviceProviderFactory.CreateScope())
{
var languageProvider = serviceScope.ServiceProvider.GetRequiredService<ILanguageProvider>();
var settingProvider = serviceScope.ServiceProvider.GetRequiredService<ISettingProvider>();
var languages = await languageProvider.GetLanguagesAsync();
var defaultLanguage = await settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage);
var languages = await languageProvider.GetLanguagesAsync();
var defaultLanguage = await settingProvider.GetOrNullAsync(LocalizationSettingNames.DefaultLanguage);
var options = !languages.Any()
? new RequestLocalizationOptions()
: new RequestLocalizationOptions
{
DefaultRequestCulture = DefaultGetRequestCulture(defaultLanguage, languages),
var options = !languages.Any()
? new RequestLocalizationOptions()
: new RequestLocalizationOptions
{
DefaultRequestCulture = DefaultGetRequestCulture(defaultLanguage, languages),
SupportedCultures = languages
.Select(l => l.CultureName)
.Distinct()
.Select(c => new CultureInfo(c))
.ToArray(),
SupportedCultures = languages
.Select(l => l.CultureName)
.Distinct()
.Select(c => new CultureInfo(c))
.ToArray(),
SupportedUICultures = languages
.Select(l => l.UiCultureName)
.Distinct()
.Select(c => new CultureInfo(c))
.ToArray()
};
SupportedUICultures = languages
.Select(l => l.UiCultureName)
.Distinct()
.Select(c => new CultureInfo(c))
.ToArray()
};
_optionsAction?.Invoke(options);
_requestLocalizationOptions = options;
_optionsAction?.Invoke(options);
_requestLocalizationOptions = options;
}
}
}
}
@ -98,4 +101,4 @@ namespace Microsoft.AspNetCore.RequestLocalization
return new RequestCulture(cultureName, uiCultureName);
}
}
}
}

7
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/AbpExceptionHandlingOptions.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class AbpExceptionHandlingOptions
{
public bool SendExceptionsDetailsToClients { get; set; } = false;
}
}

9
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/ExceptionHandling/DefaultExceptionToErrorInfoConverter.cs

@ -19,20 +19,21 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
{
public class DefaultExceptionToErrorInfoConverter : IExceptionToErrorInfoConverter, ITransientDependency
{
public bool SendAllExceptionsToClients { get; set; } = false;
protected AbpExceptionLocalizationOptions LocalizationOptions { get; }
protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; }
protected IStringLocalizerFactory StringLocalizerFactory { get; }
protected IStringLocalizer<AbpUiResource> L { get; }
protected IServiceProvider ServiceProvider { get; }
public DefaultExceptionToErrorInfoConverter(
IOptions<AbpExceptionLocalizationOptions> localizationOptions,
IOptions<AbpExceptionHandlingOptions> exceptionHandlingOptions,
IStringLocalizerFactory stringLocalizerFactory,
IStringLocalizer<AbpUiResource> abpUiStringLocalizer,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
ExceptionHandlingOptions = exceptionHandlingOptions.Value;
StringLocalizerFactory = stringLocalizerFactory;
L = abpUiStringLocalizer;
LocalizationOptions = localizationOptions.Value;
@ -52,7 +53,7 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
protected virtual RemoteServiceErrorInfo CreateErrorInfoWithoutCode(Exception exception)
{
if (SendAllExceptionsToClients)
if (ExceptionHandlingOptions.SendExceptionsDetailsToClients)
{
return CreateDetailedErrorInfoFromException(exception);
}
@ -293,4 +294,4 @@ namespace Volo.Abp.AspNetCore.ExceptionHandling
return detailBuilder.ToString();
}
}
}
}

3
framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AbpAspNetCoreContentOptions.cs

@ -400,7 +400,8 @@ namespace Volo.Abp.AspNetCore.VirtualFileSystem
{
"/Pages",
"/Views",
"/Themes"
"/Themes",
"/Components"
};
AllowedExtraWebContentFileExtensions = ContentTypeMaps.Select(x => x.Key).ToList();

0
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditScope.cs → framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs

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

@ -294,7 +294,7 @@ namespace Volo.Abp.Cli.Commands
protected virtual MobileApp GetMobilePreference(CommandLineArgs commandLineArgs)
{
var optionValue = commandLineArgs.Options.GetOrNull(Options.Mobile.Short, Options.Mobile.Long);
var template = commandLineArgs.Options.GetOrNull(Options.Template.Short, Options.Template.Long);
switch (optionValue)
{
case "none":
@ -302,7 +302,7 @@ namespace Volo.Abp.Cli.Commands
case "react-native":
return MobileApp.ReactNative;
default:
return ConsoleTemplate.TemplateName == template ? MobileApp.None : MobileApp.ReactNative;
return MobileApp.None;
}
}

13
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/UpdateCommand.cs

@ -53,9 +53,6 @@ namespace Volo.Abp.Cli.Commands
private async Task UpdateNugetPackages(CommandLineArgs commandLineArgs, string directory)
{
var includePreviews = commandLineArgs
.Options
.GetOrNull(Options.IncludePreviews.Short, Options.IncludePreviews.Long) != null;
var solution = commandLineArgs.Options.GetOrNull(Options.SolutionName.Short, Options.SolutionName.Long);
if (solution.IsNullOrWhiteSpace())
@ -69,7 +66,7 @@ namespace Volo.Abp.Cli.Commands
{
var solutionName = Path.GetFileName(solution).RemovePostFix(".sln");
await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, includePreviews, checkAll: checkAll);
await _nugetPackagesVersionUpdater.UpdateSolutionAsync(solution, checkAll: checkAll);
Logger.LogInformation($"Volo packages are updated in {solutionName} solution.");
return;
@ -81,7 +78,7 @@ namespace Volo.Abp.Cli.Commands
{
var projectName = Path.GetFileName(project).RemovePostFix(".csproj");
await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, includePreviews, checkAll: checkAll);
await _nugetPackagesVersionUpdater.UpdateProjectAsync(project, checkAll: checkAll);
Logger.LogInformation($"Volo packages are updated in {projectName} project.");
return;
@ -141,12 +138,6 @@ namespace Volo.Abp.Cli.Commands
public const string Long = "solution-name";
}
public static class IncludePreviews
{
public const string Short = "p";
public const string Long = "include-previews";
}
public static class Packages
{
public const string Npm = "npm";

14
framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/EfCoreMigrationAdder.cs

@ -7,22 +7,12 @@ namespace Volo.Abp.Cli.ProjectModification
{
public class EfCoreMigrationAdder : ITransientDependency
{
public void AddMigration(string csprojFile, string module, string startupProject, bool updateDatabase = true)
public void AddMigration(string dbMigrationsCsprojFile, string module, string startupProject)
{
var moduleName = ParseModuleName(module);
var migrationName = "Added_" + moduleName + "_Module" + GetUniquePostFix();
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" && dotnet ef migrations add " + migrationName + GetStartupProjectOption(startupProject));
if (updateDatabase)
{
UpdateDatabase(csprojFile, startupProject);
}
}
protected void UpdateDatabase(string csprojFile, string startupProject)
{
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(csprojFile) + "\" && dotnet ef database update" + GetStartupProjectOption(startupProject));
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigrationsCsprojFile) + "\" && dotnet ef migrations add " + migrationName + GetStartupProjectOption(startupProject));
}
protected virtual string ParseModuleName(string fullModuleName)

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

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Volo.Abp.Cli.Commands.Services;
using Volo.Abp.Cli.Http;
using Volo.Abp.Cli.ProjectBuilding;
using Volo.Abp.Cli.Utils;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Json;
@ -214,6 +215,11 @@ namespace Volo.Abp.Cli.ProjectModification
{
if (string.IsNullOrWhiteSpace(module.EfCoreConfigureMethodName))
{
if (!skipDbMigrations)
{
RunMigrator(projectFiles);
}
return;
}
@ -227,6 +233,12 @@ namespace Volo.Abp.Cli.ProjectModification
if (dbMigrationsProject == null)
{
Logger.LogDebug("Solution doesn't have a \".DbMigrations\" project.");
if (!skipDbMigrations)
{
RunMigrator(projectFiles);
}
return;
}
@ -240,9 +252,24 @@ namespace Volo.Abp.Cli.ProjectModification
var addedNewBuilder = DbContextFileBuilderConfigureAdder.Add(dbContextFile, module.EfCoreConfigureMethodName);
if (addedNewBuilder && !skipDbMigrations)
if (!skipDbMigrations)
{
if (addedNewBuilder)
{
EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject);
}
RunMigrator(projectFiles);
}
}
protected virtual async Task RunMigrator(string[] projectFiles)
{
var dbMigratorProject = projectFiles.FirstOrDefault(p => p.EndsWith(".DbMigrator.csproj"));
if (!string.IsNullOrEmpty(dbMigratorProject))
{
EfCoreMigrationAdder.AddMigration(dbMigrationsProject, module.Name, startupProject);
CmdHelper.RunCmd("cd \"" + Path.GetDirectoryName(dbMigratorProject) + "\" && dotnet run");
}
}

269
framework/src/Volo.Abp.Core/System/Linq/PredicateOperator.cs

@ -0,0 +1,269 @@
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using JetBrains.Annotations;
namespace System.Linq
{
// Codes below are taken from https://github.com/scottksmith95/LINQKit project.
/// <summary> The Predicate Operator </summary>
public enum PredicateOperator
{
/// <summary> The "Or" </summary>
Or,
/// <summary> The "And" </summary>
And
}
/// <summary>
/// See http://www.albahari.com/expressions for information and examples.
/// </summary>
public static class PredicateBuilder
{
private class RebindParameterVisitor : ExpressionVisitor
{
private readonly ParameterExpression _oldParameter;
private readonly ParameterExpression _newParameter;
public RebindParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
{
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node == _oldParameter ? _newParameter : base.VisitParameter(node);
}
}
/// <summary> Start an expression </summary>
public static ExpressionStarter<T> New<T>(Expression<Func<T, bool>> expr = null)
{
return new ExpressionStarter<T>(expr);
}
/// <summary> Create an expression with a stub expression true or false to use when the expression is not yet started. </summary>
public static ExpressionStarter<T> New<T>(bool defaultExpression)
{
return new ExpressionStarter<T>(defaultExpression);
}
/// <summary> OR </summary>
public static Expression<Func<T, bool>> Or<T>([NotNull] this Expression<Func<T, bool>> expr1,
[NotNull] Expression<Func<T, bool>> expr2)
{
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, expr2Body), expr1.Parameters);
}
/// <summary> AND </summary>
public static Expression<Func<T, bool>> And<T>([NotNull] this Expression<Func<T, bool>> expr1,
[NotNull] Expression<Func<T, bool>> expr2)
{
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2Body), expr1.Parameters);
}
/// <summary>
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="first">The source Predicate.</param>
/// <param name="second">The second Predicate.</param>
/// <param name="operator">The Operator (can be "And" or "Or").</param>
/// <returns>Expression{Func{T, bool}}</returns>
public static Expression<Func<T, bool>> Extend<T>([NotNull] this Expression<Func<T, bool>> first,
[NotNull] Expression<Func<T, bool>> second, PredicateOperator @operator = PredicateOperator.Or)
{
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second);
}
/// <summary>
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
/// </summary>
/// <typeparam name="T">The type</typeparam>
/// <param name="first">The source Predicate.</param>
/// <param name="second">The second Predicate.</param>
/// <param name="operator">The Operator (can be "And" or "Or").</param>
/// <returns>Expression{Func{T, bool}}</returns>
public static Expression<Func<T, bool>> Extend<T>([NotNull] this ExpressionStarter<T> first,
[NotNull] Expression<Func<T, bool>> second, PredicateOperator @operator = PredicateOperator.Or)
{
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second);
}
}
/// <summary>
/// ExpressionStarter{T} which eliminates the default 1=0 or 1=1 stub expressions
/// </summary>
/// <typeparam name="T">The type</typeparam>
public class ExpressionStarter<T>
{
public ExpressionStarter() : this(false)
{
}
public ExpressionStarter(bool defaultExpression)
{
if (defaultExpression)
{
DefaultExpression = f => true;
}
else
{
DefaultExpression = f => false;
}
}
public ExpressionStarter(Expression<Func<T, bool>> exp) : this(false)
{
_predicate = exp;
}
/// <summary>The actual Predicate. It can only be set by calling Start.</summary>
private Expression<Func<T, bool>> Predicate =>
(IsStarted || !UseDefaultExpression) ? _predicate : DefaultExpression;
private Expression<Func<T, bool>> _predicate;
/// <summary>Determines if the predicate is started.</summary>
public bool IsStarted => _predicate != null;
/// <summary> A default expression to use only when the expression is null </summary>
public bool UseDefaultExpression => DefaultExpression != null;
/// <summary>The default expression</summary>
public Expression<Func<T, bool>> DefaultExpression { get; set; }
/// <summary>Set the Expression predicate</summary>
/// <param name="exp">The first expression</param>
public Expression<Func<T, bool>> Start(Expression<Func<T, bool>> exp)
{
if (IsStarted)
{
throw new Exception("Predicate cannot be started again.");
}
return _predicate = exp;
}
/// <summary>Or</summary>
public Expression<Func<T, bool>> Or([NotNull] Expression<Func<T, bool>> expr2)
{
return (IsStarted) ? _predicate = Predicate.Or(expr2) : Start(expr2);
}
/// <summary>And</summary>
public Expression<Func<T, bool>> And([NotNull] Expression<Func<T, bool>> expr2)
{
return (IsStarted) ? _predicate = Predicate.And(expr2) : Start(expr2);
}
/// <summary> Show predicate string </summary>
public override string ToString()
{
return Predicate?.ToString();
}
#region Implicit Operators
/// <summary>
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
/// </summary>
/// <param name="right"></param>
public static implicit operator Expression<Func<T, bool>>(ExpressionStarter<T> right)
{
return right?.Predicate;
}
/// <summary>
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
/// </summary>
/// <param name="right"></param>
public static implicit operator Func<T, bool>(ExpressionStarter<T> right)
{
return right == null ? null :
(right.IsStarted || right.UseDefaultExpression) ? right.Predicate.Compile() : null;
}
/// <summary>
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
/// </summary>
/// <param name="right"></param>
public static implicit operator ExpressionStarter<T>(Expression<Func<T, bool>> right)
{
return right == null ? null : new ExpressionStarter<T>(right);
}
#endregion
#region Implement Expression<TDelagate> methods and properties
#if !(NET35)
/// <summary></summary>
public Func<T, bool> Compile()
{
return Predicate.Compile();
}
#endif
#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP)
/// <summary></summary>
public Func<T, bool> Compile(DebugInfoGenerator debugInfoGenerator) { return Predicate.Compile(debugInfoGenerator); }
/// <summary></summary>
public Expression<Func<T, bool>> Update(Expression body, IEnumerable<ParameterExpression> parameters) { return Predicate.Update(body, parameters); }
#endif
#endregion
#region Implement LamdaExpression methods and properties
/// <summary></summary>
public Expression Body => Predicate.Body;
/// <summary></summary>
public ExpressionType NodeType => Predicate.NodeType;
/// <summary></summary>
public ReadOnlyCollection<ParameterExpression> Parameters => Predicate.Parameters;
/// <summary></summary>
public Type Type => Predicate.Type;
#if !(NET35)
/// <summary></summary>
public string Name => Predicate.Name;
/// <summary></summary>
public Type ReturnType => Predicate.ReturnType;
/// <summary></summary>
public bool TailCall => Predicate.TailCall;
#endif
#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP)
/// <summary></summary>
public void CompileToMethod(MethodBuilder method) { Predicate.CompileToMethod(method); }
/// <summary></summary>
public void CompileToMethod(MethodBuilder method, DebugInfoGenerator debugInfoGenerator) { Predicate.CompileToMethod(method, debugInfoGenerator); }
#endif
#endregion
#region Implement Expression methods and properties
#if !(NET35)
/// <summary></summary>
public virtual bool CanReduce => Predicate.CanReduce;
#endif
#endregion
}
}

18
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJob.cs

@ -1,10 +1,11 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.Emailing
{
public class BackgroundEmailSendingJob : BackgroundJob<BackgroundEmailSendingJobArgs>, ITransientDependency
public class BackgroundEmailSendingJob : AsyncBackgroundJob<BackgroundEmailSendingJobArgs>, ITransientDependency
{
protected IEmailSender EmailSender { get; }
@ -13,9 +14,16 @@ namespace Volo.Abp.Emailing
EmailSender = emailSender;
}
public override void Execute(BackgroundEmailSendingJobArgs args)
public override async Task ExecuteAsync(BackgroundEmailSendingJobArgs args)
{
AsyncHelper.RunSync(() => EmailSender.SendAsync(args.To, args.Subject, args.Body, args.IsBodyHtml));
if (args.From.IsNullOrWhiteSpace())
{
await EmailSender.SendAsync(args.To, args.Subject, args.Body, args.IsBodyHtml);
}
else
{
await EmailSender.SendAsync(args.From, args.To, args.Subject, args.Body, args.IsBodyHtml);
}
}
}
}
}

6
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/BackgroundEmailSendingJobArgs.cs

@ -5,12 +5,14 @@ namespace Volo.Abp.Emailing
[Serializable]
public class BackgroundEmailSendingJobArgs
{
public string From { get; set; }
public string To { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
/// <summary>
/// Default: true.
/// </summary>
@ -18,4 +20,4 @@ namespace Volo.Abp.Emailing
//TODO: Add other properties and attachments
}
}
}

22
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/EmailSenderBase.cs

@ -69,6 +69,26 @@ namespace Volo.Abp.Emailing
);
}
public async Task QueueAsync(string from, string to, string subject, string body, bool isBodyHtml = true)
{
if (!BackgroundJobManager.IsAvailable())
{
await SendAsync(from, to, subject, body, isBodyHtml);
return;
}
await BackgroundJobManager.EnqueueAsync(
new BackgroundEmailSendingJobArgs
{
From = from,
To = to,
Subject = subject,
Body = body,
IsBodyHtml = isBodyHtml
}
);
}
/// <summary>
/// Should implement this method to send email in derived classes.
/// </summary>
@ -108,4 +128,4 @@ namespace Volo.Abp.Emailing
}
}
}
}
}

40
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/IEmailSender.cs

@ -11,12 +11,23 @@ namespace Volo.Abp.Emailing
/// <summary>
/// Sends an email.
/// </summary>
Task SendAsync(string to, string subject, string body, bool isBodyHtml = true);
Task SendAsync(
string to,
string subject,
string body,
bool isBodyHtml = true
);
/// <summary>
/// Sends an email.
/// </summary>
Task SendAsync(string from, string to, string subject, string body, bool isBodyHtml = true);
Task SendAsync(
string from,
string to,
string subject,
string body,
bool isBodyHtml = true
);
/// <summary>
/// Sends an email.
@ -24,14 +35,33 @@ namespace Volo.Abp.Emailing
/// <param name="mail">Mail to be sent</param>
/// <param name="normalize">
/// Should normalize email?
/// If true, it sets sender address/name if it's not set before and makes mail encoding UTF-8.
/// If true, it sets sender address/name if it's not set before and makes mail encoding UTF-8.
/// </param>
Task SendAsync(MailMessage mail, bool normalize = true);
Task SendAsync(
MailMessage mail,
bool normalize = true
);
/// <summary>
/// Adds an email to queue to send via background jobs.
/// </summary>
Task QueueAsync(string to, string subject, string body, bool isBodyHtml = true);
Task QueueAsync(
string to,
string subject,
string body,
bool isBodyHtml = true
);
/// <summary>
/// Adds an email to queue to send via background jobs.
/// </summary>
Task QueueAsync(
string from,
string to,
string subject,
string body,
bool isBodyHtml = true
);
//TODO: Add other Queue methods too. Problem: MailMessage is not serializable so can not be used in background jobs.
}

1
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Smtp/SmtpEmailSender.cs

@ -4,7 +4,6 @@ using System.Net.Mail;
using System.Threading.Tasks;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.Emailing.Smtp
{

2
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionInfoExtensions.cs

@ -6,6 +6,7 @@ namespace Volo.Abp.ObjectExtending
{
public static class EfCoreObjectExtensionInfoExtensions
{
[Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")]
public static ObjectExtensionInfo MapEfCoreProperty<TProperty>(
[NotNull] this ObjectExtensionInfo objectExtensionInfo,
[NotNull] string propertyName,
@ -18,6 +19,7 @@ namespace Volo.Abp.ObjectExtending
);
}
[Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")]
public static ObjectExtensionInfo MapEfCoreProperty(
[NotNull] this ObjectExtensionInfo objectExtensionInfo,
[NotNull] Type propertyType,

2
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionManagerExtensions.cs

@ -39,6 +39,7 @@ namespace Volo.Abp.ObjectExtending
);
}
[Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")]
public static ObjectExtensionManager MapEfCoreProperty<TEntity, TProperty>(
[NotNull] this ObjectExtensionManager objectExtensionManager,
[NotNull] string propertyName,
@ -53,6 +54,7 @@ namespace Volo.Abp.ObjectExtending
);
}
[Obsolete("Use MapEfCoreProperty with EntityTypeAndPropertyBuildAction parameters.")]
public static ObjectExtensionManager MapEfCoreProperty(
[NotNull] this ObjectExtensionManager objectExtensionManager,
[NotNull] Type entityType,

3
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/EfCoreObjectExtensionPropertyInfoExtensions.cs

@ -23,6 +23,7 @@ namespace Volo.Abp.ObjectExtending
return propertyExtension;
}
[Obsolete("Use MapEfCore with EntityTypeAndPropertyBuildAction parameters.")]
[NotNull]
public static ObjectExtensionPropertyInfo MapEfCore(
[NotNull] this ObjectExtensionPropertyInfo propertyExtension,
@ -49,7 +50,7 @@ namespace Volo.Abp.ObjectExtending
propertyExtension.Configuration[EfCorePropertyConfigurationName] =
new ObjectExtensionPropertyInfoEfCoreMappingOptions(
propertyExtension,
entityTypeAndPropertyBuildAction: entityTypeAndPropertyBuildAction
entityTypeAndPropertyBuildAction
);
return propertyExtension;

17
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoEfCoreMappingOptions.cs

@ -12,12 +12,14 @@ namespace Volo.Abp.ObjectExtending
[NotNull]
public ObjectExtensionInfo ObjectExtension => ExtensionProperty.ObjectExtension;
[Obsolete("Use EntityTypeAndPropertyBuildAction property.")]
[CanBeNull]
public Action<PropertyBuilder> PropertyBuildAction { get; set; }
[CanBeNull]
public Action<EntityTypeBuilder, PropertyBuilder> EntityTypeAndPropertyBuildAction { get; set; }
[Obsolete("Use other constructors.")]
public ObjectExtensionPropertyInfoEfCoreMappingOptions(
[NotNull] ObjectExtensionPropertyInfo extensionProperty,
[CanBeNull] Action<PropertyBuilder> propertyBuildAction = null,
@ -28,5 +30,20 @@ namespace Volo.Abp.ObjectExtending
PropertyBuildAction = propertyBuildAction;
EntityTypeAndPropertyBuildAction = entityTypeAndPropertyBuildAction;
}
public ObjectExtensionPropertyInfoEfCoreMappingOptions(
[NotNull] ObjectExtensionPropertyInfo extensionProperty)
{
ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty));
}
public ObjectExtensionPropertyInfoEfCoreMappingOptions(
[NotNull] ObjectExtensionPropertyInfo extensionProperty,
[CanBeNull] Action<EntityTypeBuilder, PropertyBuilder> entityTypeAndPropertyBuildAction)
{
ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty));
EntityTypeAndPropertyBuildAction = entityTypeAndPropertyBuildAction;
}
}
}

3
framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
framework/src/Volo.Abp.GlobalFeatures/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

3
framework/src/Volo.Abp.GlobalFeatures/Properties/AssemblyInfo.cs

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo("Volo.Abp.GlobalFeatures.Tests")]

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

Loading…
Cancel
Save