diff --git a/README.md b/README.md index ebba20da41..c8526a9daa 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![MyGet (nightly builds)](https://img.shields.io/myget/abp-nightly/vpre/Volo.Abp.svg?style=flat-square)](https://docs.abp.io/en/abp/latest/Nightly-Builds) [![NuGet Download](https://img.shields.io/nuget/dt/Volo.Abp.Core.svg?style=flat-square)](https://www.nuget.org/packages/Volo.Abp.Core) -ABP Framework is a complete **infrastructure** based on the **ASP.NET Core** to create **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**. +ABP Framework is a complete **infrastructure** based on the **ASP.NET Core** to create **modern web applications** and **APIs** by following the software development **best practices** and the **latest technologies**. Check out https://abp.io ## Getting Started diff --git a/docs/en/Modules/OpenIddict.md b/docs/en/Modules/OpenIddict.md index 6df46dfcd2..951a745aab 100644 --- a/docs/en/Modules/OpenIddict.md +++ b/docs/en/Modules/OpenIddict.md @@ -81,7 +81,7 @@ PreConfigure(builder => }); ``` -#### AbpOpenIddictOptions +#### AbpOpenIddictAspNetCoreOptions `UpdateAbpClaimTypes(default: true)`: Updates AbpClaimTypes to be compatible with identity server claims. `AddDevelopmentEncryptionAndSigningCertificate(default: true)`: Registers (and generates if necessary) a user-specific development encryption/development signing certificate. diff --git a/docs/en/Road-Map.md b/docs/en/Road-Map.md index e7aa25fb0a..55a99be8b7 100644 --- a/docs/en/Road-Map.md +++ b/docs/en/Road-Map.md @@ -4,15 +4,18 @@ This document provides a road map, release schedule and planned features for the ## Next Versions -### v5.3 +### v5.4 -In [5.3 milestone](https://github.com/abpframework/abp/milestone/65), we will be mostly working on the following topics: +In [5.4 milestone](https://github.com/abpframework/abp/milestone/67), we will be mostly working on the following topics: +* Providing an OpenIddict integration to replace current IdentityServer4 integration. * Maturing and documenting the [eShopOnAbp](https://github.com/abpframework/eShopOnAbp) project, writing a free e-book that explains the solution. * Working on the [LeptonX](https://blog.abp.io/abp/LeptonX-Theme-for-ABP-Framework-Alpha-Release) theme and making it as the default theme for the ABP Framework UI options. -* Working on more examples and guides. +* Improvements on the existing features and providing more guides. -The planned release date for v5.3 is **May, 2022**. +The planned release date for v5.4 is **July, 2022**. + +> After the version 5.4, we will be working for 6.0 which will be released in the end of 2022 based on .NET 7.0. ## Backlog Items @@ -20,8 +23,8 @@ The *Next Versions* section above shows the main focus of the planned versions. Here, a list of major items in the backlog we are considering to work on in the next versions. -* [#7221](https://github.com/abpframework/abp/issues/7221) / Alternative to IdentityServer4 * [#2183](https://github.com/abpframework/abp/issues/2183) / Dapr integration +* [#6655](https://github.com/abpframework/abp/pull/6655) / Use Typescript for the MVC UI * [#236](https://github.com/abpframework/abp/issues/236) / Resource based authorization system * [#2882](https://github.com/abpframework/abp/issues/2882) / Providing a gRPC integration infrastructure (while it is [already possible](https://github.com/abpframework/abp-samples/tree/master/GrpcDemo) to create or consume gRPC endpoints for your application, we plan to create endpoints for the [standard application modules](https://docs.abp.io/en/abp/latest/Modules/Index)) * [#1754](https://github.com/abpframework/abp/issues/1754) / Multi-lingual entities @@ -31,7 +34,6 @@ Here, a list of major items in the backlog we are considering to work on in the * [#4223](https://github.com/abpframework/abp/issues/4223) / WebHook system * [#162](https://github.com/abpframework/abp/issues/162) / Azure ElasticDB Integration for multitenancy * [#2296](https://github.com/abpframework/abp/issues/2296) / Feature toggling infrastructure -* [#6655](https://github.com/abpframework/abp/pull/6655) / Use Typescript for the MVC UI You can always check the milestone planning and the prioritized backlog issues on [the GitHub repository](https://github.com/abpframework/abp/milestones) for a detailed road map. The backlog items are subject to change. We are adding new items and changing priorities based on the community feedbacks and goals of the project. diff --git a/docs/zh-Hans/Module-Development-Basics.md b/docs/zh-Hans/Module-Development-Basics.md index b92ddad7fd..9cdc56ee9f 100644 --- a/docs/zh-Hans/Module-Development-Basics.md +++ b/docs/zh-Hans/Module-Development-Basics.md @@ -49,7 +49,7 @@ public class BlogModule : AbpModule } } ```` -有关配置系统的更多信息,请参阅配置(TODO:link)文档. +有关配置系统的更多信息,请参阅[配置](Configuration.md)文档. ##### 配置服务前和后 diff --git a/docs/zh-Hans/Text-Templating-Razor.md b/docs/zh-Hans/Text-Templating-Razor.md new file mode 100644 index 0000000000..7394133587 --- /dev/null +++ b/docs/zh-Hans/Text-Templating-Razor.md @@ -0,0 +1,570 @@ +# Razor 集成 + +Razor模板是标准的C#类, 所以你可以使用任何C#的功能, 例如`依赖注入`, 使用`LINQ`, 自定义方法甚至使用`仓储` + +## 安装 + +建议使用[ABP CLI](CLI.md)安装此包. + +### 使用ABP CLI + +在项目文件夹(.csproj 文件)中打开命令行窗口并输入以下命令: + +````bash +abp add-package Volo.Abp.TextTemplating.Razor +```` + +### 手动安装 + +如果你想要手动安装: + +1. 添加 [Volo.Abp.TextTemplating.Razor](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Razor) NuGet 包到你的项目: + +```` +Install-Package Volo.Abp.TextTemplating.Razor +```` + +2.添加 `AbpTextTemplatingRazorModule` 到你的模块的依赖列表: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpTextTemplatingRazorModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +```` + +## 添加 MetadataReference到CSharpCompilerOptions + +你需要将添加`MetadataReference`模板中使用的类型添加到 `CSharpCompilerOptions` 的 `References`. + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + Configure(options => + { + options.References.Add(MetadataReference.CreateFromFile(typeof(YourModule).Assembly.Location)); + }); +} +``` + +## 添加MetadataReference到模板 + +你可以添加一些`MetadataReference`到模板 + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + services.Configure(options => + { + //Hello is template name. + options.TemplateReferences.Add("Hello", new List() + { + Assembly.Load("Microsoft.Extensions.Logging.Abstractions"), + Assembly.Load("Microsoft.Extensions.Logging") + } + .Select(x => MetadataReference.CreateFromFile(x.Location)) + .ToList()); + }); +} +``` + +## 定义模板 + +在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类: + +````csharp +public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider +{ + public override void Define(ITemplateDefinitionContext context) + { + context.Add( + new TemplateDefinition("Hello") //template name: "Hello" + .WithRazorEngine() + .WithVirtualFilePath( + "/Demos/Hello/Hello.cshtml", //template content path + isInlineLocalized: true + ) + ); + } +} +```` + +* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板. +* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用). +* `/Demos/Hello/Hello.cshtml` 是模板文件的路径. +* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分. +* `WithRenderEngine` 方法为模板设置渲染引擎. + +### 模板基类 + +每个 `cshtml` 模板页面都需要继承`RazorTemplatePageBase` 或 `RazorTemplatePageBase`. 基类提供了一些使用实用的属性可以在模板中使用. 例如: `Localizer`, `ServiceProvider`. + +### 模板内容 + +`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.cshtml` 文件,并在属性窗口中将其标记为"**嵌入式资源**": + +![hello-template-razor](images/hello-template-razor.png) + +示例 `Hello.cshtml` 内容如下所示: + +```csharp +namespace HelloModelNamespace +{ + public class HelloModel + { + public string Name { get; set; } + } +} +``` + +[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件: + +````csharp +Configure(options => +{ + options.FileSets.AddEmbedded("TextTemplateDemo"); +}); +```` + +* `TextTemplateDemoModule`是模块类. +* `TextTemplateDemo` 是你的项目的根命名空间. + +## 渲染模板 + +`ITemplateRenderer` 服务用于渲染模板内容. + +### 示例: 渲染一个简单的模板 + +````csharp +public class HelloDemo : ITransientDependency +{ + private readonly ITemplateRenderer _templateRenderer; + + public HelloDemo(ITemplateRenderer templateRenderer) + { + _templateRenderer = templateRenderer; + } + + public async Task RunAsync() + { + var result = await _templateRenderer.RenderAsync( + "Hello", //the template name + new HelloModel + { + Name = "John" + } + ); + + Console.WriteLine(result); + } +} +```` + +* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它. +* `RenderAsync` 有两个基本参数: + * `templateName`: 要渲染的模板名称 (本示例中是 `Hello`). + * `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象). + +示例会返回以下结果: + +````csharp +Hello John :) +```` + +## 本地化 + +可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项. + +### 内联本地化 + +内联本地化使用[本地化系统](Localization.md)本地化模板内的文本. + +#### 示例: 重置密码链接 + +假设你需要向用户发送电子邮件重置密码. 模板内容: + +```csharp +namespace ResetMyPasswordModelNamespace +{ + public class ResetMyPasswordModel + { + public string Link { get; set; } + + public string Name { get; set; } + } +} +``` + +```csharp +@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase +@Localizer["ResetMyPassword", Model.Name] +``` + +`Localizer` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键: + +````json +"ResetMyPasswordTitle": "Reset my password", +"ResetMyPassword": "Hi {0}, Click here to reset your password" +```` + +你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源: + +````csharp +context.Add( + new TemplateDefinition( + "PasswordReset", //Template name + typeof(DemoResource) //LOCALIZATION RESOURCE + ) + .WithRazorEngine() + .WithVirtualFilePath( + "/Demos/PasswordReset/PasswordReset.cshtml", //template content path + isInlineLocalized: true + ) +); +```` + +当你这样渲染模板时: + +````csharp +var result = await _templateRenderer.RenderAsync( + "PasswordReset", //the template name + new PasswordResetModel + { + Name = "john", + Link = "https://abp.io/example-link?userId=123&token=ABC" + } +); +```` + +你可以看到以下本地化结果: + +````csharp +Hi john, Click here to reset your password +```` + +> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型. + +### 多个内容本地化 + +你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它. + +#### 示例: 欢迎电子邮件模板 + +假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板. + +首先创建一个文件夹,将模板放在里面,像 `en.cshtml`, `tr.cshtml` 每一个你支持的文化: + +![multiple-file-template-razor](images/multiple-file-template-razor.png) + +然后在模板定义提供程序类中添加模板定义: + +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en" + ) + .WithRazorEngine() + .WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", //template content folder + isInlineLocalized: false + ) +); +```` + +* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化. +* 指定 **模板文件夹** 而不是单个模板文件. +* 设置 `isInlineLocalized` 为 `false`. + +就这些,你可以渲染当前文化的模板: + +````csharp +var result = await _templateRenderer.RenderAsync("WelcomeEmail"); +```` + +> 为了简单我们跳过了模型,但是你可以使用前面所述的模型. + +### 指定文化 + +`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化. + +````csharp +var result = await _templateRenderer.RenderAsync( + "WelcomeEmail", + cultureName: "en" +); +```` + +## 布局模板 + +布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统. + +### 示例: 邮件HTML布局模板 + +例如,你想为所有电子邮件模板创建一个布局. + +首先像之前一样创建一个模板文件: + +```csharp +@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase + + + + + + + @Body + + +``` + +* 布局模板必须具有 **Body** 部分作为渲染的子内容的占位符. + +在模板定义提供程序中注册模板: + +````csharp +context.Add( + new TemplateDefinition( + "EmailLayout", + isLayout: true //SET isLayout! + ) + .WithRazorEngine() + .WithVirtualFilePath( + "/Demos/EmailLayout/EmailLayout.cshtml", + isInlineLocalized: true + ) +); +```` + +现在你可以将此模板用作任何其他模板的布局: + +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en", + layout: "EmailLayout" //Set the LAYOUT + ) + .WithRazorEngine() + .WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", + isInlineLocalized: false + ) +); +```` + +## 全局上下文 + +ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量. + +示例模板内容: + +````csharp +@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase +A global object value: @GlobalContext["myGlobalObject"] +```` + +模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它: + +````csharp +var result = await _templateRenderer.RenderAsync( + "GlobalContextUsage", + globalContext: new Dictionary + { + {"myGlobalObject", "TEST VALUE"} + } +); +```` + +渲染的结果将是: + +```` +A global object value: TEST VALUE +```` + +## 替换存在的模板 + +通过替换应用程序中使用的模块定义的模板. 这样你可以根据自己的需求自定义模板,而无需更改模块代码. + +### 选项-1: 使用虚拟文件系统 + +[虚拟文件系统](Virtual-File-System.md)允许你通过将相同文件放入项目中的相同路径来覆盖任何文件. + +#### 示例: 替换标准电子邮件布局模板 + +ABP框架提供了一个[邮件发送系统](Emailing.md), 它在内部使用文本模板来渲染邮件内容. 它在 `/Volo/Abp/Emailing/Templates/Layout.cshtml` 路径定义了一个标准邮件布局模板. 模板的唯一名称是 `Abp.StandardEmailTemplates.Layout` 并且这个字符中`Volo.Abp.Emailing.Templates.StandardEmailTemplates`静态类上定义为常量. + +执行以下步骤将替换模板替换成你自定义的; + +**1)** 在你的项目中相同的路径添加一个新文件 (`/Volo/Abp/Emailing/Templates/Layout.cshtml`): + +![replace-email-layout-razor](images/replace-email-layout-razor.png) + +**2)** 准备你的邮件布局模板: + +````html +@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase + + + + + + +

This my header

+ + @Body + +
+ This is my footer... +
+ + +```` + +此示例只是向模板添加页眉和页脚并呈现它们之间的内容(请参阅上面的布局模板部分). + + +**3)** 在`.csproj`文件配置嵌入式资源e + +* 添加 [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet 包到你的项目. +* 在 `.csproj` 中添加 `true` 到 `...` 部分. +* 添加以下代码到你的 `.csproj` 文件: + +````xml + + + + +```` + +这将模板文件做为"嵌入式资源". + +**4)** 配置虚拟文件系统 + +在[模块](Module-Development-Basics.md) `ConfigureServices` 方法配置 `AbpVirtualFileSystemOptions` 将嵌入的文件添加到虚拟文件系统中: + +```csharp +Configure(options => +{ + options.FileSets.AddEmbedded(); +}); +``` + +`BookStoreDomainModule` 应该是你的模块名称 + +> 确保你的模块(支持或间接)[依赖](Module-Development-Basics.md) `AbpEmailingModule`. 因为VFS可以基于依赖顺序覆盖文件. + +现在渲染邮件布局模板时会使用你的模板. + +### 选项-2: 使用模板定义提供者 + +你可以创建一个模板定义提供者类来获取邮件布局模板来更改它的虚拟文件路径. + +**示例: 使用 `/MyTemplates/EmailLayout.cshtml` 文件而不是标准模板** + +```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.cshtml", + isInlineLocalized: true + ); + } + } +} +``` + +你依然应该添加 `/MyTemplates/EmailLayout.cshtml` 到虚拟文件系统. 这种方法允许你在任何文件夹中找到模板,而不是在依赖模块定义的文件夹中. + +除了模板内容之外你还可以操作模板定义属性, 例如`DisplayName`, `Layout`或`LocalizationSource`. + +## 高级功能 + +本节介绍文本模板系统的一些内部知识和高级用法. + +### 模板内容Provider + +`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容. + +> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容. + +示例: + +````csharp +public class TemplateContentDemo : ITransientDependency +{ + private readonly ITemplateContentProvider _templateContentProvider; + + public TemplateContentDemo(ITemplateContentProvider templateContentProvider) + { + _templateContentProvider = templateContentProvider; + } + + public async Task RunAsync() + { + var result = await _templateContentProvider + .GetContentOrNullAsync("Hello"); + + Console.WriteLine(result); + } +} +```` + +结果是原始模板内容: + +```` +@inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase +Hello @Model.Name +```` + +* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`. +* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分). + +### 模板内容贡献者 + +`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容. + +你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容. + +示例: + +````csharp +public class MyTemplateContentProvider + : ITemplateContentContributor, ITransientDependency +{ + public async Task GetOrNullAsync(TemplateContentContributorContext context) + { + var templateName = context.TemplateDefinition.Name; + + //TODO: Try to find content from another source + return null; + } +} +```` + +如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者. + +### Template Definition Manager + +`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建). + +## 另请参阅 + +* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). +* [本地化系统](Localization.md). +* [虚拟文件系统](Virtual-File-System.md). \ No newline at end of file diff --git a/docs/zh-Hans/Text-Templating-Scriban.md b/docs/zh-Hans/Text-Templating-Scriban.md new file mode 100644 index 0000000000..97cd0ab631 --- /dev/null +++ b/docs/zh-Hans/Text-Templating-Scriban.md @@ -0,0 +1,527 @@ +# Scriban 集成 + +## 安装 + +建议使用[ABP CLI](CLI.md)安装此包. + +### 使用ABP CLI + +在项目文件夹(.csproj 文件)中打开命令行窗口并输入以下命令: + +````bash +abp add-package Volo.Abp.TextTemplating.Scriban +```` + +### 手动安装 + +如果你想要手动安装: + +1. 添加 [Volo.Abp.TextTemplating.Scriban](https://www.nuget.org/packages/Volo.Abp.TextTemplating.Scriban) NuGet 包到你的项目: + +```` +Install-Package Volo.Abp.TextTemplating.Scriban +```` + +2.添加 `AbpTextTemplatingScribanModule` 到你的模块的依赖列表: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpTextTemplatingScribanModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +```` + +## 定义模板 + +在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类: + +````csharp +public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider +{ + public override void Define(ITemplateDefinitionContext context) + { + context.Add( + new TemplateDefinition("Hello") //template name: "Hello" + .WithVirtualFilePath( + "/Demos/Hello/Hello.tpl", //template content path + isInlineLocalized: true + ) + .WithScribanEngine() + ); + } +} +```` + +* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板. +* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用). +* `/Demos/Hello/Hello.tpl` 是模板文件的路径. +* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分. +* `WithScribanEngine` 方法为模板设置渲染引擎. + +### 模板内容 + +`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.tpl` 文件,并在属性窗口中将其标记为"**嵌入式资源**": + +![hello-template](images/hello-template.png) + +示例 `Hello.tpl` 内容如下所示: + +```` +Hello {%{{{model.name}}}%} :) +```` + +[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件: + +````csharp +Configure(options => +{ + options.FileSets.AddEmbedded("TextTemplateDemo"); +}); +```` + +* `TextTemplateDemoModule`是模块类. +* `TextTemplateDemo` 是你的项目的根命名空间. + +## 渲染模板 + +`ITemplateRenderer` 服务用于渲染模板内容. + +### 示例: 渲染一个简单的模板 + +````csharp +public class HelloDemo : ITransientDependency +{ + private readonly ITemplateRenderer _templateRenderer; + + public HelloDemo(ITemplateRenderer templateRenderer) + { + _templateRenderer = templateRenderer; + } + + public async Task RunAsync() + { + var result = await _templateRenderer.RenderAsync( + "Hello", //the template name + new HelloModel + { + Name = "John" + } + ); + + Console.WriteLine(result); + } +} +```` + +* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它. +* `RenderAsync` 有两个基本参数: + * `templateName`: 要渲染的模板名称 (本示例中是 `Hello`). + * `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象). + +示例会返回以下结果: + +````csharp +Hello John :) +```` + +### 匿名模型 + +虽然建议为模板创建模型类,但在简单情况下使用匿名对象也是可行的: + +````csharp +var result = await _templateRenderer.RenderAsync( + "Hello", + new + { + Name = "John" + } +); +```` + +示例中我们并没有创建模型类,但是创建了一个匿名对象模型. + +### PascalCase 与 snake_case + +PascalCase 属性名(如 `UserName`) 在模板中使用蛇形命名(如 `user_name`). + +## 本地化 + +可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项. + +### 内联本地化 + +内联本地化使用[本地化系统](Localization.md)本地化模板内的文本. + +#### 示例: 重置密码链接 + +假设你需要向用户发送电子邮件重置密码. 模板内容: + +```` +{%{{{L "ResetMyPassword" model.name}}}%} +```` + +`L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键: + +````json +"ResetMyPasswordTitle": "Reset my password", +"ResetMyPassword": "Hi {0}, Click here to reset your password" +```` + +你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源: + +````csharp +context.Add( + new TemplateDefinition( + "PasswordReset", //Template name + typeof(DemoResource) //LOCALIZATION RESOURCE + ) + .WithScribanEngine() + .WithVirtualFilePath( + "/Demos/PasswordReset/PasswordReset.tpl", //template content path + isInlineLocalized: true + ) +); +```` + +当你这样渲染模板时: + +````csharp +var result = await _templateRenderer.RenderAsync( + "PasswordReset", //the template name + new PasswordResetModel + { + Name = "john", + Link = "https://abp.io/example-link?userId=123&token=ABC" + } +); +```` + +你可以看到以下本地化结果: + +````csharp +Hi john, Click here to reset your password +```` + +> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型. + +### 多个内容本地化 + +你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它. + +#### 示例: 欢迎电子邮件模板 + +假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板. + +首先创建一个文件夹,将模板放在里面,像 `en.tpl`, `tr.tpl` 每一个你支持的文化: + +![multiple-file-template](images/multiple-file-template.png) + +然后在模板定义提供程序类中添加模板定义: + +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en" + ) + .WithScribanEngine() + .WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", //template content folder + isInlineLocalized: false + ) +); +```` + +* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化. +* 指定 **模板文件夹** 而不是单个模板文件. +* 设置 `isInlineLocalized` 为 `false`. + +就这些,你可以渲染当前文化的模板: + +````csharp +var result = await _templateRenderer.RenderAsync("WelcomeEmail"); +```` + +> 为了简单我们跳过了模型,但是你可以使用前面所述的模型. + +### 指定文化 + +`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化. + +````csharp +var result = await _templateRenderer.RenderAsync( + "WelcomeEmail", + cultureName: "en" +); +```` + +## 布局模板 + +布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统. + +### 示例: 邮件HTML布局模板 + +例如,你想为所有电子邮件模板创建一个布局. + +首先像之前一样创建一个模板文件: + +````xml + + + + + + + {%{{{content}}}%} + + +```` + +* 布局模板必须具有 **{%{{{content}}}%}** 部分作为渲染的子内容的占位符. + +在模板定义提供程序中注册模板: + +````csharp +context.Add( + new TemplateDefinition( + "EmailLayout", + isLayout: true //SET isLayout! + ) + .WithScribanEngine() + .WithVirtualFilePath( + "/Demos/EmailLayout/EmailLayout.tpl", + isInlineLocalized: true + ) +); +```` + +现在你可以将此模板用作任何其他模板的布局: + +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en", + layout: "EmailLayout" //Set the LAYOUT + ) + .WithScribanEngine() + .WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", + isInlineLocalized: false + ) +); +```` + +## 全局上下文 + +ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量. + +示例模板内容: + +```` +A global object value: {%{{{myGlobalObject}}}%} +```` + +模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它: + +````csharp +var result = await _templateRenderer.RenderAsync( + "GlobalContextUsage", + globalContext: new Dictionary + { + {"myGlobalObject", "TEST VALUE"} + } +); +```` + +渲染的结果将是: + +```` +A global object value: TEST VALUE +```` + +## 替换存在的模板 + +通过替换应用程序中使用的模块定义的模板. 这样你可以根据自己的需求自定义模板,而无需更改模块代码. + +### 选项-1: 使用虚拟文件系统 + +[虚拟文件系统](Virtual-File-System.md)允许你通过将相同文件放入项目中的相同路径来覆盖任何文件. + +#### 示例: 替换标准电子邮件布局模板 + +ABP框架提供了一个[邮件发送系统](Emailing.md), 它在内部使用文本模板来渲染邮件内容. 它在 `/Volo/Abp/Emailing/Templates/Layout.tp` 路径定义了一个标准邮件布局模板. 模板的唯一名称是 `Abp.StandardEmailTemplates.Layout` 并且这个字符中`Volo.Abp.Emailing.Templates.StandardEmailTemplates`静态类上定义为常量. + +执行以下步骤将替换模板替换成你自定义的; + +**1)** 在你的项目中相同的路径添加一个新文件 (`/Volo/Abp/Emailing/Templates/Layout.tpl`): + +![replace-email-layout](images/replace-email-layout.png) + +**2)** 准备你的邮件布局模板: + +````html + + + + + + +

This my header

+ + {%{{{content}}}%} + +
+ This is my footer... +
+ + +```` + +此示例只是向模板添加页眉和页脚并呈现它们之间的内容(请参阅上面的布局模板部分). + + +**3)** 在`.csproj`文件配置嵌入式资源e + +* 添加 [Microsoft.Extensions.FileProviders.Embedded](https://www.nuget.org/packages/Microsoft.Extensions.FileProviders.Embedded) NuGet 包到你的项目. +* 在 `.csproj` 中添加 `true` 到 `...` 部分. +* 添加以下代码到你的 `.csproj` 文件: + +````xml + + + + +```` + +这将模板文件做为"嵌入式资源". + +**4)** 配置虚拟文件系统 + +在[模块](Module-Development-Basics.md) `ConfigureServices` 方法配置 `AbpVirtualFileSystemOptions` 将嵌入的文件添加到虚拟文件系统中: + +```csharp +Configure(options => +{ + options.FileSets.AddEmbedded(); +}); +``` + +`BookStoreDomainModule` 应该是你的模块名称 + +> 确保你的模块(支持或间接)[依赖](Module-Development-Basics.md) `AbpEmailingModule`. 因为VFS可以基于依赖顺序覆盖文件. + +现在渲染邮件布局模板时会使用你的模板. + +### 选项-2: 使用模板定义提供者 + +你可以创建一个模板定义提供者类来获取邮件布局模板来更改它的虚拟文件路径. + +**示例: 使用 `/MyTemplates/EmailLayout.tpl` 文件而不是标准模板** + +```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 + ); + } + } +} +``` + +你依然应该添加 `/MyTemplates/EmailLayout.tpl` 到虚拟文件系统. 这种方法允许你在任何文件夹中找到模板,而不是在依赖模块定义的文件夹中. + +除了模板内容之外你还可以操作模板定义属性, 例如`DisplayName`, `Layout`或`LocalizationSource`. + +## 高级功能 + +本节介绍文本模板系统的一些内部知识和高级用法. + +### 模板内容Provider + +`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容. + +> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容. + +示例: + +````csharp +public class TemplateContentDemo : ITransientDependency +{ + private readonly ITemplateContentProvider _templateContentProvider; + + public TemplateContentDemo(ITemplateContentProvider templateContentProvider) + { + _templateContentProvider = templateContentProvider; + } + + public async Task RunAsync() + { + var result = await _templateContentProvider + .GetContentOrNullAsync("Hello"); + + Console.WriteLine(result); + } +} +```` + +结果是原始模板内容: + +```` +Hello {%{{{model.name}}}%} :) +```` + +* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`. +* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分). + +### 模板内容贡献者 + +`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容. + +你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容. + +示例: + +````csharp +public class MyTemplateContentProvider + : ITemplateContentContributor, ITransientDependency +{ + public async Task GetOrNullAsync(TemplateContentContributorContext context) + { + var templateName = context.TemplateDefinition.Name; + + //TODO: Try to find content from another source + return null; + } +} + +```` + +如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者. + +### Template Definition Manager + +`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建). + +## 另请参阅 + +* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). +* [本地化系统](Localization.md). +* [虚拟文件系统](Virtual-File-System.md). \ No newline at end of file diff --git a/docs/zh-Hans/Text-Templating.md b/docs/zh-Hans/Text-Templating.md index d61c85bc9b..4a21c89c1f 100644 --- a/docs/zh-Hans/Text-Templating.md +++ b/docs/zh-Hans/Text-Templating.md @@ -8,450 +8,30 @@ Template + Model =renderer=> Rendered Content 它非常类似于 ASP.NET Core Razor View (或 Page): -*RAZOR VIEW (or PAGE) + MODEL ==render==> HTML CONTENT* +*RAZOR VIEW (或 PAGE) + MODEL ==render==> HTML CONTENT* 你可以将渲染的输出用于任何目的,例如发送电子邮件或准备一些报告. -### 示例 +模板渲染引擎非常强大: -Here, a simple template: - -```` -Hello {%{{{model.name}}}%} :) -```` - -你可以定义一个含有 `Name` 属性的类来渲染这个模板: - -````csharp -public class HelloModel -{ - public string Name { get; set; } -} -```` - -如果你使用 `Name` 为 `John` 的 `HelloModel` 渲染模板,输出为: - -```` -Hello John :) -```` - -模板渲染引擎非常强大; - -* 它基于 [Scriban](https://github.com/lunet-io/scriban) 库, 所以它支持 **条件逻辑**, **循环** 等. -* 模板内容 **可以本地化**. -* 你可以定义 **布局模板** 在渲染其他模板中用做布局. +* 它支持**条件逻辑**, **循环**等等. +* 模板内容**可以本地化**. +* 你可以为其他渲染模板定义**布局模板**。 * 对于高级场景,你可以传递任何对象到模板上下文. -### 源码 - -这里是本文开发和引用的[示例应用程序源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). - -## 安装 - -推荐使用 [ABP CLI](CLI.md) 安装包. - -### 使用 ABP CLI - -在项目目录(.csproj file)打开命令行窗口运行以下命令: - -````bash -abp add-package Volo.Abp.TextTemplating -```` - -### 手动安装 - -如果你想要手动安装; - -1. 添加 [Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating) NuGet包到你的项目: - -```` -Install-Package Volo.Abp.TextTemplating -```` - -2. 添加 `AbpTextTemplatingModule` 到你的模块依赖列表: - -````csharp -[DependsOn( - //...other dependencies - typeof(AbpTextTemplatingModule) //Add the new module dependency - )] -public class YourModule : AbpModule -{ -} -```` - -## 定义模板 - -在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类: - -````csharp -public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider -{ - public override void Define(ITemplateDefinitionContext context) - { - context.Add( - new TemplateDefinition("Hello") //template name: "Hello" - .WithVirtualFilePath( - "/Demos/Hello/Hello.tpl", //template content path - isInlineLocalized: true - ) - ); - } -} -```` - -* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板. -* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用). -* `/Demos/Hello/Hello.tpl` 是模板文件的路径. -* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分. - -### 模板内容 - -`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.tpl` 文件,并在属性窗口中将其标记为"**嵌入式资源**": - -![hello-template](images/hello-template.png) - -示例 `Hello.tpl` 内容如下所示: - -```` -Hello {%{{{model.name}}}%} :) -```` - -[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件: - -````csharp -Configure(options => -{ - options.FileSets.AddEmbedded("TextTemplateDemo"); -}); -```` - -* `TextTemplateDemoModule`是模块类. -* `TextTemplateDemo` 是你的项目的根命名空间. - -## 渲染模板 - -`ITemplateRenderer` 服务用于渲染模板内容. - -### 示例: 渲染一个简单的模板 - -````csharp -public class HelloDemo : ITransientDependency -{ - private readonly ITemplateRenderer _templateRenderer; - - public HelloDemo(ITemplateRenderer templateRenderer) - { - _templateRenderer = templateRenderer; - } - - public async Task RunAsync() - { - var result = await _templateRenderer.RenderAsync( - "Hello", //the template name - new HelloModel - { - Name = "John" - } - ); - - Console.WriteLine(result); - } -} -```` - -* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它. -* `RenderAsync` 有两个基本参数: - * `templateName`: 要渲染的模板名称 (本示例中是 `Hello`). - * `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象). - -示例会返回以下结果: - -````csharp -Hello John :) -```` - -### 匿名模型 - -虽然建议为模板创建模型类,但在简单情况下使用匿名对象也是可行的: - -````csharp -var result = await _templateRenderer.RenderAsync( - "Hello", - new - { - Name = "John" - } -); -```` - -示例中我们并没有创建模型类,但是创建了一个匿名对象模型. - -### PascalCase 与 snake_case - -PascalCase 属性名(如 `UserName`) 在模板中使用蛇形命名(如 `user_name`). - -## 本地化 - -可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项. - -### 内联本地化 - -内联本地化使用[本地化系统](Localization.md)本地化模板内的文本. - -#### 示例: 重置密码链接 - -假设你需要向用户发送电子邮件重置密码. 模板内容: - -```` -{%{{{L "ResetMyPassword" model.name}}}%} -```` - -`L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键: - -````json -"ResetMyPasswordTitle": "Reset my password", -"ResetMyPassword": "Hi {0}, Click here to reset your password" -```` - -你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源: - -````csharp -context.Add( - new TemplateDefinition( - "PasswordReset", //Template name - typeof(DemoResource) //LOCALIZATION RESOURCE - ).WithVirtualFilePath( - "/Demos/PasswordReset/PasswordReset.tpl", //template content path - isInlineLocalized: true - ) -); -```` - -当你这样渲染模板时: - -````csharp -var result = await _templateRenderer.RenderAsync( - "PasswordReset", //the template name - new PasswordResetModel - { - Name = "john", - Link = "https://abp.io/example-link?userId=123&token=ABC" - } -); -```` - -你可以看到以下本地化结果: - -````csharp -Hi john, Click here to reset your password -```` - -> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型. - -### 多个内容本地化 - -你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它. - -#### 示例: 欢迎电子邮件模板 - -假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板. - -首先创建一个文件夹,将模板放在里面,像 `en.tpl`, `tr.tpl` 每一个你支持的文化: - -![multiple-file-template](images/multiple-file-template.png) - -然后在模板定义提供程序类中添加模板定义: - -````csharp -context.Add( - new TemplateDefinition( - name: "WelcomeEmail", - defaultCultureName: "en" - ) - .WithVirtualFilePath( - "/Demos/WelcomeEmail/Templates", //template content folder - isInlineLocalized: false - ) -); -```` - -* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化. -* 指定 **模板文件夹** 而不是单个模板文件. -* 设置 `isInlineLocalized` 为 `false`. - -就这些,你可以渲染当前文化的模板: - -````csharp -var result = await _templateRenderer.RenderAsync("WelcomeEmail"); -```` - -> 为了简单我们跳过了模型,但是你可以使用前面所述的模型. - -### 指定文化 - -`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化. - -````csharp -var result = await _templateRenderer.RenderAsync( - "WelcomeEmail", - cultureName: "en" -); -```` - -## 布局模板 - -布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统. - -### 示例: 邮件HTML布局模板 - -例如,你想为所有电子邮件模板创建一个布局. - -首先像之前一样创建一个模板文件: - -````xml - - - - - - - {%{{{content}}}%} - - -```` - -* 布局模板必须具有 **{%{{{content}}}%}** 部分作为渲染的子内容的占位符. - -在模板定义提供程序中注册模板: - -````csharp -context.Add( - new TemplateDefinition( - "EmailLayout", - isLayout: true //SET isLayout! - ).WithVirtualFilePath( - "/Demos/EmailLayout/EmailLayout.tpl", - isInlineLocalized: true - ) -); -```` - -现在你可以将此模板用作任何其他模板的布局: - -````csharp -context.Add( - new TemplateDefinition( - name: "WelcomeEmail", - defaultCultureName: "en", - layout: "EmailLayout" //Set the LAYOUT - ).WithVirtualFilePath( - "/Demos/WelcomeEmail/Templates", - isInlineLocalized: false - ) -); -```` - -## 全局上下文 - -ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量. - -示例模板内容: - -```` -A global object value: {%{{{myGlobalObject}}}%} -```` - -模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它: - -````csharp -var result = await _templateRenderer.RenderAsync( - "GlobalContextUsage", - globalContext: new Dictionary - { - {"myGlobalObject", "TEST VALUE"} - } -); -```` - -渲染的结果将是: - -```` -A global object value: TEST VALUE -```` - -## 高级功能 - -本节介绍文本模板系统的一些内部知识和高级用法. - -### 模板内容Provider - -`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容. - -> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容. - -示例: - -````csharp -public class TemplateContentDemo : ITransientDependency -{ - private readonly ITemplateContentProvider _templateContentProvider; - - public TemplateContentDemo(ITemplateContentProvider templateContentProvider) - { - _templateContentProvider = templateContentProvider; - } - - public async Task RunAsync() - { - var result = await _templateContentProvider - .GetContentOrNullAsync("Hello"); - - Console.WriteLine(result); - } -} -```` - -结果是原始模板内容: - -```` -Hello {%{{{model.name}}}%} :) -```` - -* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`. -* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分). - -### 模板内容贡献者 - -`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容. - -你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容. - -示例: - -````csharp -public class MyTemplateContentProvider - : ITemplateContentContributor, ITransientDependency -{ - public async Task GetOrNullAsync(TemplateContentContributorContext context) - { - var templateName = context.TemplateDefinition.Name; - - //TODO: Try to find content from another source - return null; - } -} +ABP框架提供了两个模板引擎: -```` +* **[Razor](Text-Templating-Razor.md)** +* **[Scriban](Text-Templating-Scriban.md)** -如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者. +你可以在同一个应用应用程序中使用不同的模板引擎, 或者创建一个新的自定义模板引擎. -### Template Definition Manager +## 源码 -`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建). +查看开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). ## 另请参阅 * 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). * [本地化系统](Localization.md). -* [虚拟文件系统](Virtual-File-System.md). +* [虚拟文件系统](Virtual-File-System.md). \ No newline at end of file diff --git a/docs/zh-Hans/images/abp-book.png b/docs/zh-Hans/images/abp-book.png new file mode 100644 index 0000000000..7fd6c34c90 Binary files /dev/null and b/docs/zh-Hans/images/abp-book.png differ diff --git a/docs/zh-Hans/images/abp-select2-multiple.png b/docs/zh-Hans/images/abp-select2-multiple.png new file mode 100644 index 0000000000..bdc2d7cc95 Binary files /dev/null and b/docs/zh-Hans/images/abp-select2-multiple.png differ diff --git a/docs/zh-Hans/images/abp-select2-single.png b/docs/zh-Hans/images/abp-select2-single.png new file mode 100644 index 0000000000..219355dd66 Binary files /dev/null and b/docs/zh-Hans/images/abp-select2-single.png differ diff --git a/docs/zh-Hans/images/account-module-forgot-password.png b/docs/zh-Hans/images/account-module-forgot-password.png new file mode 100644 index 0000000000..92fce47a22 Binary files /dev/null and b/docs/zh-Hans/images/account-module-forgot-password.png differ diff --git a/docs/zh-Hans/images/account-module-login.png b/docs/zh-Hans/images/account-module-login.png new file mode 100644 index 0000000000..abde22db57 Binary files /dev/null and b/docs/zh-Hans/images/account-module-login.png differ diff --git a/docs/zh-Hans/images/account-module-manage-account.png b/docs/zh-Hans/images/account-module-manage-account.png new file mode 100644 index 0000000000..92a5f34d72 Binary files /dev/null and b/docs/zh-Hans/images/account-module-manage-account.png differ diff --git a/docs/zh-Hans/images/account-module-register.png b/docs/zh-Hans/images/account-module-register.png new file mode 100644 index 0000000000..f2b9c0587a Binary files /dev/null and b/docs/zh-Hans/images/account-module-register.png differ diff --git a/docs/zh-Hans/images/add-new-propert-to-user-database-extra-properties.png b/docs/zh-Hans/images/add-new-propert-to-user-database-extra-properties.png new file mode 100644 index 0000000000..90c697461e Binary files /dev/null and b/docs/zh-Hans/images/add-new-propert-to-user-database-extra-properties.png differ diff --git a/docs/zh-Hans/images/add-new-propert-to-user-database-field.png b/docs/zh-Hans/images/add-new-propert-to-user-database-field.png new file mode 100644 index 0000000000..334ba0aa35 Binary files /dev/null and b/docs/zh-Hans/images/add-new-propert-to-user-database-field.png differ diff --git a/docs/zh-Hans/images/add-new-property-enum.png b/docs/zh-Hans/images/add-new-property-enum.png new file mode 100644 index 0000000000..34a14b1bfb Binary files /dev/null and b/docs/zh-Hans/images/add-new-property-enum.png differ diff --git a/docs/zh-Hans/images/add-new-property-to-user-form-validation-error-custom.png b/docs/zh-Hans/images/add-new-property-to-user-form-validation-error-custom.png new file mode 100644 index 0000000000..6bb1799e19 Binary files /dev/null and b/docs/zh-Hans/images/add-new-property-to-user-form-validation-error-custom.png differ diff --git a/docs/zh-Hans/images/add-new-property-to-user-form-validation-error.png b/docs/zh-Hans/images/add-new-property-to-user-form-validation-error.png new file mode 100644 index 0000000000..dd2eb8ad1d Binary files /dev/null and b/docs/zh-Hans/images/add-new-property-to-user-form-validation-error.png differ diff --git a/docs/zh-Hans/images/add-new-property-to-user-form.png b/docs/zh-Hans/images/add-new-property-to-user-form.png new file mode 100644 index 0000000000..bc12a6d5ad Binary files /dev/null and b/docs/zh-Hans/images/add-new-property-to-user-form.png differ diff --git a/docs/zh-Hans/images/add-new-property-to-user-table.png b/docs/zh-Hans/images/add-new-property-to-user-table.png new file mode 100644 index 0000000000..a2a15087cc Binary files /dev/null and b/docs/zh-Hans/images/add-new-property-to-user-table.png differ diff --git a/docs/zh-Hans/images/angular-module-dev-app-project.png b/docs/zh-Hans/images/angular-module-dev-app-project.png new file mode 100644 index 0000000000..08e0e9fd6c Binary files /dev/null and b/docs/zh-Hans/images/angular-module-dev-app-project.png differ diff --git a/docs/zh-Hans/images/angular-module-folder-structure.png b/docs/zh-Hans/images/angular-module-folder-structure.png new file mode 100644 index 0000000000..ae2333b295 Binary files /dev/null and b/docs/zh-Hans/images/angular-module-folder-structure.png differ diff --git a/docs/zh-Hans/images/blazor-generic-exception-message.png b/docs/zh-Hans/images/blazor-generic-exception-message.png new file mode 100644 index 0000000000..5e128003cf Binary files /dev/null and b/docs/zh-Hans/images/blazor-generic-exception-message.png differ diff --git a/docs/zh-Hans/images/blazor-user-friendly-exception.png b/docs/zh-Hans/images/blazor-user-friendly-exception.png new file mode 100644 index 0000000000..fa2a70d931 Binary files /dev/null and b/docs/zh-Hans/images/blazor-user-friendly-exception.png differ diff --git a/docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png b/docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png index 307e3516a5..f8ef7f2ba0 100644 Binary files a/docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png and b/docs/zh-Hans/images/bookstore-visual-studio-solution-v3.png differ diff --git a/docs/zh-Hans/images/cmskit-module-blog-post-edit.png b/docs/zh-Hans/images/cmskit-module-blog-post-edit.png new file mode 100644 index 0000000000..528bc6b499 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-blog-post-edit.png differ diff --git a/docs/zh-Hans/images/cmskit-module-blog-posts-page.png b/docs/zh-Hans/images/cmskit-module-blog-posts-page.png new file mode 100644 index 0000000000..64d5d0e934 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-blog-posts-page.png differ diff --git a/docs/zh-Hans/images/cmskit-module-blogs-edit.png b/docs/zh-Hans/images/cmskit-module-blogs-edit.png new file mode 100644 index 0000000000..b2da4e5b5a Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-blogs-edit.png differ diff --git a/docs/zh-Hans/images/cmskit-module-blogs-feature-action.png b/docs/zh-Hans/images/cmskit-module-blogs-feature-action.png new file mode 100644 index 0000000000..c0f0b1d3f4 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-blogs-feature-action.png differ diff --git a/docs/zh-Hans/images/cmskit-module-blogs-page.png b/docs/zh-Hans/images/cmskit-module-blogs-page.png new file mode 100644 index 0000000000..6c150429b7 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-blogs-page.png differ diff --git a/docs/zh-Hans/images/cmskit-module-comment-page.png b/docs/zh-Hans/images/cmskit-module-comment-page.png new file mode 100644 index 0000000000..6647a6dd09 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-comment-page.png differ diff --git a/docs/zh-Hans/images/cmskit-module-comments-detail.png b/docs/zh-Hans/images/cmskit-module-comments-detail.png new file mode 100644 index 0000000000..686a70e1f8 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-comments-detail.png differ diff --git a/docs/zh-Hans/images/cmskit-module-features-dialog-2.png b/docs/zh-Hans/images/cmskit-module-features-dialog-2.png new file mode 100644 index 0000000000..b6dd409e0a Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-features-dialog-2.png differ diff --git a/docs/zh-Hans/images/cmskit-module-features-dialog.png b/docs/zh-Hans/images/cmskit-module-features-dialog.png new file mode 100644 index 0000000000..23aa2aa17a Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-features-dialog.png differ diff --git a/docs/zh-Hans/images/cmskit-module-features-scroll-index.png b/docs/zh-Hans/images/cmskit-module-features-scroll-index.png new file mode 100644 index 0000000000..0bc3dedcc5 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-features-scroll-index.png differ diff --git a/docs/zh-Hans/images/cmskit-module-global-resources-page.png b/docs/zh-Hans/images/cmskit-module-global-resources-page.png new file mode 100644 index 0000000000..9090c97412 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-global-resources-page.png differ diff --git a/docs/zh-Hans/images/cmskit-module-menus-page.png b/docs/zh-Hans/images/cmskit-module-menus-page.png new file mode 100644 index 0000000000..c265763788 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-menus-page.png differ diff --git a/docs/zh-Hans/images/cmskit-module-menus-public.png b/docs/zh-Hans/images/cmskit-module-menus-public.png new file mode 100644 index 0000000000..0b67064334 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-menus-public.png differ diff --git a/docs/zh-Hans/images/cmskit-module-pages-edit.png b/docs/zh-Hans/images/cmskit-module-pages-edit.png new file mode 100644 index 0000000000..cca074fdac Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-pages-edit.png differ diff --git a/docs/zh-Hans/images/cmskit-module-pages-page.png b/docs/zh-Hans/images/cmskit-module-pages-page.png new file mode 100644 index 0000000000..b64915493c Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-pages-page.png differ diff --git a/docs/zh-Hans/images/cmskit-module-ratings.png b/docs/zh-Hans/images/cmskit-module-ratings.png new file mode 100644 index 0000000000..f97c934f15 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-ratings.png differ diff --git a/docs/zh-Hans/images/cmskit-module-reactions.png b/docs/zh-Hans/images/cmskit-module-reactions.png new file mode 100644 index 0000000000..b915b85f91 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-reactions.png differ diff --git a/docs/zh-Hans/images/cmskit-module-tag-edit.png b/docs/zh-Hans/images/cmskit-module-tag-edit.png new file mode 100644 index 0000000000..eb1ebb3a52 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-tag-edit.png differ diff --git a/docs/zh-Hans/images/cmskit-module-tags-page.png b/docs/zh-Hans/images/cmskit-module-tags-page.png new file mode 100644 index 0000000000..f41b6ecbd0 Binary files /dev/null and b/docs/zh-Hans/images/cmskit-module-tags-page.png differ diff --git a/docs/zh-Hans/images/data-table-colum-extension-blazor-component-render-solution.png b/docs/zh-Hans/images/data-table-colum-extension-blazor-component-render-solution.png new file mode 100644 index 0000000000..77612b5d34 Binary files /dev/null and b/docs/zh-Hans/images/data-table-colum-extension-blazor-component-render-solution.png differ diff --git a/docs/zh-Hans/images/data-table-colum-extension-blazor-component-render.png b/docs/zh-Hans/images/data-table-colum-extension-blazor-component-render.png new file mode 100644 index 0000000000..9d018e2ab9 Binary files /dev/null and b/docs/zh-Hans/images/data-table-colum-extension-blazor-component-render.png differ diff --git a/docs/zh-Hans/images/docs-create-project-v4.4.0.png b/docs/zh-Hans/images/docs-create-project-v4.4.0.png new file mode 100644 index 0000000000..fa30fcbc3a Binary files /dev/null and b/docs/zh-Hans/images/docs-create-project-v4.4.0.png differ diff --git a/docs/zh-Hans/images/docs-module_solution-explorer.png b/docs/zh-Hans/images/docs-module_solution-explorer.png index 2988ec4134..42b2c31143 100644 Binary files a/docs/zh-Hans/images/docs-module_solution-explorer.png and b/docs/zh-Hans/images/docs-module_solution-explorer.png differ diff --git a/docs/zh-Hans/images/extension-navigation-property-form.png b/docs/zh-Hans/images/extension-navigation-property-form.png new file mode 100644 index 0000000000..5e55ce3afd Binary files /dev/null and b/docs/zh-Hans/images/extension-navigation-property-form.png differ diff --git a/docs/zh-Hans/images/extension-navigation-property-table.png b/docs/zh-Hans/images/extension-navigation-property-table.png new file mode 100644 index 0000000000..a3f333919c Binary files /dev/null and b/docs/zh-Hans/images/extension-navigation-property-table.png differ diff --git a/docs/zh-Hans/images/features-modal.png b/docs/zh-Hans/images/features-modal.png index 0fce7ecf5c..74ee8aabc3 100644 Binary files a/docs/zh-Hans/images/features-modal.png and b/docs/zh-Hans/images/features-modal.png differ diff --git a/docs/zh-Hans/images/features-module-opening.png b/docs/zh-Hans/images/features-module-opening.png index a3899924ce..bd99604cd7 100644 Binary files a/docs/zh-Hans/images/features-module-opening.png and b/docs/zh-Hans/images/features-module-opening.png differ diff --git a/docs/zh-Hans/images/generated-static-client-proxies.png b/docs/zh-Hans/images/generated-static-client-proxies.png new file mode 100644 index 0000000000..4c1678aebd Binary files /dev/null and b/docs/zh-Hans/images/generated-static-client-proxies.png differ diff --git a/docs/zh-Hans/images/hello-template-razor.png b/docs/zh-Hans/images/hello-template-razor.png new file mode 100644 index 0000000000..1e0e39fcec Binary files /dev/null and b/docs/zh-Hans/images/hello-template-razor.png differ diff --git a/docs/zh-Hans/images/identity-module-menu.png b/docs/zh-Hans/images/identity-module-menu.png index ce2ee7e3e9..8083e2f722 100644 Binary files a/docs/zh-Hans/images/identity-module-menu.png and b/docs/zh-Hans/images/identity-module-menu.png differ diff --git a/docs/zh-Hans/images/identity-module-permissions.png b/docs/zh-Hans/images/identity-module-permissions.png index dfe0a9a022..24ef15e3b4 100644 Binary files a/docs/zh-Hans/images/identity-module-permissions.png and b/docs/zh-Hans/images/identity-module-permissions.png differ diff --git a/docs/zh-Hans/images/identity-module-roles.png b/docs/zh-Hans/images/identity-module-roles.png index 53df1cdc6d..c8e2c9cde1 100644 Binary files a/docs/zh-Hans/images/identity-module-roles.png and b/docs/zh-Hans/images/identity-module-roles.png differ diff --git a/docs/zh-Hans/images/identity-module-users.png b/docs/zh-Hans/images/identity-module-users.png index 33edb4cb78..7de6da77d4 100644 Binary files a/docs/zh-Hans/images/identity-module-users.png and b/docs/zh-Hans/images/identity-module-users.png differ diff --git a/docs/zh-Hans/images/implementing-domain-driven-design-book.png b/docs/zh-Hans/images/implementing-domain-driven-design-book.png new file mode 100644 index 0000000000..6568b46f66 Binary files /dev/null and b/docs/zh-Hans/images/implementing-domain-driven-design-book.png differ diff --git a/docs/zh-Hans/images/module-tenant-management-actions.png b/docs/zh-Hans/images/module-tenant-management-actions.png index 899e731f8c..d1f01c2e93 100644 Binary files a/docs/zh-Hans/images/module-tenant-management-actions.png and b/docs/zh-Hans/images/module-tenant-management-actions.png differ diff --git a/docs/zh-Hans/images/module-tenant-management-new-tenant.png b/docs/zh-Hans/images/module-tenant-management-new-tenant.png index 7f0f50b42a..ab9ee6ed56 100644 Binary files a/docs/zh-Hans/images/module-tenant-management-new-tenant.png and b/docs/zh-Hans/images/module-tenant-management-new-tenant.png differ diff --git a/docs/zh-Hans/images/module-tenant-management-page.png b/docs/zh-Hans/images/module-tenant-management-page.png index 5ed6cc63bd..563c64aac8 100644 Binary files a/docs/zh-Hans/images/module-tenant-management-page.png and b/docs/zh-Hans/images/module-tenant-management-page.png differ diff --git a/docs/zh-Hans/images/multiple-file-template-razor.png b/docs/zh-Hans/images/multiple-file-template-razor.png new file mode 100644 index 0000000000..85f87e84f4 Binary files /dev/null and b/docs/zh-Hans/images/multiple-file-template-razor.png differ diff --git a/docs/zh-Hans/images/optimistic-concurrency.png b/docs/zh-Hans/images/optimistic-concurrency.png new file mode 100644 index 0000000000..85253594c8 Binary files /dev/null and b/docs/zh-Hans/images/optimistic-concurrency.png differ diff --git a/docs/zh-Hans/images/page-header-toolbar-blazor.png b/docs/zh-Hans/images/page-header-toolbar-blazor.png new file mode 100644 index 0000000000..a4d0454893 Binary files /dev/null and b/docs/zh-Hans/images/page-header-toolbar-blazor.png differ diff --git a/docs/zh-Hans/images/page-toolbar-button-blazor.png b/docs/zh-Hans/images/page-toolbar-button-blazor.png new file mode 100644 index 0000000000..fb907e797a Binary files /dev/null and b/docs/zh-Hans/images/page-toolbar-button-blazor.png differ diff --git a/docs/zh-Hans/images/page-toolbar-button.png b/docs/zh-Hans/images/page-toolbar-button.png new file mode 100644 index 0000000000..05e3f9da11 Binary files /dev/null and b/docs/zh-Hans/images/page-toolbar-button.png differ diff --git a/docs/zh-Hans/images/page-toolbar-custom-component-blazor.png b/docs/zh-Hans/images/page-toolbar-custom-component-blazor.png new file mode 100644 index 0000000000..fb0856fa03 Binary files /dev/null and b/docs/zh-Hans/images/page-toolbar-custom-component-blazor.png differ diff --git a/docs/zh-Hans/images/page-toolbar-custom-component.png b/docs/zh-Hans/images/page-toolbar-custom-component.png new file mode 100644 index 0000000000..a15ef4854c Binary files /dev/null and b/docs/zh-Hans/images/page-toolbar-custom-component.png differ diff --git a/docs/zh-Hans/images/permissions-module-dialog.png b/docs/zh-Hans/images/permissions-module-dialog.png index 5af4cbef43..6b190484a2 100644 Binary files a/docs/zh-Hans/images/permissions-module-dialog.png and b/docs/zh-Hans/images/permissions-module-dialog.png differ diff --git a/docs/zh-Hans/images/permissions-module-open-dialog.png b/docs/zh-Hans/images/permissions-module-open-dialog.png index 06821d8c53..46631fe126 100644 Binary files a/docs/zh-Hans/images/permissions-module-open-dialog.png and b/docs/zh-Hans/images/permissions-module-open-dialog.png differ diff --git a/docs/zh-Hans/images/replace-email-layout-razor.png b/docs/zh-Hans/images/replace-email-layout-razor.png new file mode 100644 index 0000000000..84ff6c1080 Binary files /dev/null and b/docs/zh-Hans/images/replace-email-layout-razor.png differ diff --git a/docs/zh-Hans/images/static-js-proxy-example.png b/docs/zh-Hans/images/static-js-proxy-example.png new file mode 100644 index 0000000000..575957b9ae Binary files /dev/null and b/docs/zh-Hans/images/static-js-proxy-example.png differ diff --git a/docs/zh-Hans/images/table-column-extension-example-blazor.png b/docs/zh-Hans/images/table-column-extension-example-blazor.png new file mode 100644 index 0000000000..3079fa278c Binary files /dev/null and b/docs/zh-Hans/images/table-column-extension-example-blazor.png differ diff --git a/docs/zh-Hans/images/table-column-extension-example.png b/docs/zh-Hans/images/table-column-extension-example.png new file mode 100644 index 0000000000..84d87bf46c Binary files /dev/null and b/docs/zh-Hans/images/table-column-extension-example.png differ diff --git a/docs/zh-Hans/images/upgrade-diff-empty-folders.png b/docs/zh-Hans/images/upgrade-diff-empty-folders.png new file mode 100644 index 0000000000..9f0256061a Binary files /dev/null and b/docs/zh-Hans/images/upgrade-diff-empty-folders.png differ diff --git a/docs/zh-Hans/images/user-action-blazor-extension-click-me.png b/docs/zh-Hans/images/user-action-blazor-extension-click-me.png new file mode 100644 index 0000000000..fb1ea80182 Binary files /dev/null and b/docs/zh-Hans/images/user-action-blazor-extension-click-me.png differ diff --git a/docs/zh-Hans/images/user-action-extension-click-me.png b/docs/zh-Hans/images/user-action-extension-click-me.png new file mode 100644 index 0000000000..4046630f48 Binary files /dev/null and b/docs/zh-Hans/images/user-action-extension-click-me.png differ diff --git a/docs/zh-Hans/images/user-action-extension-on-blazor-project.png b/docs/zh-Hans/images/user-action-extension-on-blazor-project.png new file mode 100644 index 0000000000..d11ea328b6 Binary files /dev/null and b/docs/zh-Hans/images/user-action-extension-on-blazor-project.png differ diff --git a/docs/zh-Hans/images/user-action-extension-on-solution.png b/docs/zh-Hans/images/user-action-extension-on-solution.png new file mode 100644 index 0000000000..5f58d3f302 Binary files /dev/null and b/docs/zh-Hans/images/user-action-extension-on-solution.png differ diff --git a/docs/zh-Hans/images/winmerge-comparison-result.png b/docs/zh-Hans/images/winmerge-comparison-result.png new file mode 100644 index 0000000000..dbce7cb52e Binary files /dev/null and b/docs/zh-Hans/images/winmerge-comparison-result.png differ diff --git a/docs/zh-Hans/images/winmerge-file-diff.png b/docs/zh-Hans/images/winmerge-file-diff.png new file mode 100644 index 0000000000..645d3de696 Binary files /dev/null and b/docs/zh-Hans/images/winmerge-file-diff.png differ diff --git a/docs/zh-Hans/images/winmerge-open-folders.png b/docs/zh-Hans/images/winmerge-open-folders.png new file mode 100644 index 0000000000..53327601d4 Binary files /dev/null and b/docs/zh-Hans/images/winmerge-open-folders.png differ diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs index 7e30a9a6bf..8c8a6cdbd0 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ServiceProxying/Angular/AngularServiceProxyGenerator.cs @@ -48,7 +48,7 @@ public class AngularServiceProxyGenerator : ServiceProxyGeneratorBase thisValues = GetAtomicValues().GetEnumerator(); - IEnumerator otherValues = other.GetAtomicValues().GetEnumerator(); + var thisValues = GetAtomicValues().GetEnumerator(); + var otherValues = other.GetAtomicValues().GetEnumerator(); - while (thisValues.MoveNext() && otherValues.MoveNext()) + var thisMoveNext = thisValues.MoveNext(); + var otherMoveNext = otherValues.MoveNext(); + while (thisMoveNext && otherMoveNext) { - if (ReferenceEquals(thisValues.Current, null) ^ - ReferenceEquals(otherValues.Current, null)) + if (ReferenceEquals(thisValues.Current, null) ^ ReferenceEquals(otherValues.Current, null)) { return false; } - if (thisValues.Current != null && - !thisValues.Current.Equals(otherValues.Current)) + if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current)) + { + return false; + } + + thisMoveNext = thisValues.MoveNext(); + otherMoveNext = otherValues.MoveNext(); + + if (thisMoveNext != otherMoveNext) { return false; } diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs index 87449956cd..bd3852098b 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/Modeling/PropertyApiDescriptionModel.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using System.Linq; using System.Reflection; using Volo.Abp.Http.ProxyScripting.Configuration; @@ -18,16 +19,31 @@ public class PropertyApiDescriptionModel public bool IsRequired { get; set; } - //TODO: Validation rules for this property + public int? MinLength { get; set; } + + public int? MaxLength { get; set; } + + public string Minimum { get; set; } + + public string Maximum { get; set; } + + public string Regex { get; set; } + public static PropertyApiDescriptionModel Create(PropertyInfo propertyInfo) { + var customAttributes = propertyInfo.GetCustomAttributes(true); return new PropertyApiDescriptionModel { Name = propertyInfo.Name, JsonName = AbpApiProxyScriptingConfiguration.PropertyNameGenerator.Invoke(propertyInfo), Type = ApiTypeNameHelper.GetTypeName(propertyInfo.PropertyType), TypeSimple = ApiTypeNameHelper.GetSimpleTypeName(propertyInfo.PropertyType), - IsRequired = propertyInfo.IsDefined(typeof(RequiredAttribute), true) + IsRequired = customAttributes.OfType().Any(), + Minimum = customAttributes.OfType().Select(x => x.Minimum).FirstOrDefault()?.ToString(), + Maximum = customAttributes.OfType().Select(x => x.Maximum).FirstOrDefault()?.ToString(), + MinLength = customAttributes.OfType().FirstOrDefault()?.Length ?? customAttributes.OfType().FirstOrDefault()?.MinimumLength, + MaxLength = customAttributes.OfType().FirstOrDefault()?.Length ?? customAttributes.OfType().FirstOrDefault()?.MaximumLength, + Regex= customAttributes.OfType().Select(x => x.Pattern).FirstOrDefault() }; } } diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs index 8992ef062a..ff1db3a772 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs @@ -45,7 +45,7 @@ public class AppUrlProvider : IAppUrlProvider, ITransientDependency public bool IsRedirectAllowedUrl(string url) { - var allow = Options.RedirectAllowedUrls.Any(url.StartsWith); + var allow = Options.RedirectAllowedUrls.Any(x => url.StartsWith(x, StringComparison.CurrentCultureIgnoreCase)); if (!allow) { Logger.LogError($"Invalid RedirectUrl: {url}, Use {nameof(AppUrlProvider)} to configure it!"); diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/Address.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/Address.cs index 7ec96848a4..cee2c8c379 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/Address.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/Address.cs @@ -11,6 +11,8 @@ public class Address : ValueObject public int Number { get; } + public string[] Tags { get; } + private Address() { } @@ -18,11 +20,13 @@ public class Address : ValueObject public Address( Guid cityId, string street, - int number) + int number, + params string[] tags) { CityId = cityId; Street = street; Number = number; + Tags = tags; } //Requires to implement this method to return properties. @@ -31,5 +35,9 @@ public class Address : ValueObject yield return Street; yield return CityId; yield return Number; + foreach (var tag in Tags) + { + yield return tag; + } } } diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs index a4a0f9c44f..144d4350d3 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Values/ValueObject_Tests.cs @@ -10,8 +10,9 @@ public class ValueObject_Tests public void ValueObjects_With_Same_Properties_Should_Be_Equals() { var cityId = Guid.NewGuid(); - var address1 = new Address(cityId, "Baris Manco", 42); - var address2 = new Address(cityId, "Baris Manco", 42); + + var address1 = new Address(cityId, "Baris Manco", 42, "home", "office"); + var address2 = new Address(cityId, "Baris Manco", 42, "home", "office"); address1.ValueEquals(address2).ShouldBeTrue(); } @@ -25,5 +26,10 @@ public class ValueObject_Tests var address2 = new Address(cityId, "Baris Manco", 43); address1.ValueEquals(address2).ShouldBeFalse(); + + address1 = new Address(cityId, "Baris Manco", 42, "home", "office"); + address2 = new Address(cityId, "Baris Manco", 42, "home"); + + address1.ValueEquals(address2).ShouldBeFalse(); } } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs index 48729aba46..746992822c 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs @@ -1,5 +1,7 @@ -using System.ComponentModel.DataAnnotations; +using System; +using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http.Extensions; using Volo.Abp.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -32,6 +34,13 @@ public class ManageModel : AccountPageModel await contributor.ConfigureAsync(ProfileManagementPageCreationContext); } + if (!Url.IsLocalUrl(ReturnUrl) && + !ReturnUrl.StartsWith(UriHelper.BuildAbsolute(Request.Scheme, Request.Host, Request.PathBase).RemovePostFix("/")) && + !AppUrlProvider.IsRedirectAllowedUrl(ReturnUrl)) + { + ReturnUrl = null; + } + return Page(); } diff --git a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml index 1b53e0e427..1d2feb2a8d 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml +++ b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Pages/Index.cshtml @@ -1,14 +1,13 @@ @page @using Microsoft.AspNetCore.Authentication +@using System.Net.Http.Headers +@using System.Text.Json @model IndexModel @{ ViewData["Title"] = "Home page"; }
-

Welcome

-

Learn about building Web apps with ASP.NET Core.

- Login Loout @@ -25,5 +24,22 @@
@await HttpContext.GetTokenAsync("access_token")

+ + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44303/api/claims"); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await HttpContext.GetTokenAsync("access_token")); + + var response = await client.SendAsync(request); + response.EnsureSuccessStatusCode(); + + + @{ + var apiResponse = JsonSerializer.Serialize(JsonDocument.Parse(await response.Content.ReadAsStringAsync()), new JsonSerializerOptions + { + WriteIndented = true + }); + } + @apiResponse; + }
diff --git a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Program.cs b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Program.cs index f5b17d5a12..6e18cde936 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Program.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Client.Mvc/Program.cs @@ -35,6 +35,8 @@ builder.Services.AddAuthentication(options => options.Scope.Add("email"); options.Scope.Add("roles"); + options.Scope.Add("phone"); + options.Scope.Add("AbpAPI"); }); var app = builder.Build(); diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj b/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj index b55295d2ec..0f09844cfa 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj +++ b/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddict.Demo.Server.csproj @@ -67,7 +67,6 @@ - runtime; build; native; contentfiles; analyzers diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddictServerModule.cs b/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddictServerModule.cs index 903779b70f..3f0b36ed66 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddictServerModule.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/OpenIddictServerModule.cs @@ -1,9 +1,7 @@ using System.Text; -using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using OpenIddict.Demo.Server.EntityFrameworkCore; -using OpenIddict.Validation.AspNetCore; using Volo.Abp; using Volo.Abp.Account; using Volo.Abp.Account.Web; @@ -109,16 +107,7 @@ public class OpenIddictServerModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { - // This is work for the OpenIddictServerBuilder.AddValidation() - context.Services.ConfigureApplicationCookie(options => - { - options.ForwardDefaultSelector = ctx => ctx.Request.Path.StartsWithSegments("/api") - ? OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme - : null; - }); - - - Configure(options => + Configure(options => { options.AddDevelopmentEncryptionAndSigningCertificate = false; }); diff --git a/modules/openiddict/app/OpenIddict.Demo.Server/Program.cs b/modules/openiddict/app/OpenIddict.Demo.Server/Program.cs index a03fbe6908..eb5b1673b2 100644 --- a/modules/openiddict/app/OpenIddict.Demo.Server/Program.cs +++ b/modules/openiddict/app/OpenIddict.Demo.Server/Program.cs @@ -1,9 +1,6 @@ -using System.Text; -using Microsoft.IdentityModel.Tokens; using OpenIddict.Demo.Server; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.Localization; -using Volo.Abp.OpenIddict.Jwt; var builder = WebApplication.CreateBuilder(args); builder.Services.AddCors(options => @@ -44,6 +41,13 @@ builder.Services.Configure(options => // options.TokenValidationParameters.TokenDecryptionKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Abp_OpenIddict_Demo_87E33FC57D80")); // }); +// builder.Services.ConfigureApplicationCookie(options => +// { +// options.ForwardDefaultSelector = ctx => ctx.Request.Path.StartsWithSegments("/api") +// ? OtherScheme +// : null; +// }); + await builder.AddApplicationAsync(); var app = builder.Build(); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj index 1b4b47d530..7bfcc4dc52 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo.Abp.OpenIddict.AspNetCore.csproj @@ -14,7 +14,6 @@ - diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs index 250b52d959..9a2a289d08 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictAspNetCoreModule.cs @@ -1,36 +1,26 @@ using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Server; using Volo.Abp.AspNetCore.MultiTenancy; -using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.Modularity; +using Volo.Abp.OpenIddict.WildcardDomains; +using Volo.Abp.Security.Claims; namespace Volo.Abp.OpenIddict; [DependsOn( - typeof(AbpAspNetCoreMvcModule), typeof(AbpAspNetCoreMvcUiThemeSharedModule), typeof(AbpAspNetCoreMultiTenancyModule), typeof(AbpOpenIddictDomainModule) )] public class AbpOpenIddictAspNetCoreModule : AbpModule { - public override void PreConfigureServices(ServiceConfigurationContext context) - { - PreConfigure(builder => - { - builder.UseAspNetCore() - .EnableAuthorizationEndpointPassthrough() - .EnableTokenEndpointPassthrough() - .EnableUserinfoEndpointPassthrough() - .EnableLogoutEndpointPassthrough() - .EnableVerificationEndpointPassthrough() - .EnableStatusCodePagesIntegration(); - }); - } - public override void ConfigureServices(ServiceConfigurationContext context) { + AddOpenIddictServer(context.Services); + Configure(options => { options.ClaimDestinationsProvider.Add(); @@ -41,4 +31,104 @@ public class AbpOpenIddictAspNetCoreModule : AbpModule options.ViewLocationFormats.Add("/Volo/Abp/OpenIddict/Views/{1}/{0}.cshtml"); }); } + + private void AddOpenIddictServer(IServiceCollection services) + { + var builderOptions = services.ExecutePreConfiguredActions(); + + if (builderOptions.UpdateAbpClaimTypes) + { + AbpClaimTypes.UserId = OpenIddictConstants.Claims.Subject; + AbpClaimTypes.Role = OpenIddictConstants.Claims.Role; + AbpClaimTypes.UserName = OpenIddictConstants.Claims.Name; + AbpClaimTypes.Name = OpenIddictConstants.Claims.GivenName; + AbpClaimTypes.SurName = OpenIddictConstants.Claims.FamilyName; + AbpClaimTypes.PhoneNumber = OpenIddictConstants.Claims.PhoneNumber; + AbpClaimTypes.PhoneNumberVerified = OpenIddictConstants.Claims.PhoneNumberVerified; + AbpClaimTypes.Email = OpenIddictConstants.Claims.Email; + AbpClaimTypes.EmailVerified = OpenIddictConstants.Claims.EmailVerified; + } + + var openIddictBuilder = services.AddOpenIddict() + .AddServer(builder => + { + builder + .SetAuthorizationEndpointUris("/connect/authorize") + // /.well-known/oauth-authorization-server + // /.well-known/openid-configuration + //.SetConfigurationEndpointUris() + // /.well-known/jwks + //.SetCryptographyEndpointUris() + .SetDeviceEndpointUris("/connect/device") + .SetIntrospectionEndpointUris("/connect/introspect") + .SetLogoutEndpointUris("/connect/logout") + .SetRevocationEndpointUris("/connect/revocat") + .SetTokenEndpointUris("/connect/token") + .SetUserinfoEndpointUris("/connect/userinfo") + .SetVerificationEndpointUris("/connect/verify"); + + builder + .AllowAuthorizationCodeFlow() + .AllowHybridFlow() + .AllowImplicitFlow() + .AllowPasswordFlow() + .AllowClientCredentialsFlow() + .AllowRefreshTokenFlow() + .AllowDeviceCodeFlow() + .AllowNoneFlow(); + + builder.RegisterScopes(new[] + { + OpenIddictConstants.Scopes.OpenId, + OpenIddictConstants.Scopes.Email, + OpenIddictConstants.Scopes.Profile, + OpenIddictConstants.Scopes.Phone, + OpenIddictConstants.Scopes.Roles, + OpenIddictConstants.Scopes.Address, + OpenIddictConstants.Scopes.OfflineAccess + }); + + builder.UseAspNetCore() + .EnableAuthorizationEndpointPassthrough() + .EnableTokenEndpointPassthrough() + .EnableUserinfoEndpointPassthrough() + .EnableLogoutEndpointPassthrough() + .EnableVerificationEndpointPassthrough() + .EnableStatusCodePagesIntegration(); + + if (builderOptions.AddDevelopmentEncryptionAndSigningCertificate) + { + builder + .AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + } + + var wildcardDomainsOptions = services.ExecutePreConfiguredActions(); + if (wildcardDomainsOptions.EnableWildcardDomainSupport) + { + var preActions = services.GetPreConfigureActions(); + + Configure(options => + { + preActions.Configure(options); + }); + + builder.RemoveEventHandler(OpenIddictServerHandlers.Authentication.ValidateClientRedirectUri.Descriptor); + builder.AddEventHandler(AbpValidateClientRedirectUri.Descriptor); + + builder.RemoveEventHandler(OpenIddictServerHandlers.Authentication.ValidateRedirectUriParameter.Descriptor); + builder.AddEventHandler(AbpValidateRedirectUriParameter.Descriptor); + + builder.RemoveEventHandler(OpenIddictServerHandlers.Session.ValidateClientPostLogoutRedirectUri.Descriptor); + builder.AddEventHandler(AbpValidateClientPostLogoutRedirectUri.Descriptor); + + builder.RemoveEventHandler(OpenIddictServerHandlers.Session.ValidatePostLogoutRedirectUriParameter.Descriptor); + builder.AddEventHandler(AbpValidatePostLogoutRedirectUriParameter.Descriptor); + } + + services.ExecutePreConfiguredActions(builder); + }); + + services.ExecutePreConfiguredActions(openIddictBuilder); + } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs similarity index 92% rename from modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs rename to modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs index 55121ba566..475c13e447 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/AbpOpenIddictOptions.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.OpenIddict; -public class AbpOpenIddictOptions +public class AbpOpenIddictAspNetCoreOptions { /// /// Updates to be compatible with OpenIddict claims. diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs similarity index 100% rename from modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs rename to modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainBase.cs diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainOptions.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainOptions.cs similarity index 100% rename from modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainOptions.cs rename to modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpOpenIddictWildcardDomainOptions.cs diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs similarity index 100% rename from modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs rename to modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientPostLogoutRedirectUri.cs diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs similarity index 100% rename from modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs rename to modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateClientRedirectUri.cs diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs similarity index 100% rename from modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs rename to modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidatePostLogoutRedirectUriParameter.cs diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs similarity index 100% rename from modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs rename to modules/openiddict/src/Volo.Abp.OpenIddict.AspNetCore/Volo/Abp/OpenIddict/WildcardDomains/AbpValidateRedirectUriParameter.cs diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj index acdea8b0fb..25b4fb8571 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo.Abp.OpenIddict.Domain.Shared.csproj @@ -11,12 +11,11 @@ - - + diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/AbpOpenIddictDomainSharedModule.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/AbpOpenIddictDomainSharedModule.cs index 23d6705318..c1b1f01023 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/AbpOpenIddictDomainSharedModule.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain.Shared/Volo/Abp/OpenIddict/AbpOpenIddictDomainSharedModule.cs @@ -1,5 +1,4 @@ -using Localization.Resources.AbpUi; -using Volo.Abp.Modularity; +using Volo.Abp.Modularity; using Volo.Abp.Localization; using Volo.Abp.OpenIddict.Localization; using Volo.Abp.Localization.ExceptionHandling; @@ -25,7 +24,7 @@ public class AbpOpenIddictDomainSharedModule : AbpModule { options.Resources .Add("en") - .AddBaseTypes(typeof(AbpValidationResource), typeof(AbpUiResource)) + .AddBaseTypes(typeof(AbpValidationResource)) .AddVirtualJson("Volo/Abp/OpenIddict/Localization/OpenIddict"); }); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj index 6165ca6576..c5e73ae294 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo.Abp.OpenIddict.Domain.csproj @@ -11,14 +11,13 @@ + - - diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs index 9b84731432..16c3ab49c5 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/AbpOpenIddictDomainModule.cs @@ -1,12 +1,9 @@ -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -using OpenIddict.Abstractions; -using OpenIddict.Server; using Volo.Abp.BackgroundWorkers; using Volo.Abp.Caching; +using Volo.Abp.DistributedLocking; using Volo.Abp.Domain; using Volo.Abp.Guids; using Volo.Abp.Identity; @@ -17,8 +14,6 @@ using Volo.Abp.OpenIddict.Applications; using Volo.Abp.OpenIddict.Authorizations; using Volo.Abp.OpenIddict.Scopes; using Volo.Abp.OpenIddict.Tokens; -using Volo.Abp.OpenIddict.WildcardDomains; -using Volo.Abp.Security.Claims; using Volo.Abp.Threading; namespace Volo.Abp.OpenIddict; @@ -27,16 +22,17 @@ namespace Volo.Abp.OpenIddict; typeof(AbpDddDomainModule), typeof(AbpIdentityDomainModule), typeof(AbpOpenIddictDomainSharedModule), + typeof(AbpDistributedLockingAbstractionsModule), typeof(AbpCachingModule), typeof(AbpGuidsModule) )] public class AbpOpenIddictDomainModule : AbpModule { - private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + private readonly static OneTimeRunner OneTimeRunner = new OneTimeRunner(); public override void ConfigureServices(ServiceConfigurationContext context) { - AddOpenIddict(context.Services); + AddOpenIddictCore(context.Services); } public override void OnApplicationInitialization(ApplicationInitializationContext context) @@ -55,23 +51,8 @@ public class AbpOpenIddictDomainModule : AbpModule } } - private void AddOpenIddict(IServiceCollection services) + private void AddOpenIddictCore(IServiceCollection services) { - var builderOptions = services.ExecutePreConfiguredActions(); - - if (builderOptions.UpdateAbpClaimTypes) - { - AbpClaimTypes.UserId = OpenIddictConstants.Claims.Subject; - AbpClaimTypes.Role = OpenIddictConstants.Claims.Role; - AbpClaimTypes.UserName = OpenIddictConstants.Claims.Name; - AbpClaimTypes.Name = OpenIddictConstants.Claims.GivenName; - AbpClaimTypes.SurName = OpenIddictConstants.Claims.FamilyName; - AbpClaimTypes.PhoneNumber = OpenIddictConstants.Claims.PhoneNumber; - AbpClaimTypes.PhoneNumberVerified = OpenIddictConstants.Claims.PhoneNumberVerified; - AbpClaimTypes.Email = OpenIddictConstants.Claims.Email; - AbpClaimTypes.EmailVerified = OpenIddictConstants.Claims.EmailVerified; - } - var openIddictBuilder = services.AddOpenIddict() .AddCore(builder => { @@ -87,76 +68,6 @@ public class AbpOpenIddictDomainModule : AbpModule .AddScopeStore() .AddTokenStore(); - services.ExecutePreConfiguredActions(builder); - }) - .AddServer(builder => - { - builder - .SetAuthorizationEndpointUris("/connect/authorize") - // /.well-known/oauth-authorization-server - // /.well-known/openid-configuration - //.SetConfigurationEndpointUris() - // /.well-known/jwks - //.SetCryptographyEndpointUris() - .SetDeviceEndpointUris("/connect/device") - .SetIntrospectionEndpointUris("/connect/introspect") - .SetLogoutEndpointUris("/connect/logout") - .SetRevocationEndpointUris("/connect/revocat") - .SetTokenEndpointUris("/connect/token") - .SetUserinfoEndpointUris("/connect/userinfo") - .SetVerificationEndpointUris("/connect/verify"); - - builder - .AllowAuthorizationCodeFlow() - .AllowHybridFlow() - .AllowImplicitFlow() - .AllowPasswordFlow() - .AllowClientCredentialsFlow() - .AllowRefreshTokenFlow() - .AllowDeviceCodeFlow() - .AllowNoneFlow(); - - builder.RegisterScopes(new[] - { - OpenIddictConstants.Scopes.OpenId, - OpenIddictConstants.Scopes.Email, - OpenIddictConstants.Scopes.Profile, - OpenIddictConstants.Scopes.Phone, - OpenIddictConstants.Scopes.Roles, - OpenIddictConstants.Scopes.Address, - OpenIddictConstants.Scopes.OfflineAccess - }); - - if (builderOptions.AddDevelopmentEncryptionAndSigningCertificate) - { - builder - .AddDevelopmentEncryptionCertificate() - .AddDevelopmentSigningCertificate(); - } - - var wildcardDomainsOptions = services.ExecutePreConfiguredActions(); - if (wildcardDomainsOptions.EnableWildcardDomainSupport) - { - var preActions = services.GetPreConfigureActions(); - - Configure(options => - { - preActions.Configure(options); - }); - - builder.RemoveEventHandler(OpenIddictServerHandlers.Authentication.ValidateClientRedirectUri.Descriptor); - builder.AddEventHandler(AbpValidateClientRedirectUri.Descriptor); - - builder.RemoveEventHandler(OpenIddictServerHandlers.Authentication.ValidateRedirectUriParameter.Descriptor); - builder.AddEventHandler(AbpValidateRedirectUriParameter.Descriptor); - - builder.RemoveEventHandler(OpenIddictServerHandlers.Session.ValidateClientPostLogoutRedirectUri.Descriptor); - builder.AddEventHandler(AbpValidateClientPostLogoutRedirectUri.Descriptor); - - builder.RemoveEventHandler(OpenIddictServerHandlers.Session.ValidatePostLogoutRedirectUriParameter.Descriptor); - builder.AddEventHandler(AbpValidatePostLogoutRedirectUriParameter.Descriptor); - } - services.ExecutePreConfiguredActions(builder); }); diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/TokenCleanupBackgroundWorker.cs b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/TokenCleanupBackgroundWorker.cs index c89ac256d0..d34b2c5ae2 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/TokenCleanupBackgroundWorker.cs +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.Domain/Volo/Abp/OpenIddict/Tokens/TokenCleanupBackgroundWorker.cs @@ -1,27 +1,46 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Volo.Abp.BackgroundWorkers; +using Volo.Abp.DistributedLocking; using Volo.Abp.Threading; namespace Volo.Abp.OpenIddict.Tokens; public class TokenCleanupBackgroundWorker : AsyncPeriodicBackgroundWorkerBase { + protected IAbpDistributedLock DistributedLock { get; } + public TokenCleanupBackgroundWorker( AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory, - IOptionsMonitor cleanupOptions) + IOptionsMonitor cleanupOptions, + IAbpDistributedLock distributedLock) : base(timer, serviceScopeFactory) { + DistributedLock = distributedLock; timer.Period = cleanupOptions.CurrentValue.CleanupPeriod; } protected async override Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) { - await workerContext - .ServiceProvider - .GetRequiredService() - .CleanAsync(); + await using (var handle = await DistributedLock.TryAcquireAsync(nameof(TokenCleanupBackgroundWorker))) + { + Logger.LogInformation($"Lock is acquired for {nameof(TokenCleanupBackgroundWorker)}"); + + if (handle != null) + { + await workerContext + .ServiceProvider + .GetRequiredService() + .CleanAsync(); + + Logger.LogInformation($"Lock is released for {nameof(TokenCleanupBackgroundWorker)}"); + return; + } + + Logger.LogInformation($"Handle is null because of the locking for : {nameof(TokenCleanupBackgroundWorker)}"); + } } } diff --git a/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo.Abp.OpenIddict.EntityFrameworkCore.csproj b/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo.Abp.OpenIddict.EntityFrameworkCore.csproj index 1e864c0222..3a4b4147bc 100644 --- a/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo.Abp.OpenIddict.EntityFrameworkCore.csproj +++ b/modules/openiddict/src/Volo.Abp.OpenIddict.EntityFrameworkCore/Volo.Abp.OpenIddict.EntityFrameworkCore.csproj @@ -11,7 +11,6 @@ - diff --git a/modules/openiddict/test/Volo.Abp.OpenIddict.Domain.Tests/Volo.Abp.OpenIddict.Domain.Tests.csproj b/modules/openiddict/test/Volo.Abp.OpenIddict.Domain.Tests/Volo.Abp.OpenIddict.Domain.Tests.csproj index b2b7ba1b89..ba5c9024bf 100644 --- a/modules/openiddict/test/Volo.Abp.OpenIddict.Domain.Tests/Volo.Abp.OpenIddict.Domain.Tests.csproj +++ b/modules/openiddict/test/Volo.Abp.OpenIddict.Domain.Tests/Volo.Abp.OpenIddict.Domain.Tests.csproj @@ -12,10 +12,4 @@ - - - - - - diff --git a/modules/openiddict/test/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests.csproj b/modules/openiddict/test/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests.csproj index 6538342dca..f3b8602433 100644 --- a/modules/openiddict/test/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests.csproj +++ b/modules/openiddict/test/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests/Volo.Abp.OpenIddict.EntityFrameworkCore.Tests.csproj @@ -9,7 +9,6 @@ -