From 919a2e2e01892f70722a4eb6ba8fd19016e33da1 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Fri, 8 May 2020 23:18:08 +0300 Subject: [PATCH 01/51] Text-Templating initial content. --- docs/en/Text-Templating.md | 156 ++++++++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 2 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index efde511323..49b59b347e 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -1,3 +1,155 @@ -# Text-Templating +# Text Templating -TODO \ No newline at end of file +In ABP Framework, `text template` is a mixture of text blocks and control logic that can generate a `string` result. [Scriban](https://github.com/lunet-io/scriban) is used for the control logic and [Abp.Localization](Localization.md) is used to make content easily localizable. The generated string can be text of any kind, such as a web page, an e-mail content etc. + +> **stored content** +```html +
    +{{~ for $i in 0..3 ~}} +
  1. {{ L "WelcomeMessage" }}
  2. +{{~ endfor ~}} +
+``` +> **result** (for en culture) +``` +1. Welcome to the abp.io! +2. Welcome to the abp.io! +3. Welcome to the abp.io! +4. Welcome to the abp.io! +``` + +## Installation + +It is suggested to use the [ABP CLI](CLI.md) to install this package. + +### Using the ABP CLI + +Open a command line window in the folder of the project (.csproj file) and type the following command: + +````bash +abp add-package Volo.Abp.TextTemplating +```` + +### Manual Installation + +If you want to manually install; + +1. Add the [Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating) NuGet package to your project: + + ```` + Install-Package Volo.Abp.TextTemplating + ```` + +2. Add the `AbpTextTemplatingModule` to the dependency list of your module: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpTextTemplatingModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +```` +## Logic + +A Text Template is a combination of two object. +- `TemplateDefinition` +- `TemplateContent` + +### Template Definition + +Template Definition is an object that contains some information about your `Text Templates`. Template Definition object contains the following properties. + +- `Name` *(string)* +- `IsLayout` *(boolean)* +- `Layout` *(string)* contains the name of layout template +- `LocalizationResource` *(Type)* Inline Localized +- `IsInlineLocalized`*(boolean)* describes that the template is inline localized or not +- `DefaultCultureName` *(string)* defines the default culture for the template + +### Template Content + +This is a simple content for your templates. For default, template contents stored as `Virtual File`. + +> Example: ForgotPasswordEmail.tpl + +```html +

{{L "PasswordReset"}}

+ +

{{L "PasswordResetInfoInEmail"}}

+ +
+ {{L "ResetMyPassword"}} +
+ +``` + +## Localization + +You can localize your Text Templates by choosing two different method. + +- `Inline Localization` +- `Multiple Content Localization` + +### Inline Localization + +Inline localized Text Templates is using only one content resource, and it is using the `Abp.Localization` to get content in different languages/cultures. + +> Example Inline Localized Text Template: +> +> ForgotPasswordEmail.tpl + +```html +

{{L "PasswordReset"}}

+ +

{{L "PasswordResetInfoInEmail"}}

+ +
+ {{L "ResetMyPassword"}} +
+``` + +### Multiple Content Localization + +You can store your Text Templates for any culture in different content resource. + +**Examples given by using `Virtual File` contents.** + +> Example Multiple Content Localization + +> ForgotPasswordEmail / en.tpl + +```html +

Reset Your Password

+ +

Hello, this is a password changing email.

+ +
+ Click To Reset Your Password +
+``` + +> ForgotPasswordEmail / tr.tpl + +```html +

Şifrenizi Değiştirin

+ +

Merhaba, bu bir şifre yenileme e postasıdır.

+ +
+ Şifrenizi Yenilemek İçin Tıklayınız +
+``` + +## Definition a Text Template + +## Rendering + +## Getting Template Definitions + +### Template Definition Manager + +## Getting Template Contents + +### Template Content Contributor From 0ce59cd1dc8a551b2e085af5d8bbdc7ddbc55fbc Mon Sep 17 00:00:00 2001 From: Ahmet Date: Fri, 8 May 2020 23:29:26 +0300 Subject: [PATCH 02/51] Update Text-Templating.md Layout System Described --- docs/en/Text-Templating.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 49b59b347e..531a365f69 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -1,6 +1,6 @@ # Text Templating -In ABP Framework, `text template` is a mixture of text blocks and control logic that can generate a `string` result. [Scriban](https://github.com/lunet-io/scriban) is used for the control logic and [Abp.Localization](Localization.md) is used to make content easily localizable. The generated string can be text of any kind, such as a web page, an e-mail content etc. +In ABP Framework, `text template` is a mixture of text blocks and control logic that can generate a `string` result. An open source package [Scriban](https://github.com/lunet-io/scriban) is used for the control logic and [Abp.Localization](Localization.md) is used to make content easily localizable. The generated string can be text of any kind, such as a web page, an e-mail content etc. > **stored content** ```html @@ -36,9 +36,9 @@ If you want to manually install; 1. Add the [Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating) NuGet package to your project: - ```` - Install-Package Volo.Abp.TextTemplating - ```` +```` +Install-Package Volo.Abp.TextTemplating +```` 2. Add the `AbpTextTemplatingModule` to the dependency list of your module: @@ -142,9 +142,29 @@ You can store your Text Templates for any culture in different content resource. ``` -## Definition a Text Template +## Layout System -## Rendering +It is typical to use the same layout for all emails. So, you can define a layout template. + +A text template can be layout for different text templates and also a text template may use a layout. + +A layout Text Template must have `{{content}}` area to render the child content. _(just like the `RenderBody()` in the MVC)_ + +> Example Email Layout Text Template + +```html + + + + + + + {{content}} + + +``` + +## Definition a Text Template ## Getting Template Definitions @@ -153,3 +173,5 @@ You can store your Text Templates for any culture in different content resource. ## Getting Template Contents ### Template Content Contributor + +## Rendering From 386923c4a603b6fcf74b373ab4fc042077474f71 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Fri, 8 May 2020 23:31:40 +0300 Subject: [PATCH 03/51] Update Text-Templating.md Layout system moved to under Logic section --- docs/en/Text-Templating.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 531a365f69..e63053d815 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -85,14 +85,14 @@ This is a simple content for your templates. For default, template contents stor ``` -## Localization +### Localization You can localize your Text Templates by choosing two different method. - `Inline Localization` - `Multiple Content Localization` -### Inline Localization +#### Inline Localization Inline localized Text Templates is using only one content resource, and it is using the `Abp.Localization` to get content in different languages/cultures. @@ -110,7 +110,7 @@ Inline localized Text Templates is using only one content resource, and it is us ``` -### Multiple Content Localization +#### Multiple Content Localization You can store your Text Templates for any culture in different content resource. @@ -142,7 +142,7 @@ You can store your Text Templates for any culture in different content resource. ``` -## Layout System +### Layout System It is typical to use the same layout for all emails. So, you can define a layout template. From 455ed693cfca77e43ee83b76922db2f08be073d4 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Fri, 8 May 2020 23:39:03 +0300 Subject: [PATCH 04/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index e63053d815..c6f684ff8f 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -114,8 +114,6 @@ Inline localized Text Templates is using only one content resource, and it is us You can store your Text Templates for any culture in different content resource. -**Examples given by using `Virtual File` contents.** - > Example Multiple Content Localization > ForgotPasswordEmail / en.tpl From ef0ddbd66552772c184a5deeeda81042d396dabc Mon Sep 17 00:00:00 2001 From: Ahmet Date: Sat, 9 May 2020 00:38:09 +0300 Subject: [PATCH 05/51] Update Text-Templating.md adding the definition is started --- docs/en/Text-Templating.md | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index c6f684ff8f..6b8357ac7d 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -51,6 +51,7 @@ public class YourModule : AbpModule { } ```` + ## Logic A Text Template is a combination of two object. @@ -164,6 +165,47 @@ A layout Text Template must have `{{content}}` area to render the child content. ## Definition a Text Template +First of all, create a class that inherited from `TemplateDefinitionProvider` abstract class and create `Define` method that derived from the base class. + +`Define` method requires a context that is `ITemplateDefinitionContext`. This `context` is a storage for template definitions and we will add our template definitions to the context. + +> For default, ABP uses **`Virtual Files`** for text templates. All given examples are for `Virtual File Text Template Definitions`. + +```csharp +public class MyTemplateDefinitionProvider : TemplateDefinitionProvider + { + public override void Define(ITemplateDefinitionContext context) + { + // Layout Text Template + context.Add( + new TemplateDefinition( + name: "MySampleTemplateLayout", // Template Definition Name + isLayout: true + ).WithVirtualFilePath("/SampleTemplates/SampleTemplateLayout.tpl", true) + ); + + // Inline Localized Text Template + context.Add( + new TemplateDefinition( + name: "ForgotPasswordEmail", + localizationResource: typeof(MyLocalizationResource), + layout: TestTemplates.TestTemplateLayout1 + ).WithVirtualFilePath("/SampleTemplates/ForgotPasswordEmail.tpl", true) + ); + + // Multiple File Localized Text Template + context.Add( + new TemplateDefinition( + name: "ForgotPasswordEmail", + defaultCultureName: "en" + ).WithVirtualFilePath("/SampleTemplates/ForgotPasswordEmail", false) + ); + } + } +``` + +### Context + ## Getting Template Definitions ### Template Definition Manager From 43ea247fd0a8a43ea2e1729c8b1941320d931b88 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Sat, 9 May 2020 00:58:26 +0300 Subject: [PATCH 06/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 6b8357ac7d..d94dc6931c 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -143,7 +143,7 @@ You can store your Text Templates for any culture in different content resource. ### Layout System -It is typical to use the same layout for all emails. So, you can define a layout template. +It is typical to use the same layout for some different Text Templates. So, you can define a layout template. A text template can be layout for different text templates and also a text template may use a layout. @@ -204,7 +204,9 @@ public class MyTemplateDefinitionProvider : TemplateDefinitionProvider } ``` -### Context +As you see in the given example all Text Templates are added with `(ITemplateDefinitionContext)context.Add` method. This method requires a `TemplateDefinition` object. Then we call `WithVirtualFilePath` method with chaining for the describe where is the virtual files. + + ## Getting Template Definitions From e85498d597e809f3af1549fc95e17aa14f6094f9 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 12 May 2020 14:01:56 +0300 Subject: [PATCH 07/51] refactor(ci): add force-publish to lerna version command --- npm/ng-packs/scripts/publish.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/npm/ng-packs/scripts/publish.ts b/npm/ng-packs/scripts/publish.ts index 5570602813..9228a752ca 100644 --- a/npm/ng-packs/scripts/publish.ts +++ b/npm/ng-packs/scripts/publish.ts @@ -28,7 +28,15 @@ const publish = async () => { await execa( 'yarn', - ['lerna', 'version', program.nextVersion, '--yes', '--no-commit-hooks', '--skip-git'], + [ + 'lerna', + 'version', + program.nextVersion, + '--yes', + '--no-commit-hooks', + '--skip-git', + '--force-publish', + ], { stdout: 'inherit', cwd: '../' }, ); From 48ac4d2bb7010af56323746cee02dcd59d5156e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 12 May 2020 15:03:14 +0300 Subject: [PATCH 08/51] #3915: Check if in lock --- .../LocalizedTemplateContentReaderFactory.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs index b30e5771f4..092815ff6e 100644 --- a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -42,12 +41,18 @@ namespace Volo.Abp.TextTemplating.VirtualFiles } finally { - _lock.ExitWriteLock(); + if (_lock.IsWriteLockHeld) + { + _lock.ExitWriteLock(); + } } } finally { - _lock.ExitUpgradeableReadLock(); + if (_lock.IsUpgradeableReadLockHeld) + { + _lock.ExitUpgradeableReadLock(); + } } } From 7246b227bed0fcd93e083e4a0ce4bb5a937c7053 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 12 May 2020 16:34:10 +0300 Subject: [PATCH 09/51] feat(core): add email property to current user type --- .../packages/core/src/lib/models/application-configuration.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts b/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts index 0a4377f4d2..b52afe9faf 100644 --- a/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts +++ b/npm/ng-packs/packages/core/src/lib/models/application-configuration.ts @@ -66,5 +66,6 @@ export namespace ApplicationConfiguration { id: string; tenantId: string; userName: string; + email: string; } } From 11a73493cee432ba7abab620d9969f4e76af5645 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Tue, 12 May 2020 16:58:32 +0300 Subject: [PATCH 10/51] test: fix testing erros --- .../packages/core/src/lib/tests/config-state.service.spec.ts | 1 + npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts index d019bda84b..17de9efb9e 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config-state.service.spec.ts @@ -122,6 +122,7 @@ const CONFIG_STATE_DATA = { id: null, tenantId: null, userName: null, + email: null, }, features: { values: {}, diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts index 3df3abc63e..aa3740218c 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts @@ -146,6 +146,7 @@ export const CONFIG_STATE_DATA = { id: null, tenantId: null, userName: null, + email: null, }, features: { values: {}, From 340e163b498ec14b137e73dd28eacd95a7bd1861 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 13 May 2020 00:15:25 +0800 Subject: [PATCH 11/51] Update document. --- ...Application-Modules-Overriding-Services.md | 19 +- .../Getting-Started-AspNetCore-Application.md | 15 +- ...ure-Active-Directory-Authentication-MVC.md | 24 +- docs/zh-Hans/Samples/Index.md | 56 +++ .../Tutorials/AspNetCore-Mvc/Part-II.md | 434 +----------------- docs/zh-Hans/docs-nav.json | 4 + 6 files changed, 104 insertions(+), 448 deletions(-) create mode 100644 docs/zh-Hans/Samples/Index.md diff --git a/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md index caa448a14a..94a2120059 100644 --- a/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md +++ b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md @@ -107,22 +107,25 @@ public class MyIdentityUserAppService : IdentityUserAppService public class MyIdentityUserManager : IdentityUserManager { public MyIdentityUserManager( - IdentityUserStore store, + IdentityUserStore store, + IIdentityRoleRepository roleRepository, + IIdentityUserRepository userRepository, IOptions optionsAccessor, IPasswordHasher passwordHasher, IEnumerable> userValidators, IEnumerable> passwordValidators, - ILookupNormalizer keyNormalizer, - IdentityErrorDescriber errors, - IServiceProvider services, + ILookupNormalizer keyNormalizer, + IdentityErrorDescriber errors, + IServiceProvider services, ILogger logger, - ICancellationTokenProvider cancellationTokenProvider - ) : base( - store, + ICancellationTokenProvider cancellationTokenProvider) : + base(store, + roleRepository, + userRepository, optionsAccessor, passwordHasher, userValidators, - passwordValidators, + passwordValidators, keyNormalizer, errors, services, diff --git a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md index 9d195562fd..0334609bd3 100644 --- a/docs/zh-Hans/Getting-Started-AspNetCore-Application.md +++ b/docs/zh-Hans/Getting-Started-AspNetCore-Application.md @@ -30,10 +30,8 @@ ABP是一个模块化框架,它需要一个**启动 (根) 模块**继承自``Abp ````C# using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Volo.Abp; -using Volo.Abp.AspNetCore.Modularity; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Modularity; @@ -42,7 +40,8 @@ namespace BasicAspNetCoreApplication [DependsOn(typeof(AbpAspNetCoreMvcModule))] public class AppModule : AbpModule { - public override void OnApplicationInitialization(ApplicationInitializationContext context) + public override void OnApplicationInitialization( + ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); var env = context.GetEnvironment(); @@ -51,8 +50,14 @@ namespace BasicAspNetCoreApplication { app.UseDeveloperExceptionPage(); } + else + { + app.UseExceptionHandler("/Error"); + } - app.UseMvcWithDefaultRoute(); + app.UseStaticFiles(); + app.UseRouting(); + app.UseConfiguredEndpoints(); } } } diff --git a/docs/zh-Hans/How-To/Azure-Active-Directory-Authentication-MVC.md b/docs/zh-Hans/How-To/Azure-Active-Directory-Authentication-MVC.md index 8b282bbe56..34aab9fa46 100644 --- a/docs/zh-Hans/How-To/Azure-Active-Directory-Authentication-MVC.md +++ b/docs/zh-Hans/How-To/Azure-Active-Directory-Authentication-MVC.md @@ -157,6 +157,16 @@ private void ConfigureAuthentication(ServiceConfigurationContext context, IConfi JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Add("sub", ClaimTypes.NameIdentifier); ```` +* Help! 我一直得到 ***System.ArgumentNullException: Value cannot be null. (Parameter 'userName')*** 错误! + + * 当你使用 Azure Authority **v2.0 端点** 而不请求 `email` 域, 会发生这些情况. [Abp 创建用户检查了唯一的邮箱](https://github.com/abpframework/abp/blob/037ef9abe024c03c1f89ab6c933710bcfe3f5c93/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs#L208). 只需添加 + + ````csharp + options.Scope.Add("email"); + ```` + + 到你的 openid 配置. + * Help! 我一直得到 ***AADSTS50011: The reply URL specified in the request does not match the reply URLs configured for the application*** 错误! * 如果你在appsettings设置 **CallbackPath** 为: @@ -170,15 +180,17 @@ private void ConfigureAuthentication(ServiceConfigurationContext context, IConfi 你在azure门户的应用程序**重定向URI**必须具有之类 `https://localhost:44320/signin-azuread-oidc` 的, 而不仅是 `/signin-azuread-oidc`. -* Help! 我一直得到 ***System.ArgumentNullException: Value cannot be null. (Parameter 'userName')*** 错误! +* Help! 我一直得到 ***AADSTS700051: The response_type 'token' is not enabled for the application.*** 错误! - * 当你使用 Azure Authority **v2.0 端点** 而不请求 `email` 域, 会发生这些情况. [Abp 创建用户检查了唯一的邮箱](https://github.com/abpframework/abp/blob/037ef9abe024c03c1f89ab6c933710bcfe3f5c93/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs#L208). 只需添加 + * 当你请求**token**(访问令牌)和**id_token**时没有在Azure门户应用程序启用访问令牌时会发生这个错误,只需勾选ID令牌顶部的**访问令牌**复选框即可同时请求令牌. - ````csharp - options.Scope.Add("email"); - ```` +* Help! 我一直得到 ***AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret*** 错误! - 到你的 openid 配置. + * 当你与 **id_token** 一起请求 **code**时,你需要在Azure门户的**证书和机密**菜单下添加**证书和机密**. 然后你需要添加openid配置选项: + + ````csharp + options.ClientSecret = "Value of your secret on azure portal"; + ```` * 如何**调试/监视**在映射之前获得的声明? diff --git a/docs/zh-Hans/Samples/Index.md b/docs/zh-Hans/Samples/Index.md new file mode 100644 index 0000000000..774d6467ef --- /dev/null +++ b/docs/zh-Hans/Samples/Index.md @@ -0,0 +1,56 @@ +# 示例应用 + +这些是ABP框架创建的官方示例. 这些示例大部分在[abpframework/abp-samples](https://github.com/abpframework/abp-samples) GitHub 仓库. + +### 微服务示例 + +演示如何基于微服务体系结构构建系统的完整解决方案. + +* [示例的文档](Microservice-Demo.md) +* [源码](https://github.com/abpframework/abp/tree/dev/samples/MicroserviceDemo) +* [微服务架构文档](../Microservice-Architecture.md) + +### Book Store + +一个简单的CRUD应用程序,展示了使用ABP框架开发应用程序的基本原理. 使用不同的技术实现了相同的示例: + +* **Book Store: Razor Pages UI & Entity Framework Core** + + * [教程](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=MVC) + * [源码](https://github.com/abpframework/abp-samples/tree/master/BookStore) + +* **Book Store: Angular UI & MongoDB** + + * [教程](https://docs.abp.io/en/abp/latest/Tutorials/Part-1?UI=NG) + * [源码](https://github.com/abpframework/abp-samples/tree/master/BookStore-Angular-MongoDb) + +* **Book Store: Modular application (Razor Pages UI & EF Core)** + + * [源码](https://github.com/abpframework/abp-samples/tree/master/BookStore-Modular) + +如果没有Razor Pages & MongoDB 结合,但你可以检查两个文档来理解它,因为DB和UI不会互相影响. + +### 其他示例 + +* **Entity Framework 迁移**: 演示如何将应用程序拆分为多个数据库的解决方案. 每个数据库包含不同的模块. + * [源码](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo) + * [EF Core数据库迁移文档](../Entity-Framework-Core-Migrations.md) +* **Dashboard Demo**: 一个简单的应用程序,展示了如何在ASP.NET Core MVC UI中使用widget系统. + * [源码](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo) + * [Widget 文档](../UI/AspNetCore/Widgets.md) +* **RabbitMQ 事件总线 Demo**: 由两个通过RabbitMQ集成的分布式事件相互通信的应用程序组成的解决方案. + * [源码](https://github.com/abpframework/abp-samples/tree/master/RabbitMqEventBus) + * [分布式事件总线文档](../Distributed-Event-Bus.md) + * [RabbitMQ 分布式事件总线集成文档](../Distributed-Event-Bus-RabbitMQ-Integration.md) +* **自定义认证**: 如何为ASP.NET Core MVC / Razor Pages应用程序自定义身份验证的解决方案. + * [源码](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization) + * 相关 "[How To](../How-To/Index.md)" 文档: + * [Azure Active Directory 认证](../How-To/Azure-Active-Directory-Authentication-MVC.md) + * [自定义登录页面](../How-To/Customize-Login-Page-MVC.md) + * [自定义 SignIn Manager](../How-To/Customize-SignIn-Manager.md) +* **空的ASP.NET Core应用程序**: 从基本的ASP.NET Core应用程序使用ABP框架. + * [源码](https://github.com/abpframework/abp-samples/tree/master/BasicAspNetCoreApplication) + * [文档](../Getting-Started-AspNetCore-Application.md) +* **空的控制台应用程序**: 从基本的控制台应用程序安装ABP框架. + * [源码](https://github.com/abpframework/abp-samples/tree/master/BasicConsoleApplication) + * [文档](../Getting-Started-Console-Application.md) \ No newline at end of file diff --git a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md index 2d4b344afa..e981259db8 100644 --- a/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md +++ b/docs/zh-Hans/Tutorials/AspNetCore-Mvc/Part-II.md @@ -1,432 +1,8 @@ -## ASP.NET Core MVC 教程 - 第二章 +# 教程 -### 关于本教程 +## 应用程序开发 -这是ASP.NET Core MVC教程系列的第二章. 查看其它章节 +* [ASP.NET Core MVC / Razor Pages UI](../Part-1?UI=MVC) +* [Angular UI](../Part-1?UI=NG) -* [Part I: 创建项目和书籍列表页面](Part-I.md) -* **Part II: 创建,编辑,删除书籍(本章)** -* [Part III: 集成测试](Part-III.md) - -你可以从[GitHub存储库](https://github.com/volosoft/abp/tree/master/samples/BookStore)访问应用程序的**源代码**. - -> 你也可以观看由ABP社区成员为本教程录制的[视频课程](https://amazingsolutions.teachable.com/p/lets-build-the-bookstore-application). - -### 新增 Book 实体 - -通过本节, 你将会了解如何创建一个 modal form 来实现新增书籍的功能. 最终成果如下图所示: - -![bookstore-create-dialog](images/bookstore-create-dialog-2.png) - -#### 新建 modal form - -在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个 `CreateModal.cshtml` Razor页面: - -![bookstore-add-create-dialog](images/bookstore-add-create-dialog-v2.png) - -##### CreateModal.cshtml.cs - -展开 `CreateModal.cshtml`,打开 `CreateModal.cshtml.cs` 代码文件,用如下代码替换 `CreateModalModel` 类的实现: - -````C# -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace Acme.BookStore.Web.Pages.Books -{ - public class CreateModalModel : BookStorePageModel - { - [BindProperty] - public CreateUpdateBookDto Book { get; set; } - - private readonly IBookAppService _bookAppService; - - public CreateModalModel(IBookAppService bookAppService) - { - _bookAppService = bookAppService; - } - - public async Task OnPostAsync() - { - await _bookAppService.CreateAsync(Book); - return NoContent(); - } - } -} -```` - -* 该类派生于 `BookStorePageModel` 而非默认的 `PageModel`. `BookStorePageModel` 继承了 `PageModel` 并且添加了一些可以被你的page model类使用的通用属性和方法. -* `Book` 属性上的 `[BindProperty]` 特性将post请求提交上来的数据绑定到该属性上. -* 该类通过构造函数注入了 `IBookAppService` 应用服务,并且在 `OnPostAsync` 处理程序中调用了服务的 `CreateAsync` 方法. - -##### CreateModal.cshtml - -打开 `CreateModal.cshtml` 文件并粘贴如下代码: - -````html -@page -@inherits Acme.BookStore.Web.Pages.BookStorePage -@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal -@model Acme.BookStore.Web.Pages.Books.CreateModalModel -@{ - Layout = null; -} - - - - - - - - - -```` - -* 这个 modal 使用 `abp-dynamic-form` Tag Helper 根据 `CreateBookViewModel` 类自动构建了表单. - * `abp-model` 指定了 `Book` 属性为模型对象. - * `data-ajaxForm` 设置了表单通过AJAX提交,而不是经典的页面回发. - * `abp-form-content` tag helper 作为表单控件渲染位置的占位符 (这是可选的,只有你在 `abp-dynamic-form` 中像本示例这样添加了其他内容才需要). - -#### 添加 "New book" 按钮 - -打开 `Pages/Books/Index.cshtml` 并按如下代码修改 `abp-card-header` : - -````html - - - -

@L["Books"]

-
- - - -
-
-```` - -如下图所示,只是在表格 **右上方** 添加了 **New book** 按钮: - -![bookstore-new-book-button](images/bookstore-new-book-button.png) - -打开 `Pages/books/index.js` 在datatable配置代码后面添加如下代码: - -````js -var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); - -createModal.onResult(function () { - dataTable.ajax.reload(); -}); - -$('#NewBookButton').click(function (e) { - e.preventDefault(); - createModal.open(); -}); -```` - -* `abp.ModalManager` 是一个在客户端打开和管理modal的辅助类.它基于Twitter Bootstrap的标准modal组件通过简化的API抽象隐藏了许多细节. - -现在,你可以 **运行程序** 通过新的 modal form 来创建书籍了. - -### 编辑更新已存在的 Book 实体 - -在 `Acme.BookStore.Web` 项目的 `Pages/Books` 目录下新建一个名叫 `EditModal.cshtml` 的Razor页面: - -![bookstore-add-edit-dialog](images/bookstore-add-edit-dialog.png) - -#### EditModal.cshtml.cs - -展开 `EditModal.cshtml`,打开 `EditModal.cshtml.cs` 文件( `EditModalModel` 类) 并替换成以下代码: - -````csharp -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace Acme.BookStore.Web.Pages.Books -{ - public class EditModalModel : BookStorePageModel - { - [HiddenInput] - [BindProperty(SupportsGet = true)] - public Guid Id { get; set; } - - [BindProperty] - public CreateUpdateBookDto Book { get; set; } - - private readonly IBookAppService _bookAppService; - - public EditModalModel(IBookAppService bookAppService) - { - _bookAppService = bookAppService; - } - - public async Task OnGetAsync() - { - var bookDto = await _bookAppService.GetAsync(Id); - Book = ObjectMapper.Map(bookDto); - } - - public async Task OnPostAsync() - { - await _bookAppService.UpdateAsync(Id, Book); - return NoContent(); - } - } -} -```` - -* `[HiddenInput]` 和 `[BindProperty]` 是标准的 ASP.NET Core MVC 特性.这里启用 `SupportsGet` 从Http请求的查询字符串中获取Id的值. -* 在 `OnGetAsync` 方法中,将 `BookAppService.GetAsync` 方法返回的 `BookDto` 映射成 `CreateUpdateBookDto` 并赋值给Book属性. -* `OnPostAsync` 方法直接使用 `BookAppService.UpdateAsync` 来更新实体. - -#### BookDto到CreateUpdateBookDto对象映射 - -为了执行`BookDto`到`CreateUpdateBookDto`对象映射,请打开`Acme.BookStore.Web`项目中的`BookStoreWebAutoMapperProfile.cs`并更改它,如下所示: - -````csharp -using AutoMapper; - -namespace Acme.BookStore.Web -{ - public class BookStoreWebAutoMapperProfile : Profile - { - public BookStoreWebAutoMapperProfile() - { - CreateMap(); - } - } -} -```` - -* 刚刚添加了`CreateMap();`作为映射定义. - -#### EditModal.cshtml - -将 `EditModal.cshtml` 页面内容替换成如下代码: - -````html -@page -@inherits Acme.BookStore.Web.Pages.BookStorePage -@using Acme.BookStore.Web.Pages.Books -@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal -@model EditModalModel -@{ - Layout = null; -} - - - - - - - - - - -```` - -这个页面内容和 `CreateModal.cshtml` 非常相似,除了以下几点: - -* 它包含`id`属性的`abp-input`, 用于存储编辑书的id(它是隐藏的Input) -* 此页面指定的post地址是`Books/EditModal`, 并用文本 *Update* 作为 modal 标题. - -#### 为表格添加 "操作(Actions)" 下拉菜单 - -我们将为表格每行添加下拉按钮 ("Actions") . 最终效果如下: - -![bookstore-books-table-actions](images/bookstore-books-table-actions.png) - -打开 `Pages/Books/Index.cshtml` 页面,并按下方所示修改表格部分的代码: - -````html - - - - @L["Actions"] - @L["Name"] - @L["Type"] - @L["PublishDate"] - @L["Price"] - @L["CreationTime"] - - - -```` - -* 只是为"Actions"增加了一个 `th` 标签. - -打开 `Pages/books/index.js` 并用以下内容进行替换: - -````js -$(function () { - - var l = abp.localization.getResource('BookStore'); - - var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); - var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal'); - - var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ - processing: true, - serverSide: true, - paging: true, - searching: false, - autoWidth: false, - scrollCollapse: true, - order: [[1, "asc"]], - ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), - columnDefs: [ - { - rowAction: { - items: - [ - { - text: l('Edit'), - action: function (data) { - editModal.open({ id: data.record.id }); - } - } - ] - } - }, - { data: "name" }, - { data: "type" }, - { data: "publishDate" }, - { data: "price" }, - { data: "creationTime" } - ] - })); - - createModal.onResult(function () { - dataTable.ajax.reload(); - }); - - editModal.onResult(function () { - dataTable.ajax.reload(); - }); - - $('#NewBookButton').click(function (e) { - e.preventDefault(); - createModal.open(); - }); -}); -```` - -* 通过 `abp.localization.getResource('BookStore')` 可以在客户端使用服务器端定义的相同的本地化语言文本. -* 添加了一个名为 `createModal` 的新的 `ModalManager` 来打开创建用的 modal 对话框. -* 添加了一个名为 `editModal` 的新的 `ModalManager` 来打开编辑用的 modal 对话框. -* 在 `columnDefs` 起始处新增一列用于显示 "Actions" 下拉按钮. -* "New Book"动作只需调用`createModal.open`来打开创建对话框. -* "Edit" 操作只是简单调用 `editModal.open` 来打开编辑对话框. - -现在,你可以运行程序,通过编辑操作来更新任一个book实体. - -### 删除一个已有的Book实体 - -打开 `Pages/books/index.js` 文件,在 `rowAction` `items` 下新增一项: - -````js -{ - text: l('Delete'), - confirmMessage: function (data) { - return l('BookDeletionConfirmationMessage', data.record.name); - }, - action: function (data) { - acme.bookStore.book - .delete(data.record.id) - .then(function() { - abp.notify.info(l('SuccessfullyDeleted')); - dataTable.ajax.reload(); - }); - } -} -```` - -* `confirmMessage` 用来在实际执行 `action` 之前向用户进行确认. -* 通过javascript代理方法 `acme.bookStore.book.delete` 执行一个AJAX请求来删除一个book实体. -* `abp.notify.info` 用来在执行删除操作后显示一个toastr通知信息. - -最终的 `index.js` 文件内容如下所示: - -````js -$(function () { - - var l = abp.localization.getResource('BookStore'); - - var createModal = new abp.ModalManager(abp.appPath + 'Books/CreateModal'); - var editModal = new abp.ModalManager(abp.appPath + 'Books/EditModal'); - - var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({ - processing: true, - serverSide: true, - paging: true, - searching: false, - autoWidth: false, - scrollCollapse: true, - order: [[1, "asc"]], - ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList), - columnDefs: [ - { - rowAction: { - items: - [ - { - text: l('Edit'), - action: function (data) { - editModal.open({ id: data.record.id }); - } - }, - { - text: l('Delete'), - confirmMessage: function (data) { - return l('BookDeletionConfirmationMessage', data.record.name); - }, - action: function (data) { - acme.bookStore.book - .delete(data.record.id) - .then(function() { - abp.notify.info(l('SuccessfullyDeleted')); - dataTable.ajax.reload(); - }); - } - } - ] - } - }, - { data: "name" }, - { data: "type" }, - { data: "publishDate" }, - { data: "price" }, - { data: "creationTime" } - ] - })); - - createModal.onResult(function () { - dataTable.ajax.reload(); - }); - - editModal.onResult(function () { - dataTable.ajax.reload(); - }); - - $('#NewBookButton').click(function (e) { - e.preventDefault(); - createModal.open(); - }); -}); -```` - -打开`Acme.BookStore.Domain.Shared`项目中的`en.json`并添加以下行: - -````json -"BookDeletionConfirmationMessage": "Are you sure to delete the book {0}?", -"SuccessfullyDeleted": "Successfully deleted" -```` - -运行程序并尝试删除一个book实体. - -### 下一章 - -查看本教程的 [下一章](Part-III.md) . + \ No newline at end of file diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 9f7d69a784..d61aa63ce6 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -495,6 +495,10 @@ { "text": "示例", "items": [ + { + "text": "所有示例", + "path": "Samples/Index.md" + }, { "text": "微服务示例", "path": "Samples/Microservice-Demo.md" From 10733a68f4a9579305a4f4a9a382f2d4a49e0275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 12 May 2020 19:47:43 +0300 Subject: [PATCH 12/51] Initial SignalR-Integration document --- docs/en/SignalR-Integration.md | 102 ++++++++++++++++++++++++++++++ docs/en/images/signal-js-file.png | Bin 0 -> 57358 bytes 2 files changed, 102 insertions(+) create mode 100644 docs/en/SignalR-Integration.md create mode 100644 docs/en/images/signal-js-file.png diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md new file mode 100644 index 0000000000..cbfa165083 --- /dev/null +++ b/docs/en/SignalR-Integration.md @@ -0,0 +1,102 @@ +## SignalR Integration + +> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages that simplify the integration and usage. + +## Installation + +### Server Side + +It is suggested to use the [ABP CLI](CLI.md) to install this package. + +#### Using the ABP CLI + +Open a command line window in the folder of your project (.csproj file) and type the following command: + +```bash +abp add-package Volo.Abp.AspNetCore.SignalR +``` + +> You typically want to add this package to the web or API layer of your application, depending on your architecture. + +#### Manual Installation + +If you want to manually install; + +1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project: + + ``` + Install-Package Volo.Abp.BackgroundJobs.HangFire + ``` + + Or use the Visual Studio NuGet package management UI to install it. + +2. Add the `AbpAspNetCoreSignalRModule` to the dependency list of your module: + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpAspNetCoreSignalRModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +``` + +### Client Side + +Client side installation depends on your UI framework. + +#### ASP.NET Core MVC / Razor Pages UI + +Run the following command in the root folder of your web project: + +````bash +yarn add @abp/signalr +```` + +> This requires to [install yarn](https://yarnpkg.com/) if you haven't install before. + +This will add the `@abp/signalr` to the dependencies in the `package.json` of your project: + +````json +{ + ... + "dependencies": { + ... + "@abp/signalr": "~2.7.0" + } +} +```` + +Run the `gulp` in the root folder of your web project: + +````bash +gulp +```` + +This will copy the SignalR JavaScript files into your project: + +![signal-js-file](D:\Github\abp\docs\en\images\signal-js-file.png) + +Finally, add the following code to your page/view to include the `signalr.js` file + +````xml +@section scripts { + +} +```` + +It requires to add `@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR` to your page/view. + +> You could add the `signalr.js` file in a standard way. But using the `SignalRBrowserScriptContributor` has additional benefits. See the [Client Side Package Management](UI/AspNetCore/Client-Side-Package-Management.md) and [Bundling & Minification](UI/AspNetCore/Bundling-Minification.md) documents for details. + +That's all. you can use the [SignalR JavaScript API](https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client) in your page. + +#### Other UI Frameworks / Clients + +Please refer to [Microsoft's documentation](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) for other type of clients. + +## The ABP Framework Integration + +When you use the ABP Framework integration, you have some additional benefits. + diff --git a/docs/en/images/signal-js-file.png b/docs/en/images/signal-js-file.png new file mode 100644 index 0000000000000000000000000000000000000000..19fc6cefe3480204c2f0a31131cb50f1b2690d1d GIT binary patch literal 57358 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 LNkvXXu0mjf{{oRT literal 0 HcmV?d00001 From 51435d0c3d6c53e78b86e10edc2c88fd508126ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 12 May 2020 20:05:30 +0300 Subject: [PATCH 13/51] Create HubRouteAttribute.GetRoutePatternForType() --- .../Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs index 5de916a89d..0373fbf691 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubRouteAttribute.cs @@ -13,6 +13,11 @@ namespace Volo.Abp.AspNetCore.SignalR RoutePattern = routePattern; } + public virtual string GetRoutePatternForType(Type hubType) + { + return RoutePattern; + } + public static string GetRoutePattern() where THub : Hub { @@ -24,7 +29,7 @@ namespace Volo.Abp.AspNetCore.SignalR var routeAttribute = hubType.GetSingleAttributeOrNull(); if (routeAttribute != null) { - return routeAttribute.RoutePattern; + return routeAttribute.GetRoutePatternForType(hubType); } return "/signalr-hubs/" + hubType.Name.RemovePostFix("Hub").ToKebabCase(); From 5651bbb8d088d63fed23405d7bd7f75e8cd02444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 12 May 2020 20:05:40 +0300 Subject: [PATCH 14/51] Update SignalR-Integration.md --- docs/en/SignalR-Integration.md | 54 +++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index cbfa165083..585b47d76d 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -42,6 +42,8 @@ public class YourModule : AbpModule } ``` +> You don't need to use the `services.AddSignalR()` and the `app.UseEndpoints(...)`, it's done by the `AbpAspNetCoreSignalRModule`. + ### Client Side Client side installation depends on your UI framework. @@ -98,5 +100,55 @@ Please refer to [Microsoft's documentation](https://docs.microsoft.com/en-us/asp ## The ABP Framework Integration -When you use the ABP Framework integration, you have some additional benefits. +This section covers the additional benefits when you use the ABP Framework integration packages. + +### Hub Route & Mapping + +ABP automatically registers Hubs to the [dependency injection](Dependency-Injection.md) (as transient) and maps the hub endpoint. So, you don't have to use the ` app.UseEndpoints(...)` to map your hubs. Hub route (URL) is determined conventionally based on your hub name. + +Example: + +````csharp +public class MessagingHub : Hub +{ + //... +} +```` + +The hub route will be `/signalr-hubs/messasing` for the `MessasingHub`: + +* Adding a standard `/signalr-hubs/` prefix +* Continue with the **camel case** hub name, without the `Hub` postfix. + +If you want to specify the route, you can use the `HubRoute` attribute: + +````csharp +[HubRoute("/my-messasing-hub")] +public class MessagingHub : Hub +{ + //... +} +```` + +### AbpHub Base Class + +Instead of the standard `Hub` class, you can inherit from the `AbpHub` which has useful base properties, like `CurrentUser`. + +Example: + +````csharp +public class MessagingHub : AbpHub +{ + public async Task SendMessage(string targetUserName, string message) + { + var currentUserName = CurrentUser.UserName; //Access to the current user info + var txt = L["MyText"]; //Localization + } +} +```` + + + +## Example Application +See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messaged between (authenticated) users. \ No newline at end of file From 22938921d303b0940ac5cc86833e57bcfbd4ce18 Mon Sep 17 00:00:00 2001 From: ChangYinShung Date: Wed, 13 May 2020 01:57:46 +0800 Subject: [PATCH 15/51] add Identity Module's Localize for zh-hant add description of RequireConfirmedPhoneNumber in zh-hant --- .../Volo/Abp/Identity/Localization/zh-Hant.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json index 240b7babdf..8ad18c7c1c 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/zh-Hant.json @@ -82,6 +82,7 @@ "DisplayName:Abp.Identity.Lockout.LockoutDuration": "鎖定時間(秒)", "DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "最大失敗存取嘗試次數", "DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "要求驗證的電子信箱", + "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "啟用手機號碼驗證", "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "要求驗證的手機號碼", "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "啟用使用者名稱更新", "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "啟用電子信箱更新", @@ -95,9 +96,9 @@ "Description:Abp.Identity.Lockout.LockoutDuration": "當鎖定發生時使用者被鎖定的時間(秒).", "Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "如果啟用鎖定,當使用者被鎖定前失敗的存取嘗試次數.", "Description:Abp.Identity.SignIn.RequireConfirmedEmail": "登入時是否需要驗證電子信箱.", + "Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "使用者手機號碼是否需要驗證.", "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "登入時是否需要驗證手機號碼.", "Description:Abp.Identity.User.IsUserNameUpdateEnabled": "是否允許使用者更新使用者名稱.", "Description:Abp.Identity.User.IsEmailUpdateEnabled": "是否允許使用者更新電子信箱." - } -} \ No newline at end of file +} From 0e492d23df67ff3a8cd7f1e4f850ccf684bc1697 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Tue, 12 May 2020 21:05:25 +0300 Subject: [PATCH 16/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 43 +++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index d94dc6931c..4a1b99f75d 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -169,7 +169,9 @@ First of all, create a class that inherited from `TemplateDefinitionProvider` ab `Define` method requires a context that is `ITemplateDefinitionContext`. This `context` is a storage for template definitions and we will add our template definitions to the context. -> For default, ABP uses **`Virtual Files`** for text templates. All given examples are for `Virtual File Text Template Definitions`. +> **NOTE!** For default, ABP uses **Virtual File System** for text templates. Do not forget to register your files as an `Embedded Resource`. Please check the [Virtual File System Documentation](Virtual-File-System.md) for more details. + +> All given examples are for `Virtual File Text Template Definitions`. ```csharp public class MyTemplateDefinitionProvider : TemplateDefinitionProvider @@ -204,9 +206,42 @@ public class MyTemplateDefinitionProvider : TemplateDefinitionProvider } ``` -As you see in the given example all Text Templates are added with `(ITemplateDefinitionContext)context.Add` method. This method requires a `TemplateDefinition` object. Then we call `WithVirtualFilePath` method with chaining for the describe where is the virtual files. +As you see in the given example, all Text Templates are added with `(ITemplateDefinitionContext)context.Add` method. This method requires a `TemplateDefinition` object. Then we call `WithVirtualFilePath` method with chaining for the describe where is the virtual files. + +`WithVirtualFilePath` is requires one `tpl` file path for the `Inline Localized` Text Templates. If your Text Tempalte is `Multi Localized` you should create a folder and store each different culture files under that. So you can send the folder path as a parameter to `WithVirtualFilePath`. + +> Inline Localized File + +``` +/ Folder / ForgotPasswordEmail.tpl +``` + +> Multi Content Localization + +``` +/ Folder / ForgotPasswordEmail / en.tpl +/ Folder / ForgotPasswordEmail / tr.tpl +``` + + +## Rendering + +When one template is registered, it is easy to render and get the result with `ITemplateRenderer` service. + +`ITemplateRenderer` service has one method that named `RenderAsync` and to render your content and it is requires some parametres. +- `templateName` (string) **_Not Null_** +- `model` (object) **_Can Be Null_** +- `cultureName` (string) **_Can Be Null_** +- `globalContext` (dictionary) **_Can Be Null_** +`templateName` is exactly same with Template Definition Name. + +`model` is a dynamic object. This is using to put dynamic data into template. For more information, please check by [Scriban Documentation](https://github.com/lunet-io/scriban). + +`cultureName` is your rendering destination culture. When it is not exist, it will use the default culture. + +`globalContext` = TODO ## Getting Template Definitions @@ -214,6 +249,4 @@ As you see in the given example all Text Templates are added with `(ITemplateDef ## Getting Template Contents -### Template Content Contributor - -## Rendering +### Template Content Contributor \ No newline at end of file From 6b2d6ef636d4dcc158b97b3c293f6bffa9ed4d1b Mon Sep 17 00:00:00 2001 From: Ahmet Date: Tue, 12 May 2020 21:42:10 +0300 Subject: [PATCH 17/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 4a1b99f75d..d042f06967 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -237,16 +237,31 @@ When one template is registered, it is easy to render and get the result with `I `templateName` is exactly same with Template Definition Name. -`model` is a dynamic object. This is using to put dynamic data into template. For more information, please check by [Scriban Documentation](https://github.com/lunet-io/scriban). +`model` is a dynamic object. This is using to put dynamic data into template. For more information, please look at [Scriban Documentation](https://github.com/lunet-io/scriban). `cultureName` is your rendering destination culture. When it is not exist, it will use the default culture. +> If `cultureName` has a language tag it will try to find exact culture with tag, if it is not exist it will use the language family. + +> If you try to render content with _"es-MX"_ it will search your template with _"es-MX"_ culture, when it fails to find, it will try to render _"es"_ culture content. If still can't find it will render the default culture content that you defined. + `globalContext` = TODO -## Getting Template Definitions +## Template Definition Manager + +When you want to get your `Template Definitions`, you can use a singleton service that named `Template Definition Manager` in runtime. + +To use it, inject `ITemplateDefinitionManager` service. + +It has three method that you can get your Template Definitions. + +- `Get` +- `GetOrNull` +- `GetAll` -### Template Definition Manager +`Get` and `GetOrNull` requires a string parameter that name of template definition. `Get` will throw error when it is not exist but `GetOrNull` returns `null`. -## Getting Template Contents +`GetAll` returns you all registered template definitions. + +## Template Content Contributor -### Template Content Contributor \ No newline at end of file From e869d5446b121e75cae71abdbb5845540ee378b8 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Tue, 12 May 2020 22:05:41 +0300 Subject: [PATCH 18/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index d042f06967..4ffce45dd8 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -247,6 +247,27 @@ When one template is registered, it is easy to render and get the result with `I `globalContext` = TODO +## Template Content Provider + +When you want to get your stored template content you can use `ITemplateContentProvider`. + +`ITemplateContentProvider` has one method that named `GetContentOrNullAsync` with two different overriding, and it returns you a string of template content or null. (**without rendering**) + +- `templateName` (_string_) or `templateDefinition` (_`TemplateDefinition`_) +- `cultureName` (_string_) +- `tryDefaults` (_bool_) +- `useCurrentCultureIfCultureNameIsNull` (_bool_) + +### Usage + +First parametres of `GetContentOrNullAsync` (`templateName` or `templateDefinition`) are required, the other three parametres can be null. + +If you want to get exact culture content, set `tryDefaults` and `useCurrentCultureIfCultureNameIsNull` as a `false`. Because the `GetContentOrNullAsync` tries to return content of template. + +> Example Scenario + +> If you have a template content that culture "`es`", when you try to get template content with "`es-MX`" it will try to return first "`es-MX`", if it fails it will return "`es`" content. If you set `tryDefaults` and `useCurrentCultureIfCultureNameIsNull` as `false` it will return `null`. + ## Template Definition Manager When you want to get your `Template Definitions`, you can use a singleton service that named `Template Definition Manager` in runtime. @@ -265,3 +286,6 @@ It has three method that you can get your Template Definitions. ## Template Content Contributor +You can store your `Template Contents` in any resource. To make it, just create a class that implements `ITemplateContentContributor` interface. + +`ITemplateContentContributor` has a one method that named `GetOrNullAsync`. This method must return content **without rendering**. \ No newline at end of file From a3d4d2e2a9c3ff6b9c17577799e0f59d42a86b86 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Tue, 12 May 2020 22:11:43 +0300 Subject: [PATCH 19/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 4ffce45dd8..f508fdaece 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -95,7 +95,7 @@ You can localize your Text Templates by choosing two different method. #### Inline Localization -Inline localized Text Templates is using only one content resource, and it is using the `Abp.Localization` to get content in different languages/cultures. +Inline localized Text Templates is using only one content resource, and it is using the [Abp.Localization](Localization.md) to get content in different languages/cultures. > Example Inline Localized Text Template: > @@ -288,4 +288,4 @@ It has three method that you can get your Template Definitions. You can store your `Template Contents` in any resource. To make it, just create a class that implements `ITemplateContentContributor` interface. -`ITemplateContentContributor` has a one method that named `GetOrNullAsync`. This method must return content **without rendering**. \ No newline at end of file +`ITemplateContentContributor` has a one method that named `GetOrNullAsync`. This method must return content **without rendering** if it finds in your resource or returns `null`. \ No newline at end of file From b51c2204dd9e62e4a4fdaa928f55d31ab8791b17 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Wed, 13 May 2020 00:24:53 +0300 Subject: [PATCH 20/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index f508fdaece..5451b1f45e 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -230,20 +230,20 @@ When one template is registered, it is easy to render and get the result with `I `ITemplateRenderer` service has one method that named `RenderAsync` and to render your content and it is requires some parametres. -- `templateName` (string) **_Not Null_** -- `model` (object) **_Can Be Null_** -- `cultureName` (string) **_Can Be Null_** -- `globalContext` (dictionary) **_Can Be Null_** +- `templateName` (_string_) +- `model` (_object_) +- `cultureName` (_string_) +- `globalContext` (_dictionary_) `templateName` is exactly same with Template Definition Name. `model` is a dynamic object. This is using to put dynamic data into template. For more information, please look at [Scriban Documentation](https://github.com/lunet-io/scriban). -`cultureName` is your rendering destination culture. When it is not exist, it will use the default culture. +`cultureName` is your destination rendering culture. When it is not exist, it will use the default culture. > If `cultureName` has a language tag it will try to find exact culture with tag, if it is not exist it will use the language family. -> If you try to render content with _"es-MX"_ it will search your template with _"es-MX"_ culture, when it fails to find, it will try to render _"es"_ culture content. If still can't find it will render the default culture content that you defined. +> Example: If you try to render content with _"es-MX"_ it will search your template with _"es-MX"_ culture, when it fails to find, it will try to render _"es"_ culture content. If still can't find it will render the default culture content that you defined. `globalContext` = TODO @@ -288,4 +288,4 @@ It has three method that you can get your Template Definitions. You can store your `Template Contents` in any resource. To make it, just create a class that implements `ITemplateContentContributor` interface. -`ITemplateContentContributor` has a one method that named `GetOrNullAsync`. This method must return content **without rendering** if it finds in your resource or returns `null`. \ No newline at end of file +`ITemplateContentContributor` has a one method that named `GetOrNullAsync`. This method must return content **without rendering** if that is exist in your resource or must return `null`. \ No newline at end of file From 8dd8805c82edde1b37cfb98bb66987974d08fea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 01:23:13 +0300 Subject: [PATCH 21/51] Resolved #3912: Finalize the signalr integration document. --- docs/en/SignalR-Integration.md | 83 +++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index 585b47d76d..5b4176b123 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -1,4 +1,4 @@ -## SignalR Integration +# SignalR Integration > It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages that simplify the integration and usage. @@ -46,7 +46,7 @@ public class YourModule : AbpModule ### Client Side -Client side installation depends on your UI framework. +Client side installation depends on your UI framework / client type. #### ASP.NET Core MVC / Razor Pages UI @@ -104,7 +104,7 @@ This section covers the additional benefits when you use the ABP Framework integ ### Hub Route & Mapping -ABP automatically registers Hubs to the [dependency injection](Dependency-Injection.md) (as transient) and maps the hub endpoint. So, you don't have to use the ` app.UseEndpoints(...)` to map your hubs. Hub route (URL) is determined conventionally based on your hub name. +ABP automatically registers all the hubs to the [dependency injection](Dependency-Injection.md) (as transient) and maps the hub endpoint. So, you don't have to use the ` app.UseEndpoints(...)` to map your hubs. Hub route (URL) is determined conventionally based on your hub name. Example: @@ -118,7 +118,7 @@ public class MessagingHub : Hub The hub route will be `/signalr-hubs/messasing` for the `MessasingHub`: * Adding a standard `/signalr-hubs/` prefix -* Continue with the **camel case** hub name, without the `Hub` postfix. +* Continue with the **camel case** hub name, without the `Hub` suffix. If you want to specify the route, you can use the `HubRoute` attribute: @@ -130,9 +130,9 @@ public class MessagingHub : Hub } ```` -### AbpHub Base Class +### AbpHub Base Classes -Instead of the standard `Hub` class, you can inherit from the `AbpHub` which has useful base properties, like `CurrentUser`. +Instead of the standard `Hub` and `Hub` classes, you can inherit from the `AbpHub` or `AbpHub` which hve useful base properties like `CurrentUser`. Example: @@ -147,7 +147,78 @@ public class MessagingHub : AbpHub } ```` +> While you could inject the same properties into your hub constructor, this way simplifies your hub class. +### Manual Registration / Mapping + +ABP automatically registers all the hubs to the [dependency injection](Dependency-Injection.md) as a **transient service**. If you want to **disable auto dependency injection** registration for your hub class, just add a `DisableConventionalRegistration` attribute. You can still register your hub class to dependency injection in the `ConfigureServices` method of your module if you like: + +````csharp +context.Services.AddTransient(); +```` + +When **you or ABP** register the class to the dependency injection, it is automatically mapped to the endpoint route configuration just as described in the previous sections. You can use `DisableAutoHubMap` attribute if you want to manually map your hub class. + +For manual mapping, you have two options: + +1. Use the `AbpSignalROptions` to add your map configuration (in the `ConfigureServices` method of your [module](Module-Development-Basics.md)), so ABP still performs the endpoint mapping for your hub: + +````csharp +Configure(options => +{ + options.Hubs.Add( + new HubConfig( + typeof(MessagingHub), //Hub type + "/my-messaging/route", //Hub route (URL) + hubOptions => + { + //Additional options + hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + } + ) + ); +}); +```` + +This is a good way to provide additional SignalR options. + +If you don't want to disable auto hub map, but still want to perform additional SignalR configuration, use the `options.Hubs.AddOrUpdate(...)` method: + +````csharp +Configure(options => +{ + options.Hubs.AddOrUpdate( + typeof(MessagingHub), //Hub type + config => //Additional configuration + { + config.RoutePattern = "/my-messaging-hub"; //override the default route + config.ConfigureActions.Add(hubOptions => + { + //Additional options + hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + }); + } + ); +}); +```` + +This is the way you can modify the options of a hub class defined in a depended module (where you don't have the source code access). + +2. Change `app.UseConfiguredEndpoints` in the `OnApplicationInitialization` method of your [module](Module-Development-Basics.md) as shown below (added a lambda method as the parameter). + +````csharp +app.UseConfiguredEndpoints(endpoints => +{ + endpoints.MapHub("/my-messaging-hub", options => + { + options.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + }); +}); +```` + +### UserIdProvider + +ABP implements SignalR's `IUserIdProvider` interface to provide the current user id from the `ICurrentUser` service of the ABP framework (see [the current user service](CurrentUser.md)), so it will be integrated to the authentication system of your application. The implementing class is the `AbpSignalRUserIdProvider`, if you want to change/override it. ## Example Application From 2619eada343b2a66f46323d7becde90edfe5bafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 01:24:51 +0300 Subject: [PATCH 22/51] Added HubConfigList and DisableAutoHubMapAttribute for the signalr integration. --- .../SignalR/AbpAspNetCoreSignalRModule.cs | 21 ++++++++++++---- .../AspNetCore/SignalR/AbpSignalROptions.cs | 8 +++---- .../SignalR/DisableAutoHubMapAttribute.cs | 9 +++++++ .../Volo/Abp/AspNetCore/SignalR/HubConfig.cs | 8 ++++++- .../Abp/AspNetCore/SignalR/HubConfigList.cs | 24 +++++++++++++++++++ 5 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs index e26de184af..b89e5e9158 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRModule.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; using Volo.Abp.Modularity; namespace Volo.Abp.AspNetCore.SignalR @@ -30,7 +31,7 @@ namespace Volo.Abp.AspNetCore.SignalR public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.AddSignalR(); - + Configure(options => { options.EndpointConfigureActions.Add(endpointContext => @@ -65,7 +66,7 @@ namespace Volo.Abp.AspNetCore.SignalR services.OnRegistred(context => { - if (typeof(Hub).IsAssignableFrom(context.ImplementationType)) + if (IsHubClass(context) && !IsDisabledForAutoMap(context)) { hubTypes.Add(context.ImplementationType); } @@ -80,7 +81,17 @@ namespace Volo.Abp.AspNetCore.SignalR }); } - private void MapHubType( + private static bool IsHubClass(IOnServiceRegistredContext context) + { + return typeof(Hub).IsAssignableFrom(context.ImplementationType); + } + + private static bool IsDisabledForAutoMap(IOnServiceRegistredContext context) + { + return context.ImplementationType.IsDefined(typeof(DisableAutoHubMapAttribute), true); + } + + private void MapHubType( Type hubType, IEndpointRouteBuilder endpoints, string pattern, @@ -101,8 +112,8 @@ namespace Volo.Abp.AspNetCore.SignalR // ReSharper disable once UnusedMember.Local (used via reflection) private static void MapHub( - IEndpointRouteBuilder endpoints, - string pattern, + IEndpointRouteBuilder endpoints, + string pattern, Action configureOptions) where THub : Hub { diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs index c0a07a2ea3..9adbe8d7a6 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions.cs @@ -1,14 +1,12 @@ -using System.Collections.Generic; - -namespace Volo.Abp.AspNetCore.SignalR +namespace Volo.Abp.AspNetCore.SignalR { public class AbpSignalROptions { - public List Hubs { get; } + public HubConfigList Hubs { get; } public AbpSignalROptions() { - Hubs = new List(); + Hubs = new HubConfigList(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs new file mode 100644 index 0000000000..2d2489bda9 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/DisableAutoHubMapAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class DisableAutoHubMapAttribute : Attribute + { + + } +} diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs index 59c9342fce..be2e2a6877 100644 --- a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfig.cs @@ -19,11 +19,17 @@ namespace Volo.Abp.AspNetCore.SignalR public HubConfig( [NotNull] Type hubType, - [NotNull] string routePattern) + [NotNull] string routePattern, + [CanBeNull] Action configureAction = null) { HubType = Check.NotNull(hubType, nameof(hubType)); RoutePattern = Check.NotNullOrWhiteSpace(routePattern, nameof(routePattern)); ConfigureActions = new List>(); + + if (configureAction != null) + { + ConfigureActions.Add(configureAction); + } } public static HubConfig Create() diff --git a/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs new file mode 100644 index 0000000000..434e50e515 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.SignalR/Volo/Abp/AspNetCore/SignalR/HubConfigList.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class HubConfigList : List + { + public void AddOrUpdate(Action configAction = null) + { + AddOrUpdate(typeof(THub)); + } + + public void AddOrUpdate(Type hubType, Action configAction = null) + { + var hubConfig = this.GetOrAdd( + c => c.HubType == hubType, + () => HubConfig.Create(hubType) + ); + + configAction?.Invoke(hubConfig); + } + } +} \ No newline at end of file From 383d9fc59d8be2d257f04af23a8de749818271ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 02:09:08 +0300 Subject: [PATCH 23/51] Add signalr demo to samples. --- docs/en/Samples/Index.md | 3 +++ docs/en/SignalR-Integration.md | 6 ++++-- docs/en/images/signalr-demo-chat.png | Bin 0 -> 46010 bytes 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 docs/en/images/signalr-demo-chat.png diff --git a/docs/en/Samples/Index.md b/docs/en/Samples/Index.md index 0c9ce68886..12095bb2c9 100644 --- a/docs/en/Samples/Index.md +++ b/docs/en/Samples/Index.md @@ -35,6 +35,9 @@ While there is no Razor Pages & MongoDB combination, you can check both document * **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) * [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) + * [Signal Integration document](../SignalR-Integration.md) * **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) diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index 5b4176b123..415b585f3f 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -78,7 +78,7 @@ gulp This will copy the SignalR JavaScript files into your project: -![signal-js-file](D:\Github\abp\docs\en\images\signal-js-file.png) +![signal-js-file](images/signal-js-file.png) Finally, add the following code to your page/view to include the `signalr.js` file @@ -222,4 +222,6 @@ ABP implements SignalR's `IUserIdProvider` interface to provide the current user ## Example Application -See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messaged between (authenticated) users. \ No newline at end of file +See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messages between (authenticated) users. + +![signalr-demo-chat](images/signalr-demo-chat.png) \ No newline at end of file diff --git a/docs/en/images/signalr-demo-chat.png b/docs/en/images/signalr-demo-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..120d7eda6ed54fd4e6a90ef31cfd93f4fc9e4b43 GIT binary patch literal 46010 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 C2)D3+kz51_-t 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 G`+ot40&F<| literal 0 HcmV?d00001 From cd3d1e57bd3d0f4ccd09c1de38a967a141c19828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 02:49:13 +0300 Subject: [PATCH 24/51] Create signalr test project. --- framework/Volo.Abp.sln | 7 +++++ .../Volo.Abp.AspNetCore.SignalR.Tests.csproj | 20 ++++++++++++++ .../SignalR/AbpAspNetCoreSignalRTestBase.cs | 12 +++++++++ .../SignalR/AbpAspNetCoreSignalRTestModule.cs | 15 +++++++++++ .../SignalR/AbpSignalROptions_Tests.cs | 26 +++++++++++++++++++ .../SampleHubs/DisableAutoHubMapHub.cs | 10 +++++++ .../DisableConventionalRegistrationHub.cs | 11 ++++++++ .../SignalR/SampleHubs/RegularAbpHub.cs | 6 +++++ .../SignalR/SampleHubs/RegularHub.cs | 8 ++++++ 9 files changed, 115 insertions(+) create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 3ff151439f..2db0442389 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -287,6 +287,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Validation.Abstrac EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR", "src\Volo.Abp.AspNetCore.SignalR\Volo.Abp.AspNetCore.SignalR.csproj", "{B64FCE08-E9D2-4984-BF12-FE199F257416}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.SignalR.Tests", "test\Volo.Abp.AspNetCore.SignalR.Tests\Volo.Abp.AspNetCore.SignalR.Tests.csproj", "{8B758716-DCC9-4223-8421-5588D1597487}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -853,6 +855,10 @@ Global {B64FCE08-E9D2-4984-BF12-FE199F257416}.Debug|Any CPU.Build.0 = Debug|Any CPU {B64FCE08-E9D2-4984-BF12-FE199F257416}.Release|Any CPU.ActiveCfg = Release|Any CPU {B64FCE08-E9D2-4984-BF12-FE199F257416}.Release|Any CPU.Build.0 = Release|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B758716-DCC9-4223-8421-5588D1597487}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -998,6 +1004,7 @@ Global {251C7FD3-D313-4BCE-8068-352EC7EEA275} = {447C8A77-E5F0-4538-8687-7383196D04EA} {FA5D1D6A-2A05-4A3D-99C1-2B6C1D1F99A3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {B64FCE08-E9D2-4984-BF12-FE199F257416} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {8B758716-DCC9-4223-8421-5588D1597487} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj new file mode 100644 index 0000000000..8e1ab29329 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo.Abp.AspNetCore.SignalR.Tests.csproj @@ -0,0 +1,20 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs new file mode 100644 index 0000000000..c7a31c8b8a --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs new file mode 100644 index 0000000000..eee1dd6ed3 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestModule.cs @@ -0,0 +1,15 @@ +using Volo.Abp.Autofac; +using Volo.Abp.Modularity; + +namespace Volo.Abp.AspNetCore.SignalR +{ + [DependsOn( + typeof(AbpAspNetCoreSignalRModule), + typeof(AbpTestBaseModule), + typeof(AbpAutofacModule) + )] + public class AbpAspNetCoreSignalRTestModule : AbpModule + { + + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs new file mode 100644 index 0000000000..38e262abe3 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpSignalROptions_Tests.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.Options; +using Shouldly; +using Volo.Abp.AspNetCore.SignalR.SampleHubs; +using Xunit; + +namespace Volo.Abp.AspNetCore.SignalR +{ + public class AbpSignalROptions_Tests : AbpAspNetCoreSignalRTestBase + { + private readonly AbpSignalROptions _options; + + public AbpSignalROptions_Tests() + { + _options = GetRequiredService>().Value; + } + + [Fact] + public void Should_Auto_Add_Maps() + { + _options.Hubs.ShouldContain(h => h.HubType == typeof(RegularHub)); + _options.Hubs.ShouldContain(h => h.HubType == typeof(RegularAbpHub)); + _options.Hubs.ShouldNotContain(h => h.HubType == typeof(DisableConventionalRegistrationHub)); + _options.Hubs.ShouldNotContain(h => h.HubType == typeof(DisableAutoHubMapHub)); + } + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs new file mode 100644 index 0000000000..3ceae70af4 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableAutoHubMapHub.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + [DisableAutoHubMap] + public class DisableAutoHubMapHub : Hub + { + + } +} diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs new file mode 100644 index 0000000000..ff28b6eca9 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/DisableConventionalRegistrationHub.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.SignalR; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + [DisableConventionalRegistration] + public class DisableConventionalRegistrationHub : Hub + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs new file mode 100644 index 0000000000..2afceffa8d --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularAbpHub.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + public class RegularAbpHub : AbpHub + { + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs new file mode 100644 index 0000000000..8d3987f314 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/SampleHubs/RegularHub.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.SignalR; + +namespace Volo.Abp.AspNetCore.SignalR.SampleHubs +{ + public class RegularHub : Hub + { + } +} \ No newline at end of file From 3154fbcbfe620ee6e9014fec5f71bff04be15c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 02:50:33 +0300 Subject: [PATCH 25/51] Make AbpAspNetCoreSignalRTestBase abstract. --- .../Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs index c7a31c8b8a..eb3cb0c265 100644 --- a/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs +++ b/framework/test/Volo.Abp.AspNetCore.SignalR.Tests/Volo/Abp/AspNetCore/SignalR/AbpAspNetCoreSignalRTestBase.cs @@ -2,7 +2,7 @@ namespace Volo.Abp.AspNetCore.SignalR { - public class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest + public abstract class AbpAspNetCoreSignalRTestBase : AbpIntegratedTest { protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) { From 9ddd879b6727b41b8f0aafe92f47f63569ad42f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 05:22:33 +0300 Subject: [PATCH 26/51] Fixed #3915: Use SemaphoreSlim in the LocalizedTemplateContentReaderFactory --- .../LocalizedTemplateContentReaderFactory.cs | 46 ++++++------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs index 092815ff6e..b403b37068 100644 --- a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs @@ -1,6 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; +using Nito.AsyncEx; using Volo.Abp.DependencyInjection; using Volo.Abp.VirtualFileSystem; @@ -9,50 +10,33 @@ namespace Volo.Abp.TextTemplating.VirtualFiles public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency { private readonly IVirtualFileProvider _virtualFileProvider; - private readonly Dictionary _readerCache; - private readonly ReaderWriterLockSlim _lock; + private readonly ConcurrentDictionary _readerCache; + protected SemaphoreSlim SyncObj; public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider) { _virtualFileProvider = virtualFileProvider; - _readerCache = new Dictionary(); - _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); + _readerCache = new ConcurrentDictionary(); + SyncObj = new SemaphoreSlim(1, 1); } public async Task CreateAsync(TemplateDefinition templateDefinition) { - _lock.EnterUpgradeableReadLock(); + if (_readerCache.TryGetValue(templateDefinition.Name, out var reader)) + { + return reader; + } - try + using (await SyncObj.LockAsync()) { - var reader = _readerCache.GetOrDefault(templateDefinition.Name); - if (reader != null) + if (_readerCache.TryGetValue(templateDefinition.Name, out reader)) { return reader; } - _lock.EnterWriteLock(); - - try - { - reader = await CreateInternalAsync(templateDefinition); - _readerCache[templateDefinition.Name] = reader; - return reader; - } - finally - { - if (_lock.IsWriteLockHeld) - { - _lock.ExitWriteLock(); - } - } - } - finally - { - if (_lock.IsUpgradeableReadLockHeld) - { - _lock.ExitUpgradeableReadLock(); - } + reader = await CreateInternalAsync(templateDefinition); + _readerCache[templateDefinition.Name] = reader; + return reader; } } From eb59629d21f90accd3f36867b97b379981a8f17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 05:25:43 +0300 Subject: [PATCH 27/51] LocalizedTemplateContentReaderFactory: Make CreateAsync method virtual and private fields protected to easier extend. --- .../LocalizedTemplateContentReaderFactory.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs index b403b37068..02b857bf5d 100644 --- a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs +++ b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs @@ -9,33 +9,33 @@ namespace Volo.Abp.TextTemplating.VirtualFiles { public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency { - private readonly IVirtualFileProvider _virtualFileProvider; - private readonly ConcurrentDictionary _readerCache; + protected IVirtualFileProvider VirtualFileProvider { get; } + protected ConcurrentDictionary ReaderCache { get; } protected SemaphoreSlim SyncObj; public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider) { - _virtualFileProvider = virtualFileProvider; - _readerCache = new ConcurrentDictionary(); + VirtualFileProvider = virtualFileProvider; + ReaderCache = new ConcurrentDictionary(); SyncObj = new SemaphoreSlim(1, 1); } - public async Task CreateAsync(TemplateDefinition templateDefinition) + public virtual async Task CreateAsync(TemplateDefinition templateDefinition) { - if (_readerCache.TryGetValue(templateDefinition.Name, out var reader)) + if (ReaderCache.TryGetValue(templateDefinition.Name, out var reader)) { return reader; } using (await SyncObj.LockAsync()) { - if (_readerCache.TryGetValue(templateDefinition.Name, out reader)) + if (ReaderCache.TryGetValue(templateDefinition.Name, out reader)) { return reader; } reader = await CreateInternalAsync(templateDefinition); - _readerCache[templateDefinition.Name] = reader; + ReaderCache[templateDefinition.Name] = reader; return reader; } } @@ -49,7 +49,7 @@ namespace Volo.Abp.TextTemplating.VirtualFiles return NullLocalizedTemplateContentReader.Instance; } - var fileInfo = _virtualFileProvider.GetFileInfo(virtualPath); + var fileInfo = VirtualFileProvider.GetFileInfo(virtualPath); if (!fileInfo.Exists) { throw new AbpException("Could not find a file/folder at the location: " + virtualPath); @@ -58,7 +58,7 @@ namespace Volo.Abp.TextTemplating.VirtualFiles if (fileInfo.IsDirectory) { var folderReader = new VirtualFolderLocalizedTemplateContentReader(); - await folderReader.ReadContentsAsync(_virtualFileProvider, virtualPath); + await folderReader.ReadContentsAsync(VirtualFileProvider, virtualPath); return folderReader; } else //File From cc1408521f0b422cf73cb298b8b647262e58d4ba Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 13 May 2020 12:03:08 +0800 Subject: [PATCH 28/51] Make AbpPaginationTagHelper generate correct href attribute --- .../en/UI/AspNetCore/Tag-Helpers/Paginator.md | 2 +- .../UI/AspNetCore/Tag-Helpers/Paginator.md | 2 +- .../AbpPaginationTagHelperService.cs | 21 ++++++++++++++++++- .../TagHelpers/Pagination/PagerModel.cs | 5 +++++ .../Pages/Components/Paginator.cshtml | 6 +++--- .../Pages/Components/Paginator.cshtml.cs | 4 ++-- .../Pages/Components/Paginator/Index.cshtml | 7 +------ .../Components/Paginator/Index.cshtml.cs | 4 ++-- .../Pages/Components/Paginator/Index.js | 9 -------- 9 files changed, 35 insertions(+), 25 deletions(-) delete mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md b/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md index 0cc63d49e8..9969bcf3cc 100644 --- a/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Paginator.md @@ -24,7 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components public void OnGet(int currentPage, string sort) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } } diff --git a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md index 7a9b3de126..945da6fa67 100644 --- a/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md +++ b/docs/zh-Hans/UI/AspNetCore/Tag-Helpers/Paginator.md @@ -24,7 +24,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components public void OnGet(int currentPage, string sort) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs index 2ab9ccef96..76bfcb3ef1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/AbpPaginationTagHelperService.cs @@ -1,4 +1,6 @@ -using System.Text; +using System; +using System.Linq; +using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; using Localization.Resources.AbpUi; @@ -123,6 +125,8 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination var tagHelperOutput = await anchorTagHelper.ProcessAndGetOutputAsync(attributeList, context, "a", TagMode.StartTagAndEndTag); + SetHrefAttribute(currentPage, attributeList); + tagHelperOutput.Content.SetHtmlContent(localizer[localizationKey]); var renderedHtml = tagHelperOutput.Render(_encoder); @@ -172,5 +176,20 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination " \r\n" + " \r\n"; } + + protected virtual void SetHrefAttribute(string currentPage, TagHelperAttributeList attributeList) + { + var hrefAttribute = attributeList.FirstOrDefault(x => x.Name.Equals("href", StringComparison.OrdinalIgnoreCase)); + + if (hrefAttribute != null) + { + var pageUrl = TagHelper.Model.PageUrl; + var routeValue = $"currentPage={currentPage}{(TagHelper.Model.Sort.IsNullOrWhiteSpace()? "" : "&sort="+TagHelper.Model.Sort)}"; + pageUrl += pageUrl.Contains("?") ? "&" + routeValue : "?" + routeValue; + + attributeList.Remove(hrefAttribute); + attributeList.Add(new TagHelperAttribute("href", pageUrl, hrefAttribute.ValueStyle)); + } + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs index 8c6eeb34a4..d071db223a 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs @@ -38,6 +38,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination PageSize = pageSize; TotalPageCount = (int)Math.Ceiling(Convert.ToDouble((decimal)TotalItemsCount / PageSize)); Sort = sort; + + if (pageUrl.IsNullOrWhiteSpace() || !pageUrl.StartsWith("/")) + { + pageUrl = "/" + pageUrl; + } PageUrl = pageUrl; if (currentPage > TotalPageCount) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml index be26fb5251..b13e1bc4ef 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml @@ -47,7 +47,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components public void OnGet(int currentPage, string sort) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } } @@ -60,7 +60,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components


-<div class="row mt-3">    
+<div class="row mt-3">
     <div class="col-sm-12 col-md-5">
         Showing 80 to 90 of 100 entries.
     </div>
@@ -105,4 +105,4 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
             
         
     
-
\ No newline at end of file
+
diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs
index 15b31db6da..3723631381 100644
--- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs
+++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo/Pages/Components/Paginator.cshtml.cs
@@ -9,7 +9,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Demo.Pages.Components
 
         public void OnGet(int currentPage, string sort)
         {
-            PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort);
+            PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml
index 6eb48dcd43..d987bce207 100644
--- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml
+++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml
@@ -7,14 +7,9 @@
 @{
     PageLayout.Content.Title = "Paginator";
 }
-@section scripts {
-    
-        
-    
-}
 
 

Paginator

Check the ABP Documentation.

-@await Component.InvokeAsync(typeof(PaginatorDemoViewComponent), new { pagerModel = Model.PagerModel }) \ No newline at end of file +@await Component.InvokeAsync(typeof(PaginatorDemoViewComponent), new { pagerModel = Model.PagerModel }) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs index 50659a0ab4..bc51f80f0a 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.cshtml.cs @@ -9,7 +9,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo.Pages.Components.Paginator public void OnGet(int currentPage = 1, string sort = null) { - PagerModel = new PagerModel(100, 10, currentPage, 10, "Paginator", sort); + PagerModel = new PagerModel(100, 10, currentPage, 10, "/Components/Paginator", sort); } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js b/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js deleted file mode 100644 index 843e0dea00..0000000000 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic.Demo/Pages/Components/Paginator/Index.js +++ /dev/null @@ -1,9 +0,0 @@ -$(function () { - var links = $("a.page-link"); - - $.each(links, function (key, value) { - var oldUrl = links[key].getAttribute("href"); - var value = Number(oldUrl.match(/currentPage=(\d+)&page/)[1]); - links[key].setAttribute("href", "/Components/Paginator?currentPage=" + value); - }) -}); \ No newline at end of file From ae3ae56d6d09d7b928d1bdd5858f63225a7025de Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 13 May 2020 16:48:50 +0800 Subject: [PATCH 29/51] Update PagerModel.cs --- .../TagHelpers/Pagination/PagerModel.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs index d071db223a..db74266f38 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Pagination/PagerModel.cs @@ -39,11 +39,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Pagination TotalPageCount = (int)Math.Ceiling(Convert.ToDouble((decimal)TotalItemsCount / PageSize)); Sort = sort; - if (pageUrl.IsNullOrWhiteSpace() || !pageUrl.StartsWith("/")) - { - pageUrl = "/" + pageUrl; - } - PageUrl = pageUrl; + PageUrl = pageUrl?.EnsureStartsWith('/') ?? "/"; if (currentPage > TotalPageCount) { From ba248a5a4a4f4088c89c7a6a3bb8c06e8000b33c Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 13:36:05 +0300 Subject: [PATCH 30/51] refactor: remove localization property from environment #3791 --- .../apps/dev-app/src/environments/environment.prod.ts | 3 --- .../apps/dev-app/src/environments/environment.ts | 3 --- .../app/angular/src/environments/environment.prod.ts | 11 ++++------- templates/app/angular/src/environments/environment.ts | 11 ++++------- .../angular/src/environments/environment.prod.ts | 3 --- .../module/angular/src/environments/environment.ts | 3 --- 6 files changed, 8 insertions(+), 26 deletions(-) diff --git a/npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts b/npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts index 5b42ebace3..ae0e6aba2c 100644 --- a/npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts +++ b/npm/ng-packs/apps/dev-app/src/environments/environment.prod.ts @@ -19,7 +19,4 @@ export const environment = { url: 'https://localhost:44305', }, }, - localization: { - defaultResourceName: 'MyProjectName', - }, }; diff --git a/npm/ng-packs/apps/dev-app/src/environments/environment.ts b/npm/ng-packs/apps/dev-app/src/environments/environment.ts index ca462ff043..e678b55a74 100644 --- a/npm/ng-packs/apps/dev-app/src/environments/environment.ts +++ b/npm/ng-packs/apps/dev-app/src/environments/environment.ts @@ -19,7 +19,4 @@ export const environment = { url: 'https://localhost:44305', }, }, - localization: { - defaultResourceName: 'MyProjectName', - }, }; diff --git a/templates/app/angular/src/environments/environment.prod.ts b/templates/app/angular/src/environments/environment.prod.ts index bf1c0ea488..ada2b74c97 100644 --- a/templates/app/angular/src/environments/environment.prod.ts +++ b/templates/app/angular/src/environments/environment.prod.ts @@ -2,7 +2,7 @@ export const environment = { production: true, application: { name: 'MyProjectName', - logoUrl: '' + logoUrl: '', }, oAuthConfig: { issuer: 'https://localhost:44305', @@ -11,14 +11,11 @@ export const environment = { scope: 'MyProjectName', showDebugInformation: true, oidc: false, - requireHttps: true + requireHttps: true, }, apis: { default: { - url: 'https://localhost:44305' - } + url: 'https://localhost:44305', + }, }, - localization: { - defaultResourceName: 'MyProjectName' - } }; diff --git a/templates/app/angular/src/environments/environment.ts b/templates/app/angular/src/environments/environment.ts index 6f2a182746..d3676e2d08 100644 --- a/templates/app/angular/src/environments/environment.ts +++ b/templates/app/angular/src/environments/environment.ts @@ -2,7 +2,7 @@ export const environment = { production: false, application: { name: 'MyProjectName', - logoUrl: '' + logoUrl: '', }, oAuthConfig: { issuer: 'https://localhost:44305', @@ -11,14 +11,11 @@ export const environment = { scope: 'MyProjectName', showDebugInformation: true, oidc: false, - requireHttps: true + requireHttps: true, }, apis: { default: { - url: 'https://localhost:44305' - } + url: 'https://localhost:44305', + }, }, - localization: { - defaultResourceName: 'MyProjectName' - } }; diff --git a/templates/module/angular/src/environments/environment.prod.ts b/templates/module/angular/src/environments/environment.prod.ts index ce43f45c24..23d41cf375 100644 --- a/templates/module/angular/src/environments/environment.prod.ts +++ b/templates/module/angular/src/environments/environment.prod.ts @@ -18,7 +18,4 @@ export const environment = { url: 'https://localhost:44300', }, }, - localization: { - defaultResourceName: 'MyProjectName', - }, }; diff --git a/templates/module/angular/src/environments/environment.ts b/templates/module/angular/src/environments/environment.ts index f1c21c40ac..32d3aa3d4c 100644 --- a/templates/module/angular/src/environments/environment.ts +++ b/templates/module/angular/src/environments/environment.ts @@ -18,7 +18,4 @@ export const environment = { url: 'https://localhost:44300', }, }, - localization: { - defaultResourceName: 'MyProjectName', - }, }; From 99c5fb6a5c4e0d0ffdb254fdd39636a9141c9b2b Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 13:36:42 +0300 Subject: [PATCH 31/51] refactor(core): make localization in environment optional #3791 --- npm/ng-packs/packages/core/src/lib/models/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/models/config.ts b/npm/ng-packs/packages/core/src/lib/models/config.ts index 0f7405380b..2169f75643 100644 --- a/npm/ng-packs/packages/core/src/lib/models/config.ts +++ b/npm/ng-packs/packages/core/src/lib/models/config.ts @@ -16,7 +16,7 @@ export namespace Config { hmr?: boolean; oAuthConfig: AuthConfig; apis: Apis; - localization: { defaultResourceName: string }; + localization?: { defaultResourceName?: string }; } export interface Application { From c79944b3a8d81301c8a94a01fd21183bd8d4b458 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 13:37:17 +0300 Subject: [PATCH 32/51] feat(core): add isLocalized method to LocalizationService #3791 --- .../src/lib/services/localization.service.ts | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 0e89732e56..95ea86e251 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -1,11 +1,12 @@ import { Injectable, NgZone, Optional, SkipSelf } from '@angular/core'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Store, Actions, ofActionSuccessful } from '@ngxs/store'; +import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; import { noop, Observable } from 'rxjs'; +import { SetLanguage } from '../actions/session.actions'; +import { ApplicationConfiguration } from '../models/application-configuration'; +import { Config } from '../models/config'; import { ConfigState } from '../states/config.state'; import { registerLocale } from '../utils/initial-utils'; -import { Config } from '../models/config'; -import { SetLanguage } from '../actions/session.actions'; type ShouldReuseRoute = (future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean; @@ -75,4 +76,31 @@ export class LocalizationService { instant(key: string | Config.LocalizationWithDefault, ...interpolateParams: string[]): string { return this.store.selectSnapshot(ConfigState.getLocalization(key, ...interpolateParams)); } + + isLocalized(key, sourceName) { + if (sourceName === '_') { + //A convention to suppress the localization + return true; + } + + const localization = this.store.selectSnapshot( + ConfigState.getOne('localization'), + ) as ApplicationConfiguration.Localization; + sourceName = sourceName || localization.defaultResourceName; + if (!sourceName) { + return false; + } + + const source = localization.values[sourceName]; + if (!source) { + return false; + } + + const value = source[key]; + if (value === undefined) { + return false; + } + + return true; + } } From ff09572736f2e0fe4ec82d67c6044f89e89d1fb9 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 13:38:51 +0300 Subject: [PATCH 33/51] refactor(core): improve the getLocalization selector of config state #3791 --- .../core/src/lib/states/config.state.ts | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 546b3d0b1e..25f2a69fbe 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -155,33 +155,45 @@ export class ConfigState { } const keys = key.split('::') as string[]; - const selector = createSelector([ConfigState], (state: Config.State) => { - if (!state.localization) return defaultValue || key; - - const defaultResourceName = snq(() => state.environment.localization.defaultResourceName); - if (keys[0] === '') { - if (!defaultResourceName) { - throw new Error( - `Please check your environment. May you forget set defaultResourceName? - Here is the example: - { production: false, - localization: { - defaultResourceName: 'MyProjectName' - } - }`, - ); - } + const selector = createSelector([ConfigState], (state: Config.State): string => { + const warn = (message: string) => { + if (!state.environment.production) console.warn(message); + }; - keys[0] = defaultResourceName; + if (keys.length < 2) { + warn('The localization source separator (::) not found.'); + return key as string; } + if (!state.localization) return defaultValue || keys[1]; - let localization = (keys as any).reduce((acc, val) => { - if (acc) { - return acc[val]; - } + const sourceName = + keys[0] || + snq(() => state.environment.localization.defaultResourceName) || + state.localization.defaultResourceName; + const sourceKey = keys[1]; - return undefined; - }, state.localization.values); + if (sourceName === '_') { + return sourceKey; + } + + if (!sourceName) { + warn( + 'Localization source name is not specified and the defaultResourceName was not defined!', + ); + + return sourceKey; + } + + const source = state.localization.values[sourceName]; + if (!source) { + warn('Could not find localization source: ' + sourceName); + return sourceKey; + } + + let localization = source[sourceKey]; + if (typeof localization === 'undefined') { + return sourceKey; + } interpolateParams = interpolateParams.filter(params => params != null); if (localization && interpolateParams && interpolateParams.length) { @@ -191,7 +203,7 @@ export class ConfigState { } if (typeof localization !== 'string') localization = ''; - return localization || defaultValue || key; + return localization || defaultValue || (key as string); }); return selector; From 5368a09afb4526d9e414d75337e59b9f8957b252 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 13:47:20 +0300 Subject: [PATCH 34/51] fix: lint error --- .../packages/core/src/lib/services/localization.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 95ea86e251..8adef08f46 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -79,7 +79,7 @@ export class LocalizationService { isLocalized(key, sourceName) { if (sourceName === '_') { - //A convention to suppress the localization + // A convention to suppress the localization return true; } From d403d9424c92072ecf599eef9287e03e0e0d9c45 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 14:11:04 +0300 Subject: [PATCH 35/51] test: fix testing errors --- .../packages/core/src/lib/states/config.state.ts | 10 +++++----- .../packages/core/src/lib/tests/config.state.spec.ts | 11 ++++------- .../core/src/lib/tests/localization.service.spec.ts | 8 +------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 25f2a69fbe..3527e5445d 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -162,7 +162,7 @@ export class ConfigState { if (keys.length < 2) { warn('The localization source separator (::) not found.'); - return key as string; + return defaultValue || (key as string); } if (!state.localization) return defaultValue || keys[1]; @@ -173,7 +173,7 @@ export class ConfigState { const sourceKey = keys[1]; if (sourceName === '_') { - return sourceKey; + return defaultValue || sourceKey; } if (!sourceName) { @@ -181,18 +181,18 @@ export class ConfigState { 'Localization source name is not specified and the defaultResourceName was not defined!', ); - return sourceKey; + return defaultValue || sourceKey; } const source = state.localization.values[sourceName]; if (!source) { warn('Could not find localization source: ' + sourceName); - return sourceKey; + return defaultValue || sourceKey; } let localization = source[sourceKey]; if (typeof localization === 'undefined') { - return sourceKey; + return defaultValue || sourceKey; } interpolateParams = interpolateParams.filter(params => params != null); diff --git a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts index aa3740218c..e823328f73 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/config.state.spec.ts @@ -272,7 +272,7 @@ describe('ConfigState', () => { ); expect(ConfigState.getLocalization('AbpIdentity::NoIdentity')(CONFIG_STATE_DATA)).toBe( - 'AbpIdentity::NoIdentity', + 'NoIdentity', ); expect( @@ -287,18 +287,15 @@ describe('ConfigState', () => { )(CONFIG_STATE_DATA), ).toBe('first and second do not match.'); - try { + expect( ConfigState.getLocalization('::Test')({ ...CONFIG_STATE_DATA, environment: { ...CONFIG_STATE_DATA.environment, localization: {} as any, }, - }); - expect(false).toBeTruthy(); // fail - } catch (error) { - expect((error as Error).message).toContain('Please check your environment'); - } + }), + ).toBe('Test'); }); }); diff --git a/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts index 5600e3aa39..8c5b859f9f 100644 --- a/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts +++ b/npm/ng-packs/packages/core/src/lib/tests/localization.service.spec.ts @@ -44,13 +44,7 @@ describe('LocalizationService', () => { describe('#instant', () => { it('should be return a localization', () => { - store.selectSnapshot.andCallFake( - (selector: (state: any, ...states: any[]) => Observable) => { - return selector({ - ConfigState: { getLocalization: (keys, ...interpolateParams) => keys }, - }); - }, - ); + store.selectSnapshot.andReturn('AbpTest'); expect(service.instant('AbpTest')).toBe('AbpTest'); }); From e28db049c9b232cfa5ccbab87a267e719896006d Mon Sep 17 00:00:00 2001 From: ChangYinShung Date: Thu, 14 May 2020 00:04:15 +0800 Subject: [PATCH 36/51] add Blog Module's Localize for zh-hant 1.add ShareOn , TitleLengthWarning in zh-hant --- .../Volo/Blogging/Localization/Resources/zh-Hant.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/zh-Hant.json b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/zh-Hant.json index fcafc171de..b5781d305e 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/zh-Hant.json +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/zh-Hant.json @@ -42,6 +42,8 @@ "CreationTime": "建立時間", "Description": "描述", "Blogs": "部落格", - "Tags": "標籤" + "Tags": "標籤", + "ShareOn": "分享在", + "TitleLengthWarning": "為了優化搜索引擎,標題建議保持在60個字元以內" } } \ No newline at end of file From 0f254f11b04d579f4afd888b5ceb69e79a3c5647 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 14 May 2020 00:17:13 +0800 Subject: [PATCH 37/51] Translate signalr integration document --- docs/en/Samples/Index.md | 2 +- docs/en/SignalR-Integration.md | 2 +- docs/zh-Hans/Samples/Index.md | 3 + docs/zh-Hans/SignalR-Integration.md | 227 ++++++++++++++++++++++ docs/zh-Hans/images/signal-js-file.png | Bin 0 -> 57358 bytes docs/zh-Hans/images/signalr-demo-chat.png | Bin 0 -> 46010 bytes 6 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 docs/zh-Hans/SignalR-Integration.md create mode 100644 docs/zh-Hans/images/signal-js-file.png create mode 100644 docs/zh-Hans/images/signalr-demo-chat.png diff --git a/docs/en/Samples/Index.md b/docs/en/Samples/Index.md index 12095bb2c9..b5fae6cbce 100644 --- a/docs/en/Samples/Index.md +++ b/docs/en/Samples/Index.md @@ -37,7 +37,7 @@ While there is no Razor Pages & MongoDB combination, you can check both document * [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) - * [Signal Integration document](../SignalR-Integration.md) + * [SignalR Integration document](../SignalR-Integration.md) * **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) diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index 415b585f3f..0a2ebdaa54 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -25,7 +25,7 @@ If you want to manually install; 1. Add the [Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR) NuGet package to your project: ``` - Install-Package Volo.Abp.BackgroundJobs.HangFire + Install-Package Volo.Abp.AspNetCore.SignalR ``` Or use the Visual Studio NuGet package management UI to install it. diff --git a/docs/zh-Hans/Samples/Index.md b/docs/zh-Hans/Samples/Index.md index 774d6467ef..97569f2ae2 100644 --- a/docs/zh-Hans/Samples/Index.md +++ b/docs/zh-Hans/Samples/Index.md @@ -35,6 +35,9 @@ * **Entity Framework 迁移**: 演示如何将应用程序拆分为多个数据库的解决方案. 每个数据库包含不同的模块. * [源码](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo) * [EF Core数据库迁移文档](../Entity-Framework-Core-Migrations.md) +* **SignalR Demo**: A simple chat application that allows to send and receive messages among authenticated users. + * [源码](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) + * [SignalR 集成文档](../SignalR-Integration.md) * **Dashboard Demo**: 一个简单的应用程序,展示了如何在ASP.NET Core MVC UI中使用widget系统. * [源码](https://github.com/abpframework/abp-samples/tree/master/DashboardDemo) * [Widget 文档](../UI/AspNetCore/Widgets.md) diff --git a/docs/zh-Hans/SignalR-Integration.md b/docs/zh-Hans/SignalR-Integration.md new file mode 100644 index 0000000000..8045bbd394 --- /dev/null +++ b/docs/zh-Hans/SignalR-Integration.md @@ -0,0 +1,227 @@ +# SignalR 集成 + +> 你可以按照[标准的微软教程](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signal)添加[SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction)到你的应用程序,但ABP提供了简化集成的SignalR集成包. + +## 安装 + +### 服务器端 + +建议使用[ABP CLI](CLI.md)安装包. + +#### 使用 ABP CLI + +在项目的文件夹(.csproj文件)中打开命令行窗口,然后输入以下命令: + +```bash +abp add-package Volo.Abp.AspNetCore.SignalR +``` + +> 你通常需要将此软件包添加到应用程序的Web或API层,具体取决于你的架构. + +#### 手动安装 + +如果你想手动安装: + +1. 添加[Volo.Abp.AspNetCore.SignalR](https://www.nuget.org/packages/Volo.Abp.AspNetCore.SignalR)NuGet包到你的项目: + + ``` + Install-Package Volo.Abp.AspNetCore.SignalR + ``` + + 或者使用VisualStudio提供的UI安装 + +2. 添加 `AbpAspNetCoreSignalRModule` 到你的模块的依赖列表. + +```csharp +[DependsOn( + //...other dependencies + typeof(AbpAspNetCoreSignalRModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +``` + +> 你不需要 `services.AddSignalR()` 和 `app.UseEndpoints(...)`,它们在 `AbpAspNetCoreSignalRModule` 中已经添加了. + +### 客户端 + +客户端安装取决于你的UI框架/客户端类型. + +#### ASP.NET Core MVC / Razor Pages UI + +在你的Web项目的根文件夹中运行以下命令: + +````bash +yarn add @abp/signalr +```` + +> 需要 [yarn](https://yarnpkg.com/) 环境. + +它会添加 `@abp/signalr` 到你的项目中的 `package.json` 依赖项: + +````json +{ + ... + "dependencies": { + ... + "@abp/signalr": "~2.7.0" + } +} +```` + +在你的Web项目的根文件夹中运行 `gulp`: + +````bash +gulp +```` + +它会将SignalR JavaScript文件拷贝到你的项目: + +![signal-js-file](images/signal-js-file.png) + +最后将以下代码添加到页面/视图中, 添加包含 `signalr.js` 文件: + +````xml +@section scripts { + +} +```` + +它需要将 `@using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR` 添加到你的页面/视图. + +> 你可以用标准方式添加 `signalr.js` 文件. 但是使用 `SignalRBrowserScriptContributor` 具有其他好处. 有关详细信息,请参见[客户端程序包管理](UI/AspNetCore/Client-Side-Package-Management.md)和[捆绑和压缩文档](UI/AspNetCore/Bundling-Minification.md). + +这就是全部了,你可以在你的页面使用[SignalR JavaScript API](https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client). + +#### 其他的UI框架/客户端 + +其他类型的客户端请参考[微软文档](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction). + +## ABP框架集成 + +本节介绍了使用ABP框架集成包的其他好处. + +### Hub 路由与Mapping + +ABP自动将所有集线器注册到[依赖注入](Dependency-Injection.md)(做为transient)并映射集线器端点. 因此你不需要使用 `app.UseEndpoints(...)` 即可映射你的集线器.集线器路由(URL)通常是根据你的集线器名称确定. + +示例: + +````csharp +public class MessagingHub : Hub +{ + //... +} +```` + +`MessasingHub` 集线器的路由为 `/signalr-hubs/messasing`: + +* 添加了标准 `/signalr-hubs/` 前缀. +* 使用**驼峰命名**集线器名称,不包含 `Hub` 后缀. + +如果你想指定路由,你可以使用 `HubRoute` attribute: + +````csharp +[HubRoute("/my-messasing-hub")] +public class MessagingHub : Hub +{ + //... +} +```` + +### AbpHub 基类 + +你可以从 `AbpHub` 或 `AbpHub` 继承标准的 `Hub` 和 `Hub` 类,它们具有实用的基本属性,如 `CurrentUser`. + +示例: + +````csharp +public class MessagingHub : AbpHub +{ + public async Task SendMessage(string targetUserName, string message) + { + var currentUserName = CurrentUser.UserName; //Access to the current user info + var txt = L["MyText"]; //Localization + } +} +```` + +> 虽然可以将相同的属性注入到集线器构造函数中,但是这种方式简化了集线器类. + +### 手动注册/Mapping + +ABP会自动将所有集线器注册到[依赖注入](Dependency-Injection.md)作为**transient service**. 如果想要禁用集线器类**自动添加依赖注入**,只需要使用 `DisableConventionalRegistration` attribute. 如果愿意,你仍然可以在模块的 `ConfigureServices` 方法中注册集线器类: + +````csharp +context.Services.AddTransient(); +```` + +当**你或ABP**将类注册到依赖注入时,如前几节所述,它会自动映射到端点路由配置. 如果要手动映射集线器类,你可以使用 `DisableAutoHubMap` attribute. + +对于手动映射,你有两个选择: + +1. 使用 `AbpSignalROptions` 添加map配置(在[模块](Module-Development-Basics.md)的 `ConfigureServices` 方法中),ABP会为集线器执行端点映射: + +````csharp +Configure(options => +{ + options.Hubs.Add( + new HubConfig( + typeof(MessagingHub), //Hub type + "/my-messaging/route", //Hub route (URL) + hubOptions => + { + //Additional options + hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + } + ) + ); +}); +```` + +这是提供其他SignalR选项的好方式. + +如果你不想禁用自动集线器map,但仍想执行其他SignalR配置,可以使用 `options.Hubs.AddOrUpdate(...)` 方法: + +````csharp +Configure(options => +{ + options.Hubs.AddOrUpdate( + typeof(MessagingHub), //Hub type + config => //Additional configuration + { + config.RoutePattern = "/my-messaging-hub"; //override the default route + config.ConfigureActions.Add(hubOptions => + { + //Additional options + hubOptions.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + }); + } + ); +}); +```` + +你可以通过这种方式修改在依赖模块(没有源代码访问权限)中定义的集线器类的选项. + +2. 在[模块](Module-Development-Basics.md)的 `OnApplicationInitialization` 方法中更改 `app.UseConfiguredEndpoints`(添加了lambda方法作为参数). + +````csharp +app.UseConfiguredEndpoints(endpoints => +{ + endpoints.MapHub("/my-messaging-hub", options => + { + options.LongPolling.PollTimeout = TimeSpan.FromSeconds(30); + }); +}); +```` + +### UserIdProvider + +ABP实现 `SignalR` 的 `IUserIdProvider` 接口,从ABP框架的 `ICurrentUser` 服务提供当前用户ID(请参阅[当前用户服务](CurrentUser.md)),它将集成到应用程序的身份验证系统中,实现类是 `AbpSignalRUserIdProvider` (如果你想更改/覆盖它). + +## 示例应用程序 + +参阅 [SignalR集成Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo),它有一个简单的聊天页面,可以在(经过身份验证的)用户之间发送消息. + +![signalr-demo-chat](images/signalr-demo-chat.png) \ No newline at end of file diff --git a/docs/zh-Hans/images/signal-js-file.png b/docs/zh-Hans/images/signal-js-file.png new file mode 100644 index 0000000000000000000000000000000000000000..19fc6cefe3480204c2f0a31131cb50f1b2690d1d GIT binary patch literal 57358 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 LNkvXXu0mjf{{oRT literal 0 HcmV?d00001 diff --git a/docs/zh-Hans/images/signalr-demo-chat.png b/docs/zh-Hans/images/signalr-demo-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..120d7eda6ed54fd4e6a90ef31cfd93f4fc9e4b43 GIT binary patch literal 46010 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 C2)D3+kz51_-t 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 G`+ot40&F<| literal 0 HcmV?d00001 From 8d2dc2fa65f36758174f9b58cf84d8578ef945bf Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 19:43:13 +0300 Subject: [PATCH 38/51] fix: set html lang when the language change --- .../packages/core/src/lib/services/localization.service.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index 0e89732e56..a3b452de11 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -33,9 +33,10 @@ export class LocalizationService { } private listenToSetLanguage() { - this.actions - .pipe(ofActionSuccessful(SetLanguage)) - .subscribe(({ payload }) => this.registerLocale(payload)); + this.actions.pipe(ofActionSuccessful(SetLanguage)).subscribe(({ payload }) => { + this.registerLocale(payload); + document.documentElement.lang = payload; + }); } setRouteReuse(reuse: ShouldReuseRoute) { From d22ba8a91967836538cac8436503197286841ce7 Mon Sep 17 00:00:00 2001 From: mehmet-erim Date: Wed, 13 May 2020 20:44:16 +0300 Subject: [PATCH 39/51] fix(core): set language when call app config api --- .../packages/core/src/lib/actions/session.actions.ts | 2 +- .../packages/core/src/lib/services/localization.service.ts | 7 +++---- npm/ng-packs/packages/core/src/lib/states/config.state.ts | 6 +++++- npm/ng-packs/packages/core/src/lib/states/session.state.ts | 7 +++++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts b/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts index 463e9a202d..a7d2cd9f3c 100644 --- a/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts +++ b/npm/ng-packs/packages/core/src/lib/actions/session.actions.ts @@ -2,7 +2,7 @@ import { ABP } from '../models'; export class SetLanguage { static readonly type = '[Session] Set Language'; - constructor(public payload: string) {} + constructor(public payload: string, public dispatchAppConfiguration?: boolean) {} } export class SetTenant { static readonly type = '[Session] Set Tenant'; diff --git a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts index a3b452de11..0e89732e56 100644 --- a/npm/ng-packs/packages/core/src/lib/services/localization.service.ts +++ b/npm/ng-packs/packages/core/src/lib/services/localization.service.ts @@ -33,10 +33,9 @@ export class LocalizationService { } private listenToSetLanguage() { - this.actions.pipe(ofActionSuccessful(SetLanguage)).subscribe(({ payload }) => { - this.registerLocale(payload); - document.documentElement.lang = payload; - }); + this.actions + .pipe(ofActionSuccessful(SetLanguage)) + .subscribe(({ payload }) => this.registerLocale(payload)); } setRouteReuse(reuse: ShouldReuseRoute) { diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 546b3d0b1e..29535be0ab 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -219,9 +219,13 @@ export class ConfigState { defaultLang = defaultLang.split(';')[0]; } + document.documentElement.setAttribute( + 'lang', + configuration.localization.currentCulture.cultureName, + ); return this.store.selectSnapshot(SessionState.getLanguage) ? of(null) - : dispatch(new SetLanguage(defaultLang)); + : dispatch(new SetLanguage(defaultLang, false)); }), catchError(err => { dispatch(new RestOccurError(new HttpErrorResponse({ status: 0, error: err }))); diff --git a/npm/ng-packs/packages/core/src/lib/states/session.state.ts b/npm/ng-packs/packages/core/src/lib/states/session.state.ts index 6acbf1d60d..0c81a46c58 100644 --- a/npm/ng-packs/packages/core/src/lib/states/session.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/session.state.ts @@ -69,12 +69,15 @@ export class SessionState { } @Action(SetLanguage) - setLanguage({ patchState, dispatch }: StateContext, { payload }: SetLanguage) { + setLanguage( + { patchState, dispatch }: StateContext, + { payload, dispatchAppConfiguration = true }: SetLanguage, + ) { patchState({ language: payload, }); - return dispatch(new GetAppConfiguration()); + if (dispatchAppConfiguration) return dispatch(new GetAppConfiguration()); } @Action(SetTenant) From d47c7e686ab1ec8d3a5d4fa6f519ac407b146d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 13 May 2020 23:40:32 +0300 Subject: [PATCH 40/51] Revise text template document --- docs/en/Text-Templating.md | 221 +++++++++++++++++++++++++----- docs/en/images/hello-template.png | Bin 0 -> 64613 bytes 2 files changed, 185 insertions(+), 36 deletions(-) create mode 100644 docs/en/images/hello-template.png diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 5451b1f45e..e421f6a8c6 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -1,22 +1,44 @@ # Text Templating -In ABP Framework, `text template` is a mixture of text blocks and control logic that can generate a `string` result. An open source package [Scriban](https://github.com/lunet-io/scriban) is used for the control logic and [Abp.Localization](Localization.md) is used to make content easily localizable. The generated string can be text of any kind, such as a web page, an e-mail content etc. +## Introduction -> **stored content** -```html -

    -{{~ for $i in 0..3 ~}} -
  1. {{ L "WelcomeMessage" }}
  2. -{{~ endfor ~}} -
-``` -> **result** (for en culture) -``` -1. Welcome to the abp.io! -2. Welcome to the abp.io! -3. Welcome to the abp.io! -4. Welcome to the abp.io! -``` +ABP Framework provides a simple, yet efficient text template system. Text templating is used to dynamically render contents based on a template and a model (a data object): + +TEMPLATE + MODEL => RENDERED CONTENT + +It is very similar to an ASP.NET Core Razor View (or Page): + +RAZOR VIEW (or PAGE) + MODEL => HTML CONTENT + +### Example + +Here, a simple template: + +```` +Hello {{model.name}} :) +```` + +You can define a class with a `Name` property to render this template: + +````csharp +public class HelloModel +{ + public string Name { get; set; } +} +```` + +If you render the template with a `HelloModel` with the `Name` is `John`, the rendered output is will be: + +```` +Hello John :) +```` + +Template rendering engine is very powerful; + +* It is based on the [Scriban](https://github.com/lunet-io/scriban) library, so it supports **conditional logics**, **loops** and much more. +* Template content **can be localized**. +* You can define **layout templates** to be used as the layout while rendering other templates. +* You can pass arbitrary objects to the template context (beside the model) for advanced scenarios. ## Installation @@ -52,21 +74,151 @@ public class YourModule : AbpModule } ```` -## Logic +## Defining Templates + +Before rendering a template, you should define it. Create a class inheriting from the `TemplateDefinitionProvider` base class: + +````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` object is used to add new templates or get the templates defined by depended modules. Used `context.Add(...)` to define a new template. +* `TemplateDefinition` is the class represents a template. Each template must have a unique name (that will be used while you are rendering the template). +* `/Demos/Hello/Hello.tpl` is the path of the template file. +* `isInlineLocalized` is used to declare if you are using a single template for all languages (`true`) or different templates for each language (`false`). See the Localization section below for more. + +### The Template Content + +`WithVirtualFilePath` indicates that we are using the [Virtual File System](Virtual-File-System.md) to store the template content. Create a `Hello.tpl` file inside your project and mark it as "**embedded resource**" on the properties window: + +![hello-template](images/hello-template.png) + +Example `Hello.tpl` content is shown below: + +```` +Hello {{model.name}} :) +```` + +The [Virtual File System](Virtual-File-System.md) requires to add your files in the `ConfigureServices` method of your [module](Module-Development-Basics.md) class: + +````csharp +Configure(options => +{ + options.FileSets.AddEmbedded("TextTemplateDemo"); +}); +```` + +* `TextTemplateDemoModule` is the module class that you define your template in. +* `TextTemplateDemo` is the root namespace of your project. + +## Rendering the Template + +`ITemplateRenderer` service is used to render a template content. Example: + +````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` is a simple class that injects the `ITemplateRenderer` in its constructor and uses it inside the `RunAsync` method. +* `RenderAsync` gets two fundamental parameters: + * `templateName`: The name of the template to be rendered (`Hello` in this example). + * `model`: An object that is used as the `model` inside the template (a `HelloModel` object in this example). + +The result shown below for this example: + +````csharp +Hello John :) +```` + +### Anonymous Model + +While it is suggested to create model classes for the templates, it would be practical (and possible) to use an anonymous object for simple cases: + +````csharp +var result = await _templateRenderer.RenderAsync( + "Hello", //the template name + new + { + Name = "John" + } +); +```` + +In this case, we haven't created a model class, but created an anonymous object as the model. + +### PascalCase vs CamelCase + +PascalCase property names (like `UserName`) is used as camelCase (like `userName`) in the template as a convention. + + -A Text Template is a combination of two object. -- `TemplateDefinition` -- `TemplateContent` + + + + + + + + + + + + + + + + + + + + + +## Logic + +A Text Template is a combination of two parts: template definition and template content. ### Template Definition -Template Definition is an object that contains some information about your `Text Templates`. Template Definition object contains the following properties. +Template Definition is an object that contains some information about your text templates. Template Definition object contains the following properties. -- `Name` *(string)* -- `IsLayout` *(boolean)* +- `Name` *(string)*: Unique name of the template. It is then used to render the template. +- `IsLayout` *(boolean)*: - `Layout` *(string)* contains the name of layout template -- `LocalizationResource` *(Type)* Inline Localized -- `IsInlineLocalized`*(boolean)* describes that the template is inline localized or not +- `LocalizationResource` *(Type)* The localization resource type that is used if this template is inline localized. +- `IsInlineLocalized` *(boolean)* describes that the template is inline localized or not - `DefaultCultureName` *(string)* defines the default culture for the template ### Template Content @@ -83,23 +235,20 @@ This is a simple content for your templates. For default, template contents stor - ``` ### Localization -You can localize your Text Templates by choosing two different method. +You can localize your Text Templates by choosing two different methods. - `Inline Localization` - `Multiple Content Localization` #### Inline Localization -Inline localized Text Templates is using only one content resource, and it is using the [Abp.Localization](Localization.md) to get content in different languages/cultures. +An inline localized text template is using only one content resource, and it is using the [Abp.Localization](Localization.md) to get content in different languages/cultures. -> Example Inline Localized Text Template: -> -> ForgotPasswordEmail.tpl +Example Inline Localized Text Template content: ```html

{{L "PasswordReset"}}

@@ -143,13 +292,13 @@ You can store your Text Templates for any culture in different content resource. ### Layout System -It is typical to use the same layout for some different Text Templates. So, you can define a layout template. +It is typical to use the same layout for some different text templates. So, you can define a layout template. A text template can be layout for different text templates and also a text template may use a layout. A layout Text Template must have `{{content}}` area to render the child content. _(just like the `RenderBody()` in the MVC)_ -> Example Email Layout Text Template +Example Email Layout Text Template ```html @@ -163,7 +312,7 @@ A layout Text Template must have `{{content}}` area to render the child content. ``` -## Definition a Text Template +## Definition of a Text Template First of all, create a class that inherited from `TemplateDefinitionProvider` abstract class and create `Define` method that derived from the base class. @@ -228,7 +377,7 @@ As you see in the given example, all Text Templates are added with `(ITemplateDe When one template is registered, it is easy to render and get the result with `ITemplateRenderer` service. -`ITemplateRenderer` service has one method that named `RenderAsync` and to render your content and it is requires some parametres. +`ITemplateRenderer` service has one method that named `RenderAsync` and to render your content and it is requires some parameters. - `templateName` (_string_) - `model` (_object_) @@ -283,7 +432,7 @@ It has three method that you can get your Template Definitions. `Get` and `GetOrNull` requires a string parameter that name of template definition. `Get` will throw error when it is not exist but `GetOrNull` returns `null`. `GetAll` returns you all registered template definitions. - + ## Template Content Contributor You can store your `Template Contents` in any resource. To make it, just create a class that implements `ITemplateContentContributor` interface. diff --git a/docs/en/images/hello-template.png b/docs/en/images/hello-template.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3ce2816a8a26253d78c150a5537b992d86d927 GIT binary patch literal 64613 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

J{oCDaPtz0 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 literal 0 HcmV?d00001 From d2c199da628eabd31053d502e7649f72b5ced68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 14 May 2020 00:41:29 +0300 Subject: [PATCH 41/51] Revise the text templating --- docs/en/Text-Templating.md | 116 +++++++++++++++++++--- docs/en/images/multiple-file-template.png | Bin 0 -> 35207 bytes 2 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 docs/en/images/multiple-file-template.png diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index e421f6a8c6..62ecaa6bd5 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -125,7 +125,9 @@ Configure(options => ## Rendering the Template -`ITemplateRenderer` service is used to render a template content. Example: +`ITemplateRenderer` service is used to render a template content. + +### Example: Rendering a Simple Template ````csharp public class HelloDemo : ITransientDependency @@ -165,11 +167,11 @@ Hello John :) ### Anonymous Model -While it is suggested to create model classes for the templates, it would be practical (and possible) to use an anonymous object for simple cases: +While it is suggested to create model classes for the templates, it would be practical (and possible) to use anonymous objects for simple cases: ````csharp var result = await _templateRenderer.RenderAsync( - "Hello", //the template name + "Hello", new { Name = "John" @@ -179,25 +181,115 @@ var result = await _templateRenderer.RenderAsync( In this case, we haven't created a model class, but created an anonymous object as the model. -### PascalCase vs CamelCase +### PascalCase vs camelCase + +PascalCase property names (like `UserName`) is used as camelCase (like `userName`) in the templates. + +## Localization + +It is possible to localize a template content based on the current culture. There are two types of localization options described in the following sections. + +### Inline localization + +Inline localization uses the [localization system](Localization.md) to localize texts inside templates. + +#### Example: Reset Password Link + +Assuming you need to send an email to a user to reset her/his password. Here, the template content: + +```` +{{L "ResetMyPassword"}} +```` + +`L` function is used to localize the given key based on the current user culture. You need to define the `ResetMyPassword` key inside your localization file: + +````json +"ResetMyPassword": "Click here to reset your password" +```` + +You also need to declare the localization resource to be used with this template, inside your template definition provider class: + +````csharp +context.Add( + new TemplateDefinition( + "PasswordReset", //Template name + typeof(DemoResource) //LOCALIZATION RESOURCE + ).WithVirtualFilePath( + "/Demos/PasswordReset/PasswordReset.tpl", //template content path + isInlineLocalized: true + ) +); +```` + +That's all. When you render this template like that: + +````csharp +var result = await _templateRenderer.RenderAsync( + "PasswordReset", //the template name + new PasswordResetModel + { + Link = "https://abp.io/example-link?userId=123&token=ABC" + } +); +```` + +You will see the localized result: -PascalCase property names (like `UserName`) is used as camelCase (like `userName`) in the template as a convention. +````csharp +Click here to reset your password +```` +> If you define the [default localization resource](Localization.md) for your application, then no need to declare the resource type for the template definition. +### Multiple Contents Localization +Instead of a single template that uses the localization system to localize the template, you may want to create different template files for each language. It can be needed if the template should be completely different for a specific culture rather than simple text localizations. +#### Example: Welcome Email +Assuming that you want to send a welcome email to your users, but want to define a completely different template based on the user culture. +First, create a folder and put your templates inside it, like `en.tpl`, `tr.tpl`... one for each culture you support: +![multiple-file-template](D:\Github\abp\docs\en\images\multiple-file-template.png) +Then add your template definition in the template definition provider class: +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en" + ) + .WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", //template content folder + isInlineLocalized: false + ) +); +```` +* Set **default culture name**, so it fallbacks to the default culture if there is no template for the desired culture. +* Specify **the template folder** rather than a single template file. +* Set `isInlineLocalized` to `false` for this case. +That's all, you can render the template for the current culture: +````csharp +var result = await _templateRenderer.RenderAsync("WelcomeEmail"); +```` +> Skipped the modal for this example to keep it simple, but you can use models as just explained before. +### Specify the Culture +`ITemplateRenderer` service uses the current culture (`CultureInfo.CurrentUICulture`) if not specified. If you need, you can specify the culture as the `cultureName` parameter: +````csharp +var result = await _templateRenderer.RenderAsync( + "WelcomeEmail", + cultureName: "en" +); +```` @@ -251,13 +343,7 @@ An inline localized text template is using only one content resource, and it is Example Inline Localized Text Template content: ```html -

{{L "PasswordReset"}}

- -

{{L "PasswordResetInfoInEmail"}}

- - +{{L "ResetMyPassword"}} ``` #### Multiple Content Localization @@ -396,6 +482,12 @@ When one template is registered, it is easy to render and get the result with `I `globalContext` = TODO + + + + + + ## Template Content Provider When you want to get your stored template content you can use `ITemplateContentProvider`. diff --git a/docs/en/images/multiple-file-template.png b/docs/en/images/multiple-file-template.png new file mode 100644 index 0000000000000000000000000000000000000000..a2e7327301a0bb1edb9c5fd6202650556d996004 GIT binary patch literal 35207 zcmZ^Kb8uu$*zbw6**MwQ#>TcbwzaWsbHm-(ww;Y_XJgy8jXUpG_p7@9+^Ie_RWo&- z?&;J0^z*~~QIHcyfWv_U002Q!;)fCdfa!uR0$6a+C$cCf#Gtn?_7a*-0Pq##gB>vDU(~FL0RUb;B|^jbrFk>~Ia2-tB5ii3uOgZnzws{C87c-DNKW*H@a+MGSzYl939F3c~hiqM@q zlK>UHZ-01=-Z5){;c5!il6K~B!!?#xC&G7e+&fs2dFY;w@8PU+7NX4A1F7#l5dg|z;W zzO)L4DhqbB^^I`yJA0==+%Q_4d!#xhd2U!5$D3LaobYno>x=BMPu8F60&Yz4*54Vb z)hvfM1o$H@PQN!$;5z=p^}I}J;wSl?N+*0)=!~%%l>S8&yQreAr6xC*n2xS8J&~K6 z+xa~G?|mi{jZ$wR1Yn?2oE_bvR(4U(7|-z(j3A%x#_)XOUY?s?3D{pWIqt3uc41Ts zC~TbjTqtUPw!C{B%9CTYi^<{=bvRjB-IqSy?uSkI2_Igq^-+l4@3Yu7ZNbx7Bu^G$ z@C9i9nn3h(T^By+Q5O)HDe>uUW%P<3Mj?Io;OJs3V~L;qNnPUC?b#Mc#mT**mTJH4 z8Gn5d`PNO`_m)ZyGjXu-BsSx>2FrSBFXbV>A4AA#{7`$?KC8^)#5Ud_6krsmJ{+!0{$m%z zJHwYCbqi9A6q_}|0{qNC&X;J-$LTcW3L3V1OUsjarB<^Pp6b*0Rz;_~y!&dIFT3^c z>OSx3kLuR)F#-{3U_W0b)7g9fK&-UxG@4@81(-&|TheuKQ|9#mY%Ib2%DK|SmH&bdOVD@N`MH31N-t%Am?d%` z4^Pi9X30Zof`G{2eYHP%$vd7SaMM{v<-RwjXkb_rEX&UCljUWK$7%rv445y_L#X@I zS@e~0K({_LtKwl^VoyewMn2n|E_7fTH#D7s2h1E*3GFWUJ&vapG6Zq19U>j$G`uYx zxRf_iYz$Sg`$DK!YhN(z0fN$zG|nrxEO{bJ>-G>cfIr!+1N;$?t;4+1t5A68eKBq1YrHx6*K<3=D~ zjF8PkJthP@mGXD@>b^(~_zUM{Sr+=Dk>z?&BEcOJ^J6^M?z4WX&hd*Lw+nK36u3Vx z!^iM7Jbom<^~B6bpf?%fOR!y)LW0f)esUfg>K6A_gT-S-S>(!CreD_U{rR8io@+ny zMF@a1i1$$?;afDKg5Sv@#IhwH!1-eW&rUsFg&~8p8wogJi9Y97#^ts=n?g3@t~bTw z%TiES{|GzKLc+P96O=~*1dTo>M6M$r&s3R@vLN^(zYIBP-vK%Qq&e@F|5V^DY%y&5 zo$6xB?=X4vnlor40x@i*n{N(afJW!EbT;;-4XI;F=aFjlm=UF1A`SP($;qihn~6*E zsE+C9UqegEdlFkk`DkQx{A$#(=nDv-HL17Dk4p0xl>JkIOL)NK)x4J`nyUA9iW@(bKPHBpWalS zIJ-6CMA8rFE9vlf)h=7iojC+n{9v!s)x}YaMRR*|RzBY6eyA1VpJ-_yf8@sH;6W#g zlx&KJ^A>I)J**q-8pCc=*dv^%cY@;nXUtsqQ&Lt|Qrz~4M2cAl3qqJo`Hh_PReRjU z2(kT<>B66A_*An{Xx20e>1wa$2%qDN4#cL`CYJ^}? zcn&W9jr(evXvN}}e&5y;zncA+3`E}b8Vg?5L``I-jz0B6beGTD{zE=-_kzVxX z9;hOrpimR@r1N@Am(^pqw4#srBWZacTJdx?PlN;7h<-5}#Xw*<_Cq*+XOf|)EUlC0ETPKe` zD|LD*xxS&HX^Y|BWd)bnDtaAj;j8amoK1lLIQrJ!xa5C{-r`d3iXXoUGt^z80K8|C629YjXdTAAgrh zz?YSsqGs6gsW)0LU0*~zl5KeiEzU}tbzsCSit?vw$%T9R@lCv35qi@`xh3RT-{6?v zb0xwp1jA7OVE-PcsKD*T$#a%4y3yBOvGEkn=U_MWFe`>|vne=nWlt&{@jMzkn8;gu&m{zBg=KC7b zA$>g#L$&2BFumK(+a58ZaPj(0>(`CXetlJYP$hh|Ty;p5p^)ag`9LaujY!8_-^}H~ z{}Er+&{`o=%2JkoB4u2=au79%J8QKv`e$eCJl(C(bj0}BOyG5!!FFnH*@$;_jD%jX z?Q&>6*_e5GUSyMy>O{cNJBR0e0>T{kU)kP@oT=~aTmGglE4|m`q1Ud7XmD_-Z@5b~ z#u*zsoxLMv;Up(;zyc<$`uY9Q+J*7{QCMT z9#pp~^(U>_^2IE#b!*g#*=)tg{ zv$mj0(PobJpGoaZ8by$u!0tfuI-HxI50yMc{QLX3sqEEJ2~YWnJ5!nE{CC{k6cM8z zU01H||3>T{bz(=1hng;^zClIeqxD$QE@*jQDdfpe@U*>F7nt@BflUzbWu|81JMCP) zK}T=*4FK@Te`rnH)Gt09{td9Een0JX(|)kb9HdV=ikh~xWFG6n=|`xz z%H^}~qzULj^W2XQF+KDR1;a?J(ihs9cW<7WMy1xgIhXqGh1*H|bM@H{Vo4n(NLKIOdGmI)>-jbBJ0;!wWnZ$enVj1;qsN5^dqs-2_sQ${>%@q$ zDCUk)wBDGdf{NM#JGSh~No`!*D0gKnQA66wjU{e$RrWFuA@`3$K{Qv=`)~MCjG0Th zN+156aj8{fV76mDzv7s9Lkc9djruR06cqIITK#S*arvLwlew(d2a%ElIFw+4EvDKo zDh{5es=^K|U3>O%{au^O{?@L(wO)%kqRG+rgx>V1+nEAms1y2o3~ETdVwoags zpn{`mjgh_@|DxGRAT+Ty+K&ze05OrH?QroT%@CM5)X$fF!REs=ZX0Qv(GK>n9|0Hl zs@7yeK5H(2l2!{~K}w zAEbqaiHWAu{)dVmzXV7xbLi+MmL`w7RBe2yWfH3N9-huu@;Pj`Muz>R9vdlZ z-%cUN z!S6QfTOB7nNmSx7!Ht{mB>q#1YIU86BC-5x$|&7OROK?MT2<1=45|Ne)-R39%cc48 z(_No?3yU$Tvzq!9^d8qypmF%~tKSqa;)dSzNh|qV`X^ z=l9t;aFDfY5|@lNtY(-hZ5V!H5ZcIa#9(#X6zdevZtM3Pj_5O4goG6#I+o2IqvZC> z4F|V|9_$*|9sdvsFAL<6tz>h|u=(*F+S#w-jQ|UQJHWijP(rD4(FmMk!Ie5a9o@K% zG`mk2eq1Z^cu*HImmOJ{(u+RH$w?t&;P8ukl$?gN{M`sHTaGF2 z%9?>%FnKT)pc9=j^{%R2=Xz{;>*`l1xAJ+h%#Hr~S`4r? z_k1T#Yo&QAb=Rt|mYG@fhAvjth4_^VL0^JM5W5ky1BXm3zuk{NDs;U<3q4t0=t>@RzL{QEn=M(?Zc5AT+nB|s_0x$0hKwfT1Sm&F40?|9$?B?F>DCCNVk{k7 zoFSr`m9{nLtgZ(rC4sa(T1brYrrNz zCK%wdD^!Dob8`|grQG$Np;X3>7Mwc3sz)eJ-*$>mM=apQG&IP=JbXQfN%Bb~?&+au zt9CN@TdUXkJcM<}TjsM(rur6iB%FWR zo98qBCC}PVtZds8%roLvhbqpf?TLf3_Q4lK$2A&S+UaTUz>W(}a9u6cBF5GzEOW2d zN7Ig6$<^y;gTLgBuCi>k^s8&OY&l~1X+9W^J78Nyo2g=Pg2-eeGrM#$1jVgv#S-|@ zBpM~|NeJRJ`b)D@562Wg=@%y_)v3@omcEr%R$AGd_dA6%hw^rPq)s0`4Ctt(a=ufH zJKM=^wtPmQUL~w&!&JTL&&Lg3?uH4XKaFJ&mwK_|OI8wNaachcxm=|7R}9>$O{jdt zErvzv+-1NJwlE8ZQ6eczyT2OJl4aDZqh3n}wvmDf-x}-DOWE`w5 z&{2jH&f!^vmUKFL9tvffCEJmFJkb}TE;ZW5X07#wM)M$MzTPpOiV%9I=n_wEcU1qL$yx6jvwPXEG!NVU+2l*!fmi802EEF4#}6y zK={i}6?p7s+_>5;d6x|XfSBr9bh^%lx54;Vx-N=gux_s<vipR?1= zR~tXqWvhs;$%Spi>3E787B@e8&QH133W!P%uEF9V&zGgW;(52pzw^bH?<5ZMX8-gQ zt^bTOIrx}O{+ZZ#z0XdjAyjKdN#nF%t~FEPnfz67c^dvgPO#!j-In#Ym2L{`Px+4D zyVlE!cDbIydm0f;xi7;c0`L85QqY^2lKpMmcA**4Il@EA%fjJNHjm?8w-W-EVfBZ{ zrUmE$SH1>pYCLrnHg=KkHE?oz&9aG_#XI7ltD0z)D=G?A9w(~KH$ZnTsg73tzHBuL z*VJ5RRFFiK*m)2@1tfyZ2foR+jxviV(JGcxrq35aBU;iKE*nun1NUihGYhmPCi@_k zR#S8Nf0|}$YN}%k-VhzHC)spbw(gky-Q7)$RN+KL(wR-V>pp8b&^?yVW^XrZq**=HLUh%gDOR${M zA0gUp(uGQ<<~YgYEs?b|J`MqOJHyGI7W`BvKyV4C|v|%+%wNS z9TnsC6#Pn6PA+zGhIyU1@-f7b(S;KBTe$XRAwh6{I<+AfyEcuYy+bx_XYFiMERfUK z8J)-L^iTU%OlMZq+d=&jgKPF8hzZrFTF^?()+sYDqaLTOYmr89o?6KY zVxdh%r#s!E)(S`WqO|2?>)m0INV1g#sz3P|k8_2#^vtHZrT-Lq0a+3n;s<}HQt8|u zGYRf=mFW@+$vpZ;B`JKq?nQN)#Ff91T$rGpu%nIew(VO$>SdAXFqK0jVa(~{+27bHr zzA|yuSq%PECAy3GQr*s{^R{2cz=HGn=Q*W`4I2Auu-a}lTl@OBYVg;I}<5z zw#(D$5`y1+A-wBFORN1c+=epkg1UNY+#SKcTUG}Ez^^=&KL$bwHA@t1)UdX%e>z(cfXnGZ^i}RLQ77tPc2FYv2&YY6a=KD8Ir|x z^^Q|;McTA#DjEO1xoK0E?}ZOigTg+sR1zrvlZ6y!1cihQz@%`fsHnbQ<%FYo_6BnH zmu$_UqjyMMNBI?iVAOw%UP~fwn3JXoA-(|5*xwB@QGh}^J-?Sua zqAAg%k}c3jM%VXB?b*d z{KsSgXlZ~p-pNT?Ow3dzzC;`}nQ#EMD5w@hvkY!6Et$5au0jbQ5(NLUH;%;0zU6b~ zHP=gto*?FS0~_;KXj4T~;W>vzn~I7cBTF(cI(qVE|DUq5avL=QNgS;BxC}Zy@ls_& zHyQ8+4{^1pCwB4>1yWH_u~dor+}Z|gctI^PKR-}7SD{qvLh<&Bi^22jcMY_(m57w) z1elbT@pB$qLqE*iV7ir=u7@l|zBF5eKw4klJ_cp)9I}JJWx{@b_h9d8Fy&Cl8txz7 zlp_n_e($k<)$vk74&ek6fyv2>Vc%#20t1ymV^Su7V1_463ga|^Ru0KJ!^#vSK|#V{ zW#r@x>e1K7!ohT_FDPIoA;A-^2@AU+7J=65%*e>h^op)Xop#d8*s>;u`1SS@^+X^A z5umTuL#mr9z-4rJ%GA>CyL;HqyQJhF_wqFBHCgsy;6~Mpx-6sBJlJJ>iRo8#!lQ>X z`iOJ2*6Tsy3Uf~uiIx{ak^OX<9C(-xpRgN}_qCN4L04Bd+xw;_G&FQ%E;11tgnCFu zsIAAKMclT7GJe=~cA2wkIp4t&5?&@GlouB6v*J`XG`N7oL7$hq9!5|ac5`rGPao&b ztom{~S5M#Ws;MC-j@-{@97BpEGrmvVq6*|BBtEhlCVLG_>)GfX8M0F&Lip!0KYF67 zfctmY`N>;Rn4eS`C+?Vnt2}=i52m;ZFI>JjM&wg{b~DxU?6#P?SokVNDt&d1mG@7! z5)88P$L$b$o~E`e{4RRn;661`KHrNgH;SR2DW!$-=uq5|P;Ux*8 zPY({XR8@WMPuLQrf{Fa|G%Qcp5JLlrBt%7_!Ux7iMoLRcjJ;fN+#M$en;wB2)|Z>} zh?ZgT|ckqB@+n%Q$M^q%LoV+-JfkIr;XoH;bZ$!z9NDXVV(F}AFNJv-ud7R1#F&JHX+ zq&A;@Yuc-GICRIfG;T*W4lWg#=))w;1d7^5 z{9u$b264l(j$^w^Nf}T1Y_?eb5y^4@v?tTU%S?(n)vc zxGR|_M?oInlP{u3JS^8C=^;dkgv`o_?b4k_b{(1Et*ou*PVWK1IKljBZ}0ZZ3_LJo zjBs*f*tPY-`S}LRyDXFsI%3x-@7s8{(3Y?8o)D2egM&y->-C0K!Vcj_^M!Q*bN#jCO_UT4ZWmBPn(7=Xni6T*hO` z1fgtJ?&16VM_TS8W=33pxPUu4cIUHtFXoUo<%|B&B+Tn4$*OPL{w^Jbwpw~eJ( zEjvt*symy%bnnsIDK8qVcChUFzg_^CTMI$dG=1E1J9Zm}&UHE~0mJPwOiCvU0QgQY z$x9*iGQtCsh6fE#c+_nG_)j%lnm5Wi^#qe)n+;&x{wbsM1 zCNc(ygjWWs?(lvt=PI$|PdC9k4Q#xhxr0fWi88D_z0BEr(bB^Kg64x!_>OyRkG_8v zm2a;0oT~rK{EtDveSb9Z{q?c$uWcj|Dr_&u(cv{VZEyrEBFn$ul&L)u8>X)(2%eBa z6%`el6|VV!kiS^I0ahf)kZ{Zj-=}~ss#tL7L^P0}+`+%0H)Vg3OpQj#@a`_pXUcJ% zS_o0;EZ+4>uV|ou^*ZKdbF)_?|9Fd`T1dFsdH7VSx1m|Sf%H0x0L8hS0_Kw#F{FTnlEEzLXnbwTARp%Ov5i!tytL5ZauCXBFan#$_N}FP+@6=w1{e8Zv%p0CA3Jrr#x{Zu&2xTj56 zb+AMPS*fJ_s2T(?YFM+}Gn<```%NNgpxV=5q2h2;QqeK6xZ}e*8#h&a0P*7?AncAz zGY};uK=D>Rx}jX2*Q;Y%rWVpRU+fV=|5;X}_U)KBiiGN4v*Sts@m>ToG8cU6`$}Ct zpQqwGH89e9)s8y1AePd2&!(|Si1S8>XMLQ%S8+5)Af z;o)BgQ!^4he8|uUUuz(M-p0!^Hd|*a z4lw*BUR+w9&Qj*X;i}nR|0uY`Cp`Iod{_9_X#2`i{VHVSLD1LBiwp>#z3g!!f5SYCFpv##B_ z=24`9#dusNulC?n3uo`NrQ1f=;Jn+=b&6S`DUgQfuOaCHk{VqT!S{O4?F$tRlpw)` z&u&2-LT(NwNzNP@!X#JK!vDp8fqA{wF#o#g@#6Fk{(I-wbV%f8cde$O{pmUU*Z0@T z;;^Cihxal~updq%gnE^m*^=6)F6JSa(nJZsY^%I}%?72)cm2Eul> zUg>^^uV6>qG}Hq|a>X;<`;L9Ry(%=S-ft~Yo9W{RwY9Yq4m9F4I1>H~FDHW`+zlHtsP3M^YtII`vwfLR;3cj0JBC8?GvO;z8IV7LwBa7U6e}y} zZqV z1+zD@3e3X>9K&!--MJuuDMskZiMXKL)Ww5#9GRx3CN`#<3<-)6mML>84i1h4s&nhZ)CL{h zBhQm9E4Boxpf*<9{e7kQJtr7%v^~f9e1<7x;Uhj5IZZ!yy{weX4zKgW?DmKrxvR{5 z;9oA@qwgu1UpUgnfsn9pzse6wJJoV9PPuK}`tsB;Mvi}8Ae)yy`{wfL!BXoBw^UWK z<$2&6U*(hkX}YSGGK?O?EQ`B}u^ut~r^)O}C1n^z)mn?`L}mzXdY>pVq)hX)41h%w z|4{vM^B7g>i0Af-NRqrA%VH+JObj$EMir@~h{$k)HIv--w<0x~a+v#@%x-Z4Rb~h7LU+UhV+#X|vm#1bCFmaRFWjs8kceWI7yVto#F-UaQF`c6V1E()pxgit zAWD=fR4V;}Tp*rLprhb>EIe%(4V(TqmBIH6B{=r8KWVR@B`|Hxd)G3;;&{J0*$*I!W7VE+(?wuUI;C?O)*}@ePV3GK-+ML|%TztTZh3E+^b-=3{O@RC8Bq>|hUM^Ank*c~E`g+>L$zjJab!hhq0wGOmtYY7oP zBQBWsni1|W+$wFl#b=PxawX}`AQW2PW z+If64fDRng->2Epc51Vrno3ELv}48{%aj!&`$9r)TYOD(G`bQ$$Et$2+MH+Td}qJf zIMK*^KJ<>g;2V3A5tPZSJb&-Rc2ARe)89yV&2NiZ0JRUB0_Sm@MaZvofY`Vl%Z7cp z&(RxyNKUY(*cbZpJ@5|ztl7Wss;fs}3Cwr!1>5#q*8Bk!lAR1LZJ|VzH8rcD@kYA5 zt=+8B8rhrVkjQL@xnCC%vBg!Fau0>)-tO18$3BF3A!QQP8$W!$0p`#)N@EkIR_3Ge zV2Up0x6waUtB^&ks_-QjAd}}3^ImGX0f2e8dXl4L^rg+-dA!i9I$;a?>1lp{8m*|I zsjPJCG36G>p_g25f-!T;pjy{@dYw?_=*vu(Z4s!{Ol2|orKe4Rs_HTJ$31`uH+&ZH zVx5|G@lciK7o(bS#)pqM@wAd=6&OI(>259_m{QUK4UpMNy}5|EOxHUI*|eH|Z}YzS zp~WSK@biQAsN-)9RlMmrso@RXm~AHv1>eTcGG?m#&j5FX_w3KTbn^Tqm*k|%XGX5B z&Qp9Ust+#DX-mcwbj` zCXW>gDj15&A_wH%1js><9UUX5;8#FEFL?A%97yoa$jEU%;TAq;f`c(D5Nkk_l9)J8 z%IBY-zk6EUrKqGdv$pmof#i$^2^K41ZDvM^5iUv+D?!%FIHu325GN!w?L*MQ`gV5cKPMi%4h$F=y0KB0A!leLbTjqD4dX7*OoN@Y@tkYa*i@KVnP-c7M7NpL802;A6C=Z zZQ@1<L3;o4G8bRzpvBDBT|D!qAQ8RT+Sfe+eEVg; znIacb-)G~2PH7~YQm7TPgM%iKuBXkiu4*>^3U2C;fxF%|bYjWczkjb*?KljuUQWuT z-{0N{g4LCXke{BPJ-obb-P^0AH+!E;TjDbrJwT4k{|u9*_aAl1HqURF2ERV=V{-?rOL(``}%&U>-j_y9hIrtd3kvWxM$)-ia9`J%IWFqehd-% z5%7B#G&ZW`PcJMkanJFS>sFvZ42Q0WkCga@<`1 zQkgg2Ls#th5@tK|6(8>ApqD0CxJqF-Ffh>b$*@UAO|4OQ+SX28MFk5!+4AqAVlB+uk?DpY_05Q!(F zzWkGnZ2C%YdN~YX-tzG{eLLWC-UH|E+BYb3c1RC7wU@zg5i6eW5nf$$k`ry2u!7+1D3>O z()Z?&)y=a6OhMkpjf6U@p-OvrKrUV{YiGa&Tu{VdC%5@jD>^qkR3n?qRbv6eVt~nc zJ0Vw?9YN6HtoZ%elN10%(AmA$Vu)aH0M@-rPQ1;zmDvaZ^iNdJd=W4~a3HY9{dxcs zG($}`ujDK&xBREV$am29_xD^J3Z*JlRaJF%jNVW7l$6DHcXvxmOI;r?v}|k*+6R3I zbE*x&CaMcyJ~m+f6F3b=@5v!yvlBxUy0o}AGp)(u9w1kqmwXzR-@X+CXFUd9B$sy4 zwZ_f9xUzRMh#K>l)!pReK<{v7e^9D&d<#RE3|fT%mY~KmK6}$loHV{2GZn|VL`|Qv zf47tt>5Jp+s5ccMt3H^UUUCu*!QKsJ{`(+JdIhWf@o=KA3Gtlkwmd^k~cu zV>VdG0I087_-vMU3zgdPS-i{nFb$+?YHGT=SvJ|_Wo4kYGRT7r>0obP+&}=ApG1l z55vgGk-OY_?5Te?n(XxZ&Eon@+cO%y@l4vHf`BcHx;cE9mFoO-!XO3Ul#cV_du{y; z0TcYS^5Eq+QZQU8kVy${R*fgt_VGa&T89e;XG|B+J$&c`1_;#{OjbwDWolf%7N%ju z%QVs&zhdqViWk{-0RWzGr{8T>xTp&tn*IU1)T(cI)Y9J%_mBWp9lb@#_qmZhj)NxH zw)$u~W|=U8|JobIj}V&l?olZj{?B-o^ud?k(zq+4qd_qcPx|BO-ZM=GruOnexr6$H z{4S*W*MOm1I=c%9jcPYqbGe+%z5j^q5)z*!qN0lF(Qj^RYiVn9XmNv52QfmT!7%3N znyFAWEX-QcG*b1bpNt?|gY8r@x^WYXvud(gG8N>EzFp^gNsq4!xkYfHO;2h1O&G1h zYc~-w;4ieUq@}fFn%^|Hz;SX19t+uRGADQ#w^l!cD*`j{O%+lSq|~^JJ|3DB98znq z$Z2_K#jc2+m`s8L=2hP-&25qTHT0A$^lLQn=)=#E323Ca-Bio#yt7i_H*kAtf)4CK4qu(+ z{;OeKqrvO*;tX&)?Cgn{AIX{vYg?hvGsHm%$PCxqW7mBY=QNTa#pBr0JEuv9{^{x|f`bFHcwTPYZnfHT zST?+XfHzQ5U5yD#u;33)a`SMCNj0*TN(B?BYgfj=ecv-P?nY7B(6Z}C5h}Y#O}j4( zojIWri&6KH>C@nbe#~kVEN4X|YRwLjBkS*u=c=tHfXd1pd2*vC;M4YbgWtQoD5=A# zOJP@3XK^p1eVccPoNA$Q$; zJMDn?bIrxDKl-;%SLB*p4KfrQzI=uAK3oH|~P4h+lc!#OQ3sMd#WnrbOp^K1CC z!)boA%teM_v_52j=uh6A@Z9RZ*r8)v9q2%kd~`z2?&N{mjH0<9OygwccklD%%pX?+ zvQVL}=M06*o{U)Qp9tum%mgj-3fkK0>XekfLyeM?5))NHCzuZfYyjCvde+xOsl<3E zD}sWb8|UpVSbc+%ILQ9sz;B3yajb~Z-L8!XuNoW}|22rN{$Lw#(*etuo+T;r-)|%^ z)3u#%v`_$Y1U{GR#2$u$X`#W^lZVjJ{-c`?hd;H?;^Sd2zvkPZ(m`$zG=jv-kI;s! zcY6BjTa2UXvnQuF`S4`^&A+=UA>H|#L9lx834kE#Ni(6Zy^qy)S}@(}gc^ju*&Mp( z=z_>1Poi+0oX5rExURI`t8wb~kP$PGE)?kT+JxVcn)I*0>i2T(`0;)6B5(1AOAQL} zCML6xz3KS&?$KtMAbl;TE!X6S)!_cuNcbjzkPxKN2v!UDzDeaT zz`BCXLVILwJVvf?GcT>iZp3uDtm=`EdjdobCYCegY(p$li`pQ4j`W)!p{y$Qxc}5q zYe>b}T1aCjSip>sc2ctHfO8mxxp zqD#+8VZsO`j&kAxobqSP8(HZ1iS-tL<^ z_GRbZNs zV`rq-yuQq-!P9bq0ZO##*{lGpkC7t2bn*1kZaL9E@`|$}otW|*QlhzQH;ZNeI1vNG zjXAHPBG{T6I)PO|!8zMc5Y*xED#zT&$noBYvS&wUogtaUGEcWjPrZ;@=LAWHHKDG- zbnJ?!tXgm^qc&AFha&zLxG;iSB#A&38l5)R^F&&8xlHbLO>7o*nbt>>WzHDLQgxN7 zFlCf@0ze|p8N~e(Ug!~Dzpns%p^K*s4agvH#x^+piX4d&k` z$SJdOEmNuZsvw33xj~?|*rnZd@6HbZ%@dlpGg0_XRYSJ#+gslP|47A4UK19o7ZQ_^ z84rZ*gKDxzDiQFQ?_j_2BQ1(^w`#FPWr_2dwB1OxyQg^T734CNuMaT^_hdC&ua`MS zrIQFGavi%B-^7o}K|}oh{rlNChq}AF``Q(ehVLP%zrUiiE;lfWqmoai2ssnA>uog` z0Re>M$v}q?!KO?0ar@<`j|ZW2ZC>~jal)B3^@C=L?|K# zZ_}I6WWxxiR|NOVv}jKB1POPk7gj?;3ALNPtLtL~A8T<`OhR^*vTGj$dZM0aUj&UD z*M>UVUE`9$V8*N^_#aj~XInhiB4#wzeB(EKd?5QEsU!;s-yQzdyuDhpKn?U2aTNTL zD@MX*Hoo2&ARw4YUoPhf>^}<1uQr}zA(PyJ#t$>Z3ba@#O)Pd3k*&&yO#gO1et4@s z4oAY19J1s1oCVo(>^t8xr=h2%%0bq>`7J6*vOs#lcTcs$t*;D=ZRm6Uk)!k9V%LW> z>ffNO4*Y7DEl&G*yUMGSVkr)LdPSzgoYmzFF9 z0pBM}N2sNT^w;T?dDpRO0iL*He#4$(>5y}Gua6qTtrkRrowZW&q=3A~<487i6cm(j z3_52ww^Jod!2btw0Bx@H>r}>jL?qmrah~+6ew7fUwm*?RW2$7YMDZ;6&!ArynIDl+ z_Fo;nkmqO1*|>u!(Sc_1acl$iPQOQxR%TvF8!0d0Lcpg6`fF#9VtkzY_b#z=W%k0K z61_ID;azw%mS6!?yeR71M~8#o7|(q|YHX#oITbLf*-mCt!af;Q+_-)LwG z(-;E+D4~s$`$^;F53Q`gYfMndN(8%ee%@idTn<`^kV078{<}IdEQ0o=NiNWIExxz7 zaY3dC9u0zjc6A_&P3ux;VcMjcAJ2<_Frh=S*g_xny!(t>wuD&hf;i$6%kXqo&PCF0 zFaY4dp$Wf@1b`PY6V-ApO`VOT?l@K}7pnNkJa{i{0ZNgc<8-H*h_bLa-b#@iLLCO$ z(JG=>-?#f!QgEGf(X&?@8mIx%?P*skbJnrJK?(|rhYM`D;NW1Lf*N6_nU!xrP2_b0 z<1vaeczKfl!5<51YcX7NTlIF;O;wDgZY z_(`x#FO(BY_F7wzum_M}g{8wcHO4{;Ayes55$#ilZmul79L$xpoN}zP_WQk^Pn3%y zJkDzUmv{3Mq2s@FIUmmyO7ltZg2*7DBNQ54G)^xIMX*%;0q}|F$?w||hW)_p> zsx3Z4CiY=dTSwz4P=SbNGyeCxymU5b=D?RDC=9!lp+VX$K@ZCIa}6x;QB@(_3(T}$go ze<)H1@818aei(#E$URr}Qu{L+BGSxcl1Y_Xt5qF8D?8hrn8UJ{>@&h=L{>{*%S$}y zd65F2j*KZb5q7a;&eO-`xFLY%T#);{rVR`I(}19>EtZTi5l_Xj*~k6%z$`9kOv-6z z9vm@z!^A`^Ket6RS+lHj5gZO$fYoy5))fK(+!6UnMC8tjSc(~FWI_J)!^h7Z(p2V0 z{PdX9dz`Hm9+K1{U(TfIaXpb?r_UFg)rgj_n?8LU5H35 zF&s_cn=Eq)&R6b^P4OoR3cQY4b4xM8h%@6)Uz<}|Q-&vs+2Gz^USX%wRkz_ZZ8c|Y zc7Ia^p6?vn4Hbg{Tb1O`0|;8ASJoht5Q0gnOCdHm8Yu!**oI!jG65bq7zIQRH&cN`EnW5isSP;P zz;16mF~WBYS`bae!g5MrU`mL`gF;86pih^h$%-VyiJe_aqliWAlJmsiuc_%$6Ljt1 zKY3XFvjuhNIxa5GYv~OM90U}RH=2LhnkEo0l8EKFy%{lLeO#zjYUus6$b}_$M-F0dP%fB!Z*4|Wx;Dj3EX62*MNT1dejZ6mj zz{72i6CR2wI!o_5W|w-3kK{1uW@0G2N}qFGuL`fZyaC}79eJ+6S5{-UI# zV|{0UA$9x!1iVwmb+jKiH=*w%jc*{uSbim&7&HXKZWK`$$6;qCGpFXR=2?{q%tIUafnCnI{ghWL1_9K-R=nk_|O7koIw zfhfpmDES!H7_G{OMLj>|a<;D+-;rSgZBfog%RHfw94gZ=I+L2*&XH|CNAqCZLnxI=jn;toc zOj27LH-M3YgJZK?{#VYFMlj{Os1=oX1UKry)t-pIKI{ilkfeFU} zWO^MOJiNQ>q3ktMHUc1qmEr)c2MGtBDg@UVEA!XP5w}M$@i9Op;?|g*p$C^zC{r5G?Vk~p}WSPy}yT_8GkCf+D4GJjZ|A(-* zjEdunz6A?!+}$k@2oNN=ySr`|O?WtF5I4^jvyFay9-*OUsHtk^b2EOnbg96$i_makb{Mk;UUu8PZxU zsq=XVn?F^2t;^~(w3s3^ zhx2889JCP7;~|P%l>PS~cAA%dlWT&|HlgB|E+m69<@CDR?G{gKCHp(sNh}>c(IVn zL^99527l$2MU^Dn`G1cRyRnqR{?CVu3XyDGoy@5H0`L-(7}`t9`XcyNOSnb1g|^efwk-N z0ali|KbtkX9*@oc9=z_B#t{j=Kco6MpEY*6d4G($4DKcND&8UWF&p2$loh>qJg@ah zk3>vaOO8>s6YkaOH68)l`azFg^)rp!)T}=6zXv z93;Frsgw1lae)n%cJep}ci0Vmw70L8&C0j=bmvp0V>gvPr=o3RDZpU$DxskZuNUzDGvD2FRou9Mvs7N*(N!8w( zTOEYEb6Z(IF}l8rpgR1I2bcFf`6d=lL)~tvk^f^_@x$Mgba5?^<^V!)&?KP2O|AWUM!Ex^oqJDbNgqwq& z%=&Fo=837tRWsauJ(_uQuOulVX9CNwff)`|?dSXSjR$v?fzg)-Q^Zc!=cR z&0;r7tby9q(fk%ohYCQmGkk1>LbOMoSr5c785Q!ObHqW(-Zqp!J-6Qx(g9?q+qbG4 z^SFtp57nFWOU?ENcQZ8l_|3nx3?rws!Mz~(z+&&qJOoe@f#-API{1wGL3aruxU_r` zasF20v!Smu?CgBDmUzh063eE$&W}ZYR7>LX^a&M;)@1E&4ocyomgkMO6vT?p8Z6iT z%a%@_ZaWS+q_GUJcsOTF=6`9xB`xaEtSgzfYH;J?R&1|LZhxvikUUK&fJ@V1%3WloB}dNZ5-D?hd>qWhzRfey zqlbI~I!97FohsH)f$+X2W4)QmZxR^VFOYSE2Lg1+p4ne~<`z2lLXgv*4)xwDbTk?P zAU~P_?+4RLlm&(1HvnJ^QAtR`z%^W{ZM~dNacTRS^=x>R*i!hZHA&?&?5#r38~s!0 zVe%&4#45#Y^l#;nmdivL$Iok01dNuX2$n-MRdA-X%drPhDNl!|>C1QY8{Wq|bYJ$W zeLC?V05Vl3pMxJZCu0HjnXW#;rZYuV-tDD5tIuzQ6P%SD?p?`lS!0y*-Y;7`o%=fB z5h!e{i$03ZtcmnQ%XM|jnTTG`19k>*zt{FN&>~PQr~A$7%@R+ zBmJa$by48r2=&6g{A0JiN~0AmF2=kjM1f3&*jjk*ubYZcLRJXldA^h*_L?|BOAe~o zZNdy}WBJo=0_wsGK&D-psB|_{@Qtv;$0>l@Vdjx0E6e+>yYwOH9DdO<(d%K0z+^jq z33b(N!P*k%g5gpXOP%M}3D3`%HBB8wg0CV4a!VW>5UAMqv-N)=Pi~r*LYvrpMXc+G zztJTeR+c$rPGZa-xYSCwqmwoLlp)WH505HD#(Np3_BkWHnI-=7!tgr1?Ku5=?nJ=j zuCWkvBrL(Q)61#ilgCOvO=c#4!Qbn_cJ{^vy|&vB)Ol!9cUs9CLM^{;m(${98vfhJWyGS-_0o+WclxiPrERkTPtK(%T!`mP9ay&#|Xn#_0p{yN~)KTy~0x0}z%vMdPkFRE@sO}i;? zJPd>O{08gSn%D#W%pjIO%*j-68j>`TpsmNrDT%O@b2K=CcW#lW%JFzsRIPk=(%L^; zSmV%{+AZqA%*#M&1OaH=Tk9So7Gji(IAn+Pgar|$+n zizC-S*ZGE?$SU?vE#;pskf6B&H9rq=3VLr=9cW|`xxCq2L>4! z_!lF$;gfo?m%jfD<+Q!KVl#Ci(Bw9fxIE9Qx5a2@e$+g)-?|(EPL7bG#||+I0W{5s zW+~N>xXR%gaqchV)cmS2KyDXD{JFU!gt%FpM}Z|g04&exDrsSs!wc;WHET6h6y|mW zp#!uc;_hEeD=JDG>n-CyePZQPh5^ba^wpI1FQEWMa^|BcZ3mOseusP_3WcGu`9EX` zp0&-eGs(u8r6Nx9fSiRsat2cXoAvYY>d*@+hR?-;fo4z`wyNst%k2k-*OYcw zN6ph@N@&E5Pty2$x(EJv((e8X{bD5F*19zQ$mk0n9N4!f z|Bx8``1lA$8y$EOyU6(?r^^y(gCPLO7!tWliUzyRg3`M_-C=TR$q3arDmoY(e8St#`c&b*Q^U+Q-uihAo93JLUXAgPcbXO5AY z;Md5ms~s0}a!#Sm#p)_@+F(ph=hA`0s-~MensZrHzJoF@QnYkVh*L;VHoN<9x(3er zH#{)m^yq+y6D7m-Ax+IlvR1PIcJ=wM92@uQ`^obloBtUdTKu2)(!8-l-ETBa{kj(D zRKC^=vF%-njn1+S)T-6HUU`>GY}t&5?(K(?IUD|`_1vGN08q*z@3H?!t7T4O;-%;8 z;`<3XI>rh;wyJ51&}+~1!&s+*jNQD({7L{1la|bTS=Fm+hqzvD*CfBd7+Br)`v@OD zK`&#^VB3HC?Z4gz2q;+I*UR46*w_PdWQr*NEbKC!C-s$Tx2*AJH?3;x=)KHtsiBnF z@r-U&Xh~`P#6;sb3@mB#a#Mb&A7<{&TxCz|zaVsP@mttaq<)x<|>5C4B8ZRdW1D|q&!5KQlUvDS}F@;*Z zG>)$9Dhp;PU)_upjBQP|xnXz!uSESb2nk~Ijj<~0&xCI;=G5qbn^RE5j8#45EZCH9tno-|Dy@1iyMNA_0Zfy-st~aL30&g~NCpJF)Qa z4D6lk(i=72cS)U0zc|S^;^@j1#=q5)oLZLrr3i&|`sKcJ*YBJzN$RtZvBT1RQK+OR zUVQOKL13dT?+Xy)@xX)lqVvu>Nl-F#V#pF%K`5ZBn#fvikS~spgL$-Gd;0sqhvNp4 zO!D-v`GXw(7t{1?PazVH^TH4~nin+mE;L;8cgHOno>Fa2ubxO#gNe^>Tki^p6W2Gf zOui>;mzlz%a%3Ni^GcAr03guMQGhPYu< zoTKD*Kg;!z>5%nZ=Whe}(gp(Awg*Ww^_23XJCFAs_5~g$b8Ps%UuNW-2E0{1-d-zq z7G9s6lmI#Wn^V3&GKp=^L(`CorfUzctPk=G-iI>`8M?UgL%Cp$?T418gk>TBZ5Gb~ zuU^akT2hwYe2?g;>qUFoFCy(r@DnZI&wlFt^5I>6GX)G-8o1w89_?7JKX;s$3*dx} zM4U5=`0YFvE9HSJO-xc!m-l*j3bv)!Ujhs?)n7Bdu!yd2Daa;UxL#bgiE=r&r0ssa zSl24R=jsdG8rC;YaX090h}-WG>GxE1rh`G7YqVk0R`G5MDj3jb?bd6u7;At5j;not_5~-~KtEV`C|{pz@A-QdOAoDzdG8&;C25H? zqf7+rqB^O?e)LF~O(b$2!2;U!`NfT$3f z7*a0;hP!EyQlQi2w}m|b@MZt3TAAG=k~4|a)qI2NV{eZacd(RL2n*Io+qE$0lO8U!`%iUP`$Pu4!!|aG`7?r!^vduMWwufZE*iVm-H&l7=wG+Ol~-N zQsdqW*Z{8OSe(7zWWCi~Z|15!<}c;P<%PIE!^6`y;9AL(VZt-(%5 zXqJsQps=G9=F!Bl1aM>JO$#+oo!5YYlbGt?Uh98E?6g{5R($%VCxySonmJavYcxOr zStvU_lN!oAw!p_}1OcnLZg@Hn6NHj(Z{+VVv9S`NVDiZh$RkNa2v)z81UA`7i7A&E(1c?;ZO8JNx{<64UI1O3OoT z`FacGWi7I)-!b`Hkd)zufjEDh>R3gvVsSVwk}#BI9O0hDlC$C!O3 ztk^fT%DpEBBqk?PDn^(84)~78!lsgjUG@B}{`DA}eXjB~#Ce2WuK`OuJ4hK_Z*H4oloHlwqO+|0p(>PJ z-1>$Gs8#Q&W3CfRfaX_hw_g&r!~0Yc#fG=fdrHD11Wy^&(hAdEhZqTp=D7sdsy4C(CXB zsIIQ=M$^)^`X@d&TQr$5Hw=08Ik_O6M$y9mOZ>7EGPglT@utfP4+@#o*L( z0)_Jm6#=v$--(k}-V9f&@n8}fa2qx<3GZXa%g;|VG^BZmrf+$hsDh z`>O4*sXx=K-W@&)>ry0;;#ezpMk(!!-X43twQ?}s3}K2Ok%z%vEs%Qu=!M%iF$b~n z>gwFhA!b=w*IiWy=xaZg|GlWyW?|BXK*)o)S$o?+?O304)J4G))4Z>!{Ut}43@9p1 zm4XdQA8(Tw$hq>040xFJNpoB8=O@!sogz9l4NfBoY{&RGBH_`Azfj|*m3cp1pXkYv zXo(IdU=}GHM`)g9D-%jDct1`}2OPL}V*+w3q?l4xCSLNz8%xH|M#n+icz zo^^U(>e*QX#)V3aZwG!JC}+zxh6^<=^rPrn(WH-CI}H$GJ%4G=)8^au#}2$V`W2;I z`0skW8WS$ujYQ^?(@Z0~^iy7L`3Z}Al_`uF4xFnolCb&AP+`Jz-=6Of2XI5>Rr)jG z+k6w}mqGrMpNCYqv_ymd&Fmnlz!J_~I}m!IiG0$naHbsl2*fCS01jvY5Z=JC+Ue{NQJC&&;DFBPjo|pV4o$IO-GxLD|kC@*ga_(qJe7DX~^85$AMdQ@mmP7WKg)6}Wgx;B1eN_>S0;T#WB>9FkB z5+ff@PP2UA)h3IdOM8d^JWW>+iNs_eb76iMCjm<6eKJd4KD82{TLn! zjUNhn|5IcPPByr?v_GLnBRp8G*HCmmKvlIjZccQvW1p`7t?zTc*PydonhMD8-m~`t z=MuE;h7v;;4_Rq*zsJTdxUBG5yMT-V7I^;_i|ix8xh)4y7{Io!3hE}5b>!;?x&Nl) zA`p4T6e#Ovb}nhXXW--6Y;PGm!MR(X|ENMd!s;C|c8EnW<(M{TF(_-=V{X@&8 z=#Iec#9AZzUTUy@$0hwu9N+4op?4A{M%9AP zM>}u3C6wJGo3x;YQu#js5Z#;gO7=MR=TyeFVI#yl*|o@y>O-Yq|lIo1b%b?#L+4la6qi3xPM zGrJ<6{lB#UMWj%hR{@o2JoIrpT-lNL3{^fguU%Zr$kMvBQ5%!v6;-0#>IF#%0!E!x%*{pN1f!HQdq)qU%b8woe5&Q`VANkTEbYl8Bq) zZ6$?;w~h0MMr5RdQo`Um^$u3?^&Ug^65#^fmaLRhW6&6CM~dSp-nKqr|c4O4}mA&sl!6 z7~>++SvM9@oH!bhl1#@0k0^^OVfCpr>t&oTBCg@VhZ%tr31te{ej&3JwQf5rGQq5D ztasmyovVK)_P^CBlZIAYqQc|YB^{~3U)B~C+FYp>YtsGY@aHLT7u~ya|lzr_QxCI5bf45dxCAvke>^MeR5Bvp1N*)X)^9h`N>Na6vXpGtB7 z+ho0TKUENmPgGOy)+~4y@FfGiqGP5Cg7XXRHvO!H6QvSWmK0u2J3j#95)N8u(xMIz z44nCs`IG5iGQ)^%EuqIZ4{Zp#bT|-A5n_3LtJ6*8dOM)-YS2TW2oI(G?kOnOm~41D zrNoZB5q^b4fsjiE^6x_QTtS^c%%uhKGJ)DAAtYoF+9qMzgF$)E0t=~?Y?7!*@FfeA zceL%u2nLw_oV1q~7_@#kL<}8hI_LrPB~7lG(c4^%kHmvtW%K6$nXpvIhZwo~qFnM; zO__bIt0*EvfTRfro3Dc=9EuP){?W_ z)Z4nrqL<@stl5fQikG^_j^Retfx%NkX>Z;AA!TJG|GRLSM5eNj?9nyav$6yw4YVpX!fx z-QOx2u~n5b&yL+gi(#lBxoGakiw~1WW3@GopRhpgwop|;4d=;f|GqoUSq9D1mS?5Qwasl&BZk zJz&bLkD2!o0k|CO4x=f|7K1H1teTu6wRU8m?)rMV%mPQ6M=AjTp%kb5t1S%)d!p5K zgMC*%$U?E6+Q`bM(x1l{1HQFTg;IN{%6-lo4JW2v>QC+2#qwv3S0YJf^1JM zPjwPtU{bw~1DdA>bsn!8BNcdpX@h{I}pd_$ORdW=*t?oEF~7601fhoV`T}9>otCP;QFYoZ(B1#EwW9C z+t!rFg258f45pFP-4qi-b4|p3+zHI)+H6eA1$PKqdj9@%NA4=t?6t@Bq<)#(Ma-lE z?N^O{0LVvu6V?Is11IfZ}rJ*^6R-%Bj8> zJg@P?+eB_$rb`y;y{|mTQ|`+nwh$d9ZT&y&NOf|G$)Wy;D8vMxQQrqF74n2w$TaX+ zzGlx3SFury(0gUbem?B|)<_fcWO7fezAiR{%!X6MP2h)ss5WtAwD@=VSTyuXguYyj5W)?u$HtdT{Ks1X+n=7L5_>QhQvBVHh(f^txwUtdDHY* z+RY=|(_$9pq#}weN@x}l$8VK+@~pWydsT##;(<f`QD`xAtRE&{q>0Wxg<8Nw%Mg2nRf4%3 z8EFfQpYfF2cD>M4$=fgh7t43hxNP&(XeeYmjv^csYWqt{3r!R{ zUxw1ZDKhkr-_qDKx2_ZQRkZkZPE#Fp=lX$?kcPKsdR9e@m7@pUGzr50W|IuT(K|MF z+YB=}_YnTmH^Y?=e|WO!*ZE&cl;Qrg+Fqx*1i7Ed!dgX>$fSA;sN_Occ{rs@6f;Y^ z&>Kq|Hlpf_m-Kbp3uAx9v6M+VB7;Mrp>e!y32y$)Kd$;Au)F}SoLb!8M*DGDA51CfOaw_XKK95`@i(40;c;x8ZEDaXqeEgU&Le;=R` zq>3O2s}uu2HBv80Pyc#5$8rkg2sESao;=%ZSLqWHpLy`b}vHJx3>1fNt$}xJx7crxc?$PXdZLDMkj*J zkE|u;()UYXVO=grC?M=Re$|9!;}S24&19}PqXrS7Znkl}kPlVR7k`l9p!p8$exM0! zW|p*kmf8=qU#_JaZw@6BE;)?K+oXY1$a*MrwxL~Hks94xdA0blTS0WV<4>HCkPzsi z)oY6=LTJsx5CABg77HOf*Iz3?;KGpeQ>buSHL*X)GXx63P}gQh5bKvd$tICe-B;&e`@eEa!pBc1;)zLL@bbNw$PF=6zTw1eCg1`F4y11^$Yy8`7f zve1#EZ5aIubvOnKz;~x9TR9sE#TWM0pr zGtMNi3*I=yS6T`sd~VO;^3ry0RyV(HsD3IeDqLk*XVqVQP9^|MIU{$hRUkPi*(CIt z{5RjIVHHwJEc0#bd?K~rAsG3{e@JE-cbRmkVuM%yT{qT00svy$mgS$c;1Jua>rZA& zgi9p)j~0REaSGUQ~#%)ccoVd41hyDn3tIPS$8iD1S1l zC_~V95Jj&N2E^p&OgvKhM(v0C%a96P$Na6W?rjMf%Z}JnGk3cG;y`g%ti8O+HaxXZ zDZCIG7Bg+>{^C!0EX~Xv*N(9t^5R1jl#~Gy@cx@uuF(3EX0!iMpP>@%4q~diTt?b3#zxR@3F#KTTZ5o`B`9G?xpUI{MQZ>J zEH*JZMCE8moS`VW4PO}GW2V?m%1SLjR_DW%B4{B((P*-=cjsZEG#^d9mH^2om@VkiZ$TRmsf;uOe%> z#rsL5JjIm)IQ}pW{*o-(6Ay2Y;6k{|^c9SdPpT1LPf{zLmhze;ez7Vd54e&P;>~MU zn`>b8y5xx}%*4?}V)E53IxKJN6TtiLy0UnCXrdJ#Q*SE~biz5lDY%`-H*^W!?l6ah4KiTx1dn|pv*yI z3jfoU6CV?E%HVoALE1ZeulVtcd&D;1T*)qxk$U^6eQ5Pd54?NV8Csg2l^X19zAdY^ z2>xCY&;@Uav1hTa+V%}t^?n}~Q_B#AVGd)g@ZX97EkUaze52?G13>h<>7oE*V~i)~ z$IcN1Uis`y!lu>kVrcQ&&Amr+pKU*NUDs*)G%fvdCIUyOSmdIPvQoH(ey;N^BhFbK3iy;>`wKp@R6Lm(;(ai1Y?T9~!C_ zI6R~@x001fN;LHNL0Xg~ho)RLDm!P7RKPB=fp?YiO1TJRY59^G60P^odeo00WS?m)vP^+ACP`(s)@ z!zLfNF0lQK?uYHi|NI#gTHn2G=L#)EehHD(-(PLbkChK;D{XvaL_;tc^~J*XXYz-& zjm5{^%WEF9PKBpD*|o`k#1VA`TXil$A~9d59eymE9Gt0D&(6S_p(( zAUA)c?4(*u1OR?9jv~{cqPl)X(K6o(@dK!N9!j|<#J_X;<$aV>$AQE*trr3fIiucs z885C2;E-v-dzf5q^&%o7G9#t&J{|Xl$S~lHm`4kvfl-1uDqA86rE(EcZeg)c(emT1 z3(R(m)JjI z8OrDX*?-rIQDM_F$aZq{j+FnfWKnmu4>|3j-xBlRSSyM_qN22qwZ*@5 zpvXc;$uKoLO1jE_D+kjACRkT~6~+5vf^+j;sM0)T=l#*m@btQ-1#drm4rUS6%ndP& z)RHM&1hYsZ?ytd=-g-(2g-(m9gbeOl`VIAD({W=K;+h|#R#Efi0(B4oEQZy}j1OlB zyf;Auz}I^IFIIgFqypDi{v91=mIY*I3|uzu>1+TP7MNXF_;`ERVCsCmPG-&nI|8ho zbGv=vjG~XHix-rt5XNCtNuezhFUw9RWxO-_2!2{ehE&RhOpIdnWAY1B`hiVQ4=Vp#`w2LRwwR_~}I;J>{{)Lc7PWewi=*-E3OWG?wvx_kU_3=Vyj+Gl~fq8}HD z6s9l4NOHs*91wtOgUQ5ldq#nW(B$7+h!RmFYQjWJTu7h-yVW^qO3KSVu9pq;NC%eY zEw2IqElKQpr%O33;OmftgQZ^IBnE(_NHTH+RR4~wro^ILG^Fv#tiQW=O&@W{t?FHy zJJBNo*U9PVTCo6{?ZVlL_9-+Voe-B$^gvWsY(L#g8*5sMdd|5@J3^R(%SZ|6zRaIm z?MMA8N1Yk>~Az{`Fm)rvzSR#iIBu_Fef$Js~ z$;frOc!zGELDa3d+;9JG#I6{g+jR}e1jcw~+GxWe2N4}j$t86hWUL*?@?5kpHAJeI zx8`3jJ~RUe|K#o@0ND}IC|%kdY$q zHmBp~{Ydmg$H#k!>28n`Rf_$|PV_o+j>6ZRc)IEhK8GxlB%xTUIn&`cu*vTFtdxZo z)#GKUCU8YN`+I+ z;X{4g9p*JAgF}koa54>P2M^EnY!RpXNWZ_+%4J_2Z2~M%mqK{})=ptdf#*hP(2S_>x}WNeP9=q${CvrJvp?=;yq~NJB_$r+Y<(}{q5#U2CXRI-5n)5tlT>OROP`YR|5ud1+`WUs>Siv(baXxcV^dbj*9z zP=Lb5?MNKSf~BBRxkgoa2&wnt1!~*X0HK^5jR~VU9E5Dxh!Ln6y+|0O>ql<{&j>{7 zI9q)l81yF_zQZc{^ro(lB?c|xnp(B4IYw#as{H6D2FHxA-B?xmQ>Fc1L{TcT!LR z_e^toVlP+UK9+S=s~?i({;0k_;*z$s>_>A5=(T12gi-z^!Wj&jX!|j+R_NcD^}@d~ z0u-)|2-JK(AB6|wZTxtRyF_y%KF@2Txp#hb2U!_(FK*qG0_E_R#tF>c-MdOB_h&Aw zg(VNXY^nO*7a=lyNLK5;op)PuP$Mz~{zgQqI6uPe+|9wt%;ovzQ?OBgJXX8YkU`n- zm)+(4z$}Aici)RWAD_mG(fZugyL0~~qh}x0>IkDZ6M@dV%!#c%AP1$P?X_o>Fy!NY zpYCMIvmtju+vLw4l}gQP@u|mor@;ASQM6CT_@MBQ47)8Aj-Ltf)>dFCTHJYO$4mX{ zgpGh;$HUAjeWTY+mwy4W{Ev*^k5@UC9yXfhjQ!86*NdY4=Njuz=ZdU|i>1?DMmD2O z?K$uD3~4?-vg*gwEftt*>rW%mOwSoAZ2C zjExUy1rzw_hy@f?jqT30eFSBrqSZhD3oZ99Efm?)Xa7cBq)~3Yv`ry^2uvzARujmP zkW>g1GS*xqP*N^lW79uOenBN;fe;vjudap)DRkGcF1}|0fVP=7ml<(>BgN-f`N^DD zr=Q|Ltc^Kcjr(X?A`Yx8@ck6~=VTDA>ae2GaSW0DT%0E=6tG!sv%X#9v222QMqKo{Kc0t1~={=T>v z>5bSRD=g9>@&5DM0|S356p#oHjhDFNMG`ED&&jD39ni(8w>GC#*DAftHB)8K2?d9+ z;r&8fyR_;i6}6MDPGcydO3rN(hw$x^pY@D+MJv&gGyvSFZ+T6;teDD2&U#QPtMUsv z_ya&J_?@Q#x9X> zwX(X4VO>0CO$1Px%UZ~dRxow&rtClfe=L=S6Cj{a)Xu))=TaiIdD#3pX?m8@rMan7 zojSwJ@+8~L4J+Ew$dQlEEgDWSfAec4$)3oBL909T|ADP$n$#>U=g(V_v*pMy$Js6u~RwXCi3VI{UC zzX)R!V0<KEE9O>Qo$eKUoj+yQuiu z(ZS8{6*=e?xjCS)pQd(xI^2RlG~exGAh?WXKjjmO+*Q>_KhdUGrYVfUoJ&=w_NMs< zM!0Sl>cG8PrhZ5-z4#VQA1QR~mYsB1y$d!v5~X7IuWE$vymB*9^P4vMcN@_-#N`6+ zPL{R++s>NSKSv*#O#f;OK%b*oHy$vkem>Dk^9h$v z=lw{)|2QLL-5+lpzgK32bD#7TMtgjHeFb;ht{GOR&&7!@lm1Q8)^=s5fs0E3voW zG{t?rg|DFA*;<;@`LyH>$z|Ok==t(%N?CP7_sM}66&pK0^RR7$Pb#xl(Q6sU!yuCn z$C78o{dJ+BW=$;5IZnotKW(+42mUe*EA{R_1PnSVCLBvfYiq83`6v1l3tnU453IYe z5JcUsg9_T`w?7jf&iyV6YEOKU1a0~oH6|k`$H(Kxv`Sb|5S@cZ_A5l*oeF5&?>87+ zes+_YpQ<|NCft@#M$+NmhlM3Hg{1?xZGXsJVh6(pH;O zF*U|#&09k~@cI_6UIagQgY9DP;Md|q_eg}Z8nobFqV@WpmOG&@`@dzuBJO(hKZWZt z7p1)_14^p>O9+URFqeFS*VB+n+S{_l}%=`~~sVuMhPfX6|5vM1Z84v25G2u3ci|wtWt`D0R&gnBz4$aKtF(%|!fr z&(xL7fRadAb?k3lJ68831 zXSln}44cUG(A>42{YqJ46vh93q%hazifXObG3q#R*Ek_>4mlk2bE(z1H= zuV?2=IHZ**s!8m0@++UbmW$y?LSca}6L;)-k0++G2lZx602l}Fwsa-4dfRG~%+E6d z$tf1wF(549v+r|JLCxd6!Lm6J+sKP{0BhnI=x|bH`M~9w{zVlhZK|lzfe8}OKWXUn zk?L?8t~+9VVV@zluwQ7QzWe3$0l!exkNU&b#JJdIXz*KqAJv z`H7Ig_tm-j-}>OlBa53tVhzR=Oj9jKthzxhvY4jfNNh>$tzrP#?y;FlQ)|P3bS)rg zL9ruTJ>XuOv=8CSk{m}FkcTm4XmSRfhjAD$s4Dx}(C)Zv~xv97vd zl@vrBXcR?I%dnV%8YZRgFb`Xs0R!log#X4k`-bdHAXNSD(XpLKaPw?lU8s`A3%Q6+tC}LMSU8STj3rZqC-&;+BpO z5v~SJP|Cm$2r~0h@S|6=Cll*KfHz!H)(!2|wY>Y={gR1=Nly6{uuUB~wnhT7Ik9p6 zWbbK9pH$?6e94YO12qGP0N*%802E~hi{MuXd75;~4mN@n+8=gszLGuw1bdb$-$iu36rIw+mx#Wutn84!&9S{+@e zA>8C!x90`6bhTYgg~T0mQd~dZqGS@CzHxx)kAz=hE^aQaNujPAJ2VZw}8fk$)Yy?;SU5_&NBW9~lB>u>>^- zGH!5JdG6bOV;CQkH}bzM zd`)##r!Uq4%E;K1qVP1K z$OeRMi^QgK%+V+U%6c4#AsQ8h;JX*C!G`|V&$%iK0DyLGTlw3X{p}eaQB(T3>u>C@ z0|2^Z{o*BC51WP1)SIrmIyFKG0D`0Fqxb$e(YPff>4xjB@$LBJuOjuRTdyCM6ngBN zhgN?4>&{k)8#Hg)D3uHcfac=WKmNAEgQ8>ae0<8t?2t(A!RMcAsoebevVF-TZtBZ! z{$^z{!vg@skC`!jXbb>=r|Iuyf9xu41ptU0^uVO-9osi154bulLJ9tcWk3G0x4{k) z5@*K6kOmY*Gcqz}&AP8|-@Zq5;gpCN4u%+Q_F%{wC~zzS2!azl6Y$s?A>azvV^fT;er&423Yu(#g+ac2c! zGSm3`KDZ(7`=7r3rLZL~EsLZS2q#NEvU`(1Y<;E*=BNlzP^z)VMsGDXz|7zT0_r6eeK~3i*{=r-fX~@jXeBgoEIXOAU znm(Nh5kYxFw5;z$fn{6=ci?KBDk+EcIQfG+2}=bcLbwj4C~8b@XMRS`GPVTU zZzJhg2mu@Yf0hTWL`-=Yat{$R#d!6T;?ukT?qsyfsG&FS1oH@OF=bq#- zeVV`_!UfRXf1**zkd)K@qTS2yt8~UhMy32Mhe;`X(b3mX#4ERJ^~r;~46=`xaiAL?B8NMFwOF1wdy81lqKr_fET_ zZq4V5c2%^Olk38-yL(>ma8O62D0n+gbRwpTjO47yK*8F|BX`o(PsUPE~6AUQ(xWeA*>b}4Lu>bx*=-74O3!QKL7mxc2x=hI7*y0t9kEn zw@%5GEqd$Af?zBWh_u1uuF7E--1h8VyB7dLaf*b_0|Puw`#D{h3E+r@GbxwL1aE!4 z72v$rPe}A|F1uwjh`?i(3h(+Ky_l5^69WpFk=IYkYoyXWsP{Je4h{ud?{J4=-L1tumw zPS!s^|NZ^Xz`(%3Z|iJK;1UHf>(nI6=Lc4AIf9t=U}NKD`~M4einOSsvo7q`-S3}1 zf5LW&%(u%oFFyUR;EB00s?l;pqNJco0|$S4>EL<0bnmRgqBNSrDF0000 Date: Thu, 14 May 2020 00:54:12 +0300 Subject: [PATCH 42/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 180 ++++++++----------------------------- 1 file changed, 35 insertions(+), 145 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 62ecaa6bd5..84d1fff3d1 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -245,7 +245,7 @@ You will see the localized result: Instead of a single template that uses the localization system to localize the template, you may want to create different template files for each language. It can be needed if the template should be completely different for a specific culture rather than simple text localizations. -#### Example: Welcome Email +#### Example: Welcome Email Template Assuming that you want to send a welcome email to your users, but want to define a completely different template based on the user culture. @@ -291,102 +291,17 @@ var result = await _templateRenderer.RenderAsync( ); ```` +## Layout Templates +Layout templates are used to create shared layouts among other templates. It is similar to the layout system in the ASP.NET Core MVC / Razor Pages. +### Example: Email HTML Layout Template +For example, you may want to create a single layout for all of your email templates. +First, create a template file just like before: - - - -## Logic - -A Text Template is a combination of two parts: template definition and template content. -### Template Definition - -Template Definition is an object that contains some information about your text templates. Template Definition object contains the following properties. - -- `Name` *(string)*: Unique name of the template. It is then used to render the template. -- `IsLayout` *(boolean)*: -- `Layout` *(string)* contains the name of layout template -- `LocalizationResource` *(Type)* The localization resource type that is used if this template is inline localized. -- `IsInlineLocalized` *(boolean)* describes that the template is inline localized or not -- `DefaultCultureName` *(string)* defines the default culture for the template - -### Template Content - -This is a simple content for your templates. For default, template contents stored as `Virtual File`. - -> Example: ForgotPasswordEmail.tpl - -```html -

{{L "PasswordReset"}}

- -

{{L "PasswordResetInfoInEmail"}}

- -
-``` - -### Localization - -You can localize your Text Templates by choosing two different methods. - -- `Inline Localization` -- `Multiple Content Localization` - -#### Inline Localization - -An inline localized text template is using only one content resource, and it is using the [Abp.Localization](Localization.md) to get content in different languages/cultures. - -Example Inline Localized Text Template content: - -```html -{{L "ResetMyPassword"}} -``` - -#### Multiple Content Localization - -You can store your Text Templates for any culture in different content resource. - -> Example Multiple Content Localization - -> ForgotPasswordEmail / en.tpl - -```html -

Reset Your Password

- -

Hello, this is a password changing email.

- - -``` - -> ForgotPasswordEmail / tr.tpl - -```html -

Şifrenizi Değiştirin

- -

Merhaba, bu bir şifre yenileme e postasıdır.

- - -``` - -### Layout System - -It is typical to use the same layout for some different text templates. So, you can define a layout template. - -A text template can be layout for different text templates and also a text template may use a layout. - -A layout Text Template must have `{{content}}` area to render the child content. _(just like the `RenderBody()` in the MVC)_ - -Example Email Layout Text Template - -```html +````xml @@ -396,67 +311,42 @@ Example Email Layout Text Template {{content}} -``` - -## Definition of a Text Template - -First of all, create a class that inherited from `TemplateDefinitionProvider` abstract class and create `Define` method that derived from the base class. - -`Define` method requires a context that is `ITemplateDefinitionContext`. This `context` is a storage for template definitions and we will add our template definitions to the context. - -> **NOTE!** For default, ABP uses **Virtual File System** for text templates. Do not forget to register your files as an `Embedded Resource`. Please check the [Virtual File System Documentation](Virtual-File-System.md) for more details. +```` -> All given examples are for `Virtual File Text Template Definitions`. +* A layout template must have a **{{content}}** part as a place holder for the rendered child content. -```csharp -public class MyTemplateDefinitionProvider : TemplateDefinitionProvider - { - public override void Define(ITemplateDefinitionContext context) - { - // Layout Text Template - context.Add( - new TemplateDefinition( - name: "MySampleTemplateLayout", // Template Definition Name - isLayout: true - ).WithVirtualFilePath("/SampleTemplates/SampleTemplateLayout.tpl", true) - ); - - // Inline Localized Text Template - context.Add( - new TemplateDefinition( - name: "ForgotPasswordEmail", - localizationResource: typeof(MyLocalizationResource), - layout: TestTemplates.TestTemplateLayout1 - ).WithVirtualFilePath("/SampleTemplates/ForgotPasswordEmail.tpl", true) - ); - - // Multiple File Localized Text Template - context.Add( - new TemplateDefinition( - name: "ForgotPasswordEmail", - defaultCultureName: "en" - ).WithVirtualFilePath("/SampleTemplates/ForgotPasswordEmail", false) - ); - } - } -``` +The register your template in the template definition provider: -As you see in the given example, all Text Templates are added with `(ITemplateDefinitionContext)context.Add` method. This method requires a `TemplateDefinition` object. Then we call `WithVirtualFilePath` method with chaining for the describe where is the virtual files. +````csharp +context.Add( + new TemplateDefinition( + "EmailLayout", + isLayout: true //SET isLayout! + ).WithVirtualFilePath( + "/Demos/EmailLayout/EmailLayout.tpl", + isInlineLocalized: true + ) +); +```` -`WithVirtualFilePath` is requires one `tpl` file path for the `Inline Localized` Text Templates. If your Text Tempalte is `Multi Localized` you should create a folder and store each different culture files under that. So you can send the folder path as a parameter to `WithVirtualFilePath`. +Now, you can use this template as the layout of any other template: -> Inline Localized File +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en", + layout: "EmailLayout" //Set the LAYOUT + ).WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", + isInlineLocalized: false + ) +); +```` -``` -/ Folder / ForgotPasswordEmail.tpl -``` +## Global Context -> Multi Content Localization -``` -/ Folder / ForgotPasswordEmail / en.tpl -/ Folder / ForgotPasswordEmail / tr.tpl -``` ## Rendering From 3c6f1ebf8c163b4750d86bca4baeda67895a0bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 14 May 2020 01:09:10 +0300 Subject: [PATCH 43/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 44 ++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 84d1fff3d1..c722d2650e 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -10,6 +10,8 @@ It is very similar to an ASP.NET Core Razor View (or Page): RAZOR VIEW (or PAGE) + MODEL => HTML CONTENT +You can use the rendered output for any purpose, like sending emails or preparing some reports. + ### Example Here, a simple template: @@ -346,35 +348,31 @@ context.Add( ## Global Context +ABP passes the `model` that can be used to access to the model inside the template. You can pass more global variables if you need. +An example template content: +```` +A global object value: {{myGlobalObject}} +```` -## Rendering - -When one template is registered, it is easy to render and get the result with `ITemplateRenderer` service. - -`ITemplateRenderer` service has one method that named `RenderAsync` and to render your content and it is requires some parameters. - -- `templateName` (_string_) -- `model` (_object_) -- `cultureName` (_string_) -- `globalContext` (_dictionary_) - -`templateName` is exactly same with Template Definition Name. - -`model` is a dynamic object. This is using to put dynamic data into template. For more information, please look at [Scriban Documentation](https://github.com/lunet-io/scriban). - -`cultureName` is your destination rendering culture. When it is not exist, it will use the default culture. - -> If `cultureName` has a language tag it will try to find exact culture with tag, if it is not exist it will use the language family. - -> Example: If you try to render content with _"es-MX"_ it will search your template with _"es-MX"_ culture, when it fails to find, it will try to render _"es"_ culture content. If still can't find it will render the default culture content that you defined. - -`globalContext` = TODO - +This template assumes that that is a `myGlobalObject` object in the template rendering context. You can provide it like shown below: +````csharp +var result = await _templateRenderer.RenderAsync( + "GlobalContextUsage", + globalContext: new Dictionary + { + {"myGlobalObject", "TEST VALUE"} + } +); +```` +The rendering result will be: +```` +A global object value: TEST VALUE +```` From 4226f18186de85c3b521002dfaa125f0f4fcca3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 14 May 2020 01:25:32 +0300 Subject: [PATCH 44/51] Finalize the Text-Templating document. --- docs/en/Text-Templating.md | 77 +++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index c722d2650e..db2f25a807 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -4,11 +4,11 @@ ABP Framework provides a simple, yet efficient text template system. Text templating is used to dynamically render contents based on a template and a model (a data object): -TEMPLATE + MODEL => RENDERED CONTENT +***TEMPLATE + MODEL ==render==> RENDERED CONTENT*** It is very similar to an ASP.NET Core Razor View (or Page): -RAZOR VIEW (or PAGE) + MODEL => HTML CONTENT +*RAZOR VIEW (or PAGE) + MODEL ==render==> HTML CONTENT* You can use the rendered output for any purpose, like sending emails or preparing some reports. @@ -374,47 +374,72 @@ The rendering result will be: A global object value: TEST VALUE ```` +## Advanced Features +This section covers some internals and more advanced usages of the text templating system. -## Template Content Provider +### Template Content Provider -When you want to get your stored template content you can use `ITemplateContentProvider`. +`ITemplateRenderer` is used to render the template, which is what you want for most of the cases. However, you can use the `ITemplateContentProvider` to get the raw (not rendered) template contents. -`ITemplateContentProvider` has one method that named `GetContentOrNullAsync` with two different overriding, and it returns you a string of template content or null. (**without rendering**) +> `ITemplateContentProvider` is internally used by the `ITemplateRenderer` to get the raw template contents. -- `templateName` (_string_) or `templateDefinition` (_`TemplateDefinition`_) -- `cultureName` (_string_) -- `tryDefaults` (_bool_) -- `useCurrentCultureIfCultureNameIsNull` (_bool_) +Example: -### Usage +````csharp +public class TemplateContentDemo : ITransientDependency +{ + private readonly ITemplateContentProvider _templateContentProvider; -First parametres of `GetContentOrNullAsync` (`templateName` or `templateDefinition`) are required, the other three parametres can be null. + public TemplateContentDemo(ITemplateContentProvider templateContentProvider) + { + _templateContentProvider = templateContentProvider; + } -If you want to get exact culture content, set `tryDefaults` and `useCurrentCultureIfCultureNameIsNull` as a `false`. Because the `GetContentOrNullAsync` tries to return content of template. + public async Task RunAsync() + { + var result = await _templateContentProvider + .GetContentOrNullAsync("Hello"); -> Example Scenario + Console.WriteLine(result); + } +} +```` -> If you have a template content that culture "`es`", when you try to get template content with "`es-MX`" it will try to return first "`es-MX`", if it fails it will return "`es`" content. If you set `tryDefaults` and `useCurrentCultureIfCultureNameIsNull` as `false` it will return `null`. +The result will be the raw template content: -## Template Definition Manager +```` +Hello {{model.name}} :) +```` + +* `GetContentOrNullAsync` returns `null` if no content defined for the requested template. +* It can get a `cultureName` parameter that is used if template has different files for different cultures (see Multiple Contents Localization section above). -When you want to get your `Template Definitions`, you can use a singleton service that named `Template Definition Manager` in runtime. +### Template Content Contributor -To use it, inject `ITemplateDefinitionManager` service. +`ITemplateContentProvider` service uses `ITemplateContentContributor` implementations to find template contents. There is a single pre-implemented content contributor, `VirtualFileTemplateContentContributor`, which gets template contents from the virtual file system as described above. -It has three method that you can get your Template Definitions. +You can implement the `ITemplateContentContributor` to read raw template contents from another source. -- `Get` -- `GetOrNull` -- `GetAll` +Example: + +````csharp +public class MyTemplateContentProvider + : ITemplateContentContributor, ITransientDependency +{ + public async Task GetOrNullAsync(TemplateContentContributorContext context) + { + var templateName = context.TemplateDefinition.Name; -`Get` and `GetOrNull` requires a string parameter that name of template definition. `Get` will throw error when it is not exist but `GetOrNull` returns `null`. + //TODO: Try to find content from another source + return null; + } +} -`GetAll` returns you all registered template definitions. +```` -## Template Content Contributor +Return `null` if your source can not find the content, so `ITemplateContentProvider` fallbacks to the next contributor. -You can store your `Template Contents` in any resource. To make it, just create a class that implements `ITemplateContentContributor` interface. +### Template Definition Manager -`ITemplateContentContributor` has a one method that named `GetOrNullAsync`. This method must return content **without rendering** if that is exist in your resource or must return `null`. \ No newline at end of file +`ITemplateDefinitionManager` service can be used to get the template definitions (created by the template definition providers). \ No newline at end of file From 03e4913f10898b8b739c9bf043388963009c3cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 14 May 2020 01:35:48 +0300 Subject: [PATCH 45/51] add Text Templating to the navigation menu. --- docs/en/docs-nav.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 430fdf6b47..5f6203054e 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -179,6 +179,10 @@ "text": "Object to object mapping", "path": "Object-To-Object-Mapping.md" }, + { + "text": "Text Templating", + "path": "Text-Templating.md" + }, { "text": "Object Serialization" }, From 659baff96db010f1882c41efaa06be1f05a4fb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 14 May 2020 01:36:07 +0300 Subject: [PATCH 46/51] reference the sample text templating source code --- docs/en/Samples/Index.md | 3 +++ docs/en/Text-Templating.md | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/en/Samples/Index.md b/docs/en/Samples/Index.md index 0c9ce68886..a42d74c1c3 100644 --- a/docs/en/Samples/Index.md +++ b/docs/en/Samples/Index.md @@ -42,6 +42,9 @@ While there is no Razor Pages & MongoDB combination, you can check both document * [Source code](https://github.com/abpframework/abp-samples/tree/master/RabbitMqEventBus) * [Distributed event bus document](../Distributed-Event-Bus.md) * [RabbitMQ distributed event bus integration document](../Distributed-Event-Bus-RabbitMQ-Integration.md) +* **Text Templates Demo**: Shows different use cases of the text templating system. + * [Source code](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo) + * [Text templating documentation](../Text-Templating.md) * **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/aspnet-core/Authentication-Customization) * Related "[How To](../How-To/Index.md)" documents: diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index db2f25a807..925d26ed6f 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -42,6 +42,10 @@ Template rendering engine is very powerful; * You can define **layout templates** to be used as the layout while rendering other templates. * You can pass arbitrary objects to the template context (beside the model) for advanced scenarios. +### Source Code + +Get [the source code of the sample application](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo) developed and referred through this document. + ## Installation It is suggested to use the [ABP CLI](CLI.md) to install this package. @@ -442,4 +446,10 @@ Return `null` if your source can not find the content, so `ITemplateContentProvi ### Template Definition Manager -`ITemplateDefinitionManager` service can be used to get the template definitions (created by the template definition providers). \ No newline at end of file +`ITemplateDefinitionManager` service can be used to get the template definitions (created by the template definition providers). + +## See Also + +* [The source code of the sample application](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo) developed and referred through this document. +* [Localization system](Localization.md). +* [Virtual File System](Virtual-File-System.md). \ No newline at end of file From 7f1af28e77c57f2206c306a368ba1c408dc9174c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 14 May 2020 01:37:32 +0300 Subject: [PATCH 47/51] Update Text-Templating.md --- docs/en/Text-Templating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 925d26ed6f..9cad6598f1 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -257,7 +257,7 @@ Assuming that you want to send a welcome email to your users, but want to define First, create a folder and put your templates inside it, like `en.tpl`, `tr.tpl`... one for each culture you support: -![multiple-file-template](D:\Github\abp\docs\en\images\multiple-file-template.png) +![multiple-file-template](images/multiple-file-template.png) Then add your template definition in the template definition provider class: From 2143e904343707902f16fdf5da71bb5c63e54fc1 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Thu, 14 May 2020 02:11:02 +0300 Subject: [PATCH 48/51] fixes #3944 --- .../EfCoreIdentityUserRepository.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs index cab891bc94..dc94dd35e8 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/EfCoreIdentityUserRepository.cs @@ -19,7 +19,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task FindByNormalizedUserNameAsync( - string normalizedUserName, + string normalizedUserName, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -32,7 +32,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task> GetRoleNamesAsync( - Guid id, + Guid id, CancellationToken cancellationToken = default) { var query = from userRole in DbContext.Set() @@ -44,8 +44,8 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task FindByLoginAsync( - string loginProvider, - string providerKey, + string loginProvider, + string providerKey, bool includeDetails = true, CancellationToken cancellationToken = default) { @@ -77,7 +77,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task> GetListByNormalizedRoleNameAsync( - string normalizedRoleName, + string normalizedRoleName, bool includeDetails = false, CancellationToken cancellationToken = default) { @@ -97,10 +97,10 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task> GetListAsync( - string sorting = null, + string sorting = null, int maxResultCount = int.MaxValue, - int skipCount = 0, - string filter = null, + int skipCount = 0, + string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default) { @@ -110,7 +110,8 @@ namespace Volo.Abp.Identity.EntityFrameworkCore !filter.IsNullOrWhiteSpace(), u => u.UserName.Contains(filter) || - u.Email.Contains(filter) + u.Email.Contains(filter) || + (u.PhoneNumber != null && u.PhoneNumber.Contains(filter)) ) .OrderBy(sorting ?? nameof(IdentityUser.UserName)) .PageBy(skipCount, maxResultCount) @@ -131,7 +132,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore } public virtual async Task GetCountAsync( - string filter = null, + string filter = null, CancellationToken cancellationToken = default) { return await this.WhereIf( From 0881683b663842c9c36504526a3c94276b9a1a82 Mon Sep 17 00:00:00 2001 From: Alper Ebicoglu Date: Thu, 14 May 2020 02:32:45 +0300 Subject: [PATCH 49/51] closes #3946 --- .../Volo/Abp/Identity/Localization/tr.json | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json index 8b8b936c0a..1f1042549b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json @@ -71,6 +71,34 @@ "Permission:Delete": "Silme", "Permission:ChangePermissions": "İzinleri değiştirme", "Permission:UserManagement": "Kullanıcı yönetimi", - "Permission:UserLookup": "Kullanıcı sorgulama" + "Permission:UserLookup": "Kullanıcı sorgulama", + "DisplayName:Abp.Identity.Password.RequiredLength": "Uzunluk gerekli", + "DisplayName:Abp.Identity.Password.RequiredUniqueChars": "Tekil karakter gerekli", + "DisplayName:Abp.Identity.Password.RequireNonAlphanumeric": "Alfasayısal olmayan karakter gerekli", + "DisplayName:Abp.Identity.Password.RequireLowercase": "Küçük harf gerekli", + "DisplayName:Abp.Identity.Password.RequireUppercase": "Büyük harf gerekli", + "DisplayName:Abp.Identity.Password.RequireDigit": "Rakam gerekli", + "DisplayName:Abp.Identity.Lockout.AllowedForNewUsers": "Yeni kullanıcılar için aktif", + "DisplayName:Abp.Identity.Lockout.LockoutDuration": "Kilitli kalma süresi (saniye)", + "DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts": "Maksimum başarısız giriş denemesi", + "DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail": "Onaylı e-posta gerekli", + "DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Telefon numarası onayını etkin", + "DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Onaylı telefon numarası gerekli", + "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "Kullanıcı adı güncellenebilir", + "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "E-posta güncellenebilir", + "Description:Abp.Identity.Password.RequiredLength": "Minimum parola uzunluğu.", + "Description:Abp.Identity.Password.RequiredUniqueChars": Bir parolanın içermesi gereken minimum tekil karakter sayısı.", + "Description:Abp.Identity.Password.RequireNonAlphanumeric": "Parolaların alfasayısal olmayan bir karakter içermesi gerekiyorsa.", + "Description:Abp.Identity.Password.RequireLowercase": "Parolaların küçük harfli bir ASCII karakteri içermesi gerekiyorsa.", + "Description:Abp.Identity.Password.RequireUppercase": "Parolaların büyük harfli bir ASCII karakteri içermesi gerekiyorsa.", + "Description:Abp.Identity.Password.RequireDigit": "Parolaların bir rakam içermesi gerekiyorsa.", + "Description:Abp.Identity.Lockout.AllowedForNewUsers": "Yeni kullanıcılar kilitlenebilir.", + "Description:Abp.Identity.Lockout.LockoutDuration": "Kilitlenme olduğunda, ne kadar kilitli kalacağı.", + "Description:Abp.Identity.Lockout.MaxFailedAccessAttempts": "Kilitleme etkin olduğunda, kullanıcıya kilitlenmeden önce izin verilen başarısız giriş sayısı.", + "Description:Abp.Identity.SignIn.RequireConfirmedEmail": "Oturum açmak için onaylanmış bir e-posta adresinin gerekli olup olmadığı.", + "Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Oturum açmak için telefon numarası gerekli", + "Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Oturum açmak için onaylanmış bir telefon numarasının gerekli olup olmadığı.", + "Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Kullanıcı adının, kullanıcının kendisi tarafından güncellenebilirliği.", + "Description:Abp.Identity.User.IsEmailUpdateEnabled": "E-posta alanının, kullanıcının kendisi tarafından güncellenebilirliği" } } From 500f9ff8c87e10786a5e26a048305d4a91f42e84 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 14 May 2020 08:31:26 +0800 Subject: [PATCH 50/51] Fix tr.json. --- .../Volo/Abp/Identity/Localization/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json index 1f1042549b..92328ad596 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json +++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/Localization/tr.json @@ -87,7 +87,7 @@ "DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled": "Kullanıcı adı güncellenebilir", "DisplayName:Abp.Identity.User.IsEmailUpdateEnabled": "E-posta güncellenebilir", "Description:Abp.Identity.Password.RequiredLength": "Minimum parola uzunluğu.", - "Description:Abp.Identity.Password.RequiredUniqueChars": Bir parolanın içermesi gereken minimum tekil karakter sayısı.", + "Description:Abp.Identity.Password.RequiredUniqueChars": "Bir parolanın içermesi gereken minimum tekil karakter sayısı.", "Description:Abp.Identity.Password.RequireNonAlphanumeric": "Parolaların alfasayısal olmayan bir karakter içermesi gerekiyorsa.", "Description:Abp.Identity.Password.RequireLowercase": "Parolaların küçük harfli bir ASCII karakteri içermesi gerekiyorsa.", "Description:Abp.Identity.Password.RequireUppercase": "Parolaların büyük harfli bir ASCII karakteri içermesi gerekiyorsa.", From 23f304253674e2313c6793aa00bb284940ace568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 14 May 2020 04:11:03 +0300 Subject: [PATCH 51/51] Implemented SearchAsync for the user lookup --- .../Identity/IIdentityUserLookupAppService.cs | 3 ++ .../Abp/Identity/UserLookupSearchInputDto.cs | 11 ++++++ .../Identity/IdentityUserLookupAppService.cs | 17 +++++++++ ...sitoryExternalUserLookupServiceProvider.cs | 19 ++++++++++ .../HttpClientIdentityUserLookupService.cs | 19 ++++++++++ .../Identity/IdentityUserLookupController.cs | 8 +++++ .../IdentityUserLookupAppService_Tests.cs | 35 +++++++++++++++++-- .../IExternalUserLookupServiceProvider.cs | 9 ++++- .../Volo/Abp/Users/IUserLookupService.cs | 7 +++- .../Volo/Abp/Users/IUserRepository.cs | 8 +++++ .../Volo/Abp/Users/UserLookupService.cs | 13 +++++++ .../EfCoreAbpUserRepositoryBase.cs | 20 +++++++++++ .../Users/MongoDB/MongoUserRepositoryBase.cs | 17 +++++++++ 13 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupSearchInputDto.cs diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityUserLookupAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityUserLookupAppService.cs index 1ab79f400e..f3c3a3bc53 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityUserLookupAppService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IIdentityUserLookupAppService.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; using Volo.Abp.Users; @@ -10,5 +11,7 @@ namespace Volo.Abp.Identity Task FindByIdAsync(Guid id); Task FindByUserNameAsync(string userName); + + Task> SearchAsync(UserLookupSearchInputDto input); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupSearchInputDto.cs b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupSearchInputDto.cs new file mode 100644 index 0000000000..a2413e64ea --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/UserLookupSearchInputDto.cs @@ -0,0 +1,11 @@ +using Volo.Abp.Application.Dtos; + +namespace Volo.Abp.Identity +{ + public class UserLookupSearchInputDto : LimitedResultRequestDto, ISortedResultRequest + { + public string Sorting { get; set; } + + public string Filter { get; set; } + } +} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserLookupAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserLookupAppService.cs index 48d42c4579..696ce864ce 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserLookupAppService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserLookupAppService.cs @@ -1,6 +1,8 @@ using System; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; +using Volo.Abp.Application.Dtos; using Volo.Abp.Users; namespace Volo.Abp.Identity @@ -37,5 +39,20 @@ namespace Volo.Abp.Identity return new UserData(userData); } + + public async Task> SearchAsync(UserLookupSearchInputDto input) + { + var users = await UserLookupServiceProvider.SearchAsync( + input.Sorting, + input.Filter, + input.MaxResultCount + ); + + return new ListResultDto( + users + .Select(u => new UserData(u)) + .ToList() + ); + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRepositoryExternalUserLookupServiceProvider.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRepositoryExternalUserLookupServiceProvider.cs index 7fe729b325..9d8cd1dab4 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRepositoryExternalUserLookupServiceProvider.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserRepositoryExternalUserLookupServiceProvider.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; @@ -45,5 +47,22 @@ namespace Volo.Abp.Identity ) )?.ToAbpUserData(); } + + public virtual async Task> SearchAsync( + string sorting, + string filter, + int maxResultCount, + CancellationToken cancellationToken = default) + { + var users = await UserRepository.GetListAsync( + sorting: sorting, + maxResultCount: maxResultCount, + filter: filter, + includeDetails: false, + cancellationToken: cancellationToken + ); + + return users.Select(u => u.ToAbpUserData()).ToList(); + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientIdentityUserLookupService.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientIdentityUserLookupService.cs index ec1dca264f..6d5fdd0f92 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientIdentityUserLookupService.cs +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi.Client/Volo/Abp/Identity/HttpClientIdentityUserLookupService.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -25,5 +27,22 @@ namespace Volo.Abp.Identity { return await UserLookupAppService.FindByUserNameAsync(userName); } + + public async Task> SearchAsync( + string sorting, + string filter, + int maxResultCount, + CancellationToken cancellationToken = default) + { + var result = await UserLookupAppService.SearchAsync( + new UserLookupSearchInputDto + { + Filter = filter, + MaxResultCount = maxResultCount + } + ); + + return result.Items.Cast().ToList(); + } } } diff --git a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs index 2f8011bf7f..70dcaaa300 100644 --- a/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs +++ b/modules/identity/src/Volo.Abp.Identity.HttpApi/Volo/Abp/Identity/IdentityUserLookupController.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Application.Dtos; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Users; @@ -32,5 +33,12 @@ namespace Volo.Abp.Identity { return LookupAppService.FindByUserNameAsync(userName); } + + [HttpGet] + [Route("search")] + public Task> SearchAsync(UserLookupSearchInputDto input) + { + return LookupAppService.SearchAsync(input); + } } } diff --git a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserLookupAppService_Tests.cs b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserLookupAppService_Tests.cs index 227977cf15..fc5a18cbd3 100644 --- a/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserLookupAppService_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.Application.Tests/Volo/Abp/Identity/IdentityUserLookupAppService_Tests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Shouldly; @@ -52,5 +50,38 @@ namespace Volo.Abp.Identity var user = await _identityUserLookupAppService.FindByUserNameAsync(Guid.NewGuid().ToString()); user.ShouldBeNull(); } + + [Fact] + public async Task Search_Without_Filter_And_Sorting() + { + var result = await _identityUserLookupAppService.SearchAsync(new UserLookupSearchInputDto()); + result.Items.Count.ShouldBeGreaterThanOrEqualTo(3); + result.Items.ShouldContain(u => u.UserName == "john.nash"); + } + + [Fact] + public async Task Search_With_Filter() + { + var result = await _identityUserLookupAppService.SearchAsync( + new UserLookupSearchInputDto + { + Filter = "a" + } + ); + + result.Items.Count.ShouldBeGreaterThanOrEqualTo(2); + result.Items.ShouldContain(u => u.UserName == "john.nash"); + result.Items.ShouldContain(u => u.UserName == "david"); + + result = await _identityUserLookupAppService.SearchAsync( + new UserLookupSearchInputDto + { + Filter = "neo" + } + ); + + result.Items.Count.ShouldBeGreaterThanOrEqualTo(1); + result.Items.ShouldContain(u => u.UserName == "neo"); + } } } diff --git a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs index 8f498e32c3..b973033be6 100644 --- a/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs +++ b/modules/users/src/Volo.Abp.Users.Abstractions/Volo/Abp/Users/IExternalUserLookupServiceProvider.cs @@ -1,13 +1,20 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace Volo.Abp.Users { - public interface IExternalUserLookupServiceProvider //TODO: Consider to inherit from IUserLookupService + public interface IExternalUserLookupServiceProvider { Task FindByIdAsync(Guid id, CancellationToken cancellationToken = default); Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default); + + Task> SearchAsync( + string sorting, + string filter, + int maxResultCount, + CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs index 2c50d7795f..44e84eb350 100644 --- a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserLookupService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -11,6 +12,10 @@ namespace Volo.Abp.Users Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default); - //TODO: More... + Task> SearchAsync( + string sorting, + string filter, + int maxResultCount, + CancellationToken cancellationToken = default); } } diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs index 14eea6a3fe..fc0412c734 100644 --- a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/IUserRepository.cs @@ -13,5 +13,13 @@ namespace Volo.Abp.Users Task FindByUserNameAsync(string userName, CancellationToken cancellationToken = default); Task> GetListAsync(IEnumerable ids, CancellationToken cancellationToken = default); + + Task> SearchAsync( + string sorting = null, + int maxResultCount = int.MaxValue, + int skipCount = 0, + string filter = null, + CancellationToken cancellationToken = default + ); } } \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs index 2c28d71b80..7621f3f63e 100644 --- a/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs +++ b/modules/users/src/Volo.Abp.Users.Domain/Volo/Abp/Users/UserLookupService.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -138,6 +140,17 @@ namespace Volo.Abp.Users return await _userRepository.FindAsync(externalUser.Id, cancellationToken: cancellationToken); } + public async Task> SearchAsync(string sorting, string filter, int maxResultCount, CancellationToken cancellationToken = default) + { + if (ExternalUserLookupServiceProvider != null) + { + return await ExternalUserLookupServiceProvider.SearchAsync(sorting, filter, maxResultCount, cancellationToken); + } + + var localUsers = await _userRepository.SearchAsync(sorting, maxResultCount, 0, filter, cancellationToken); + return localUsers.Cast().ToList(); + } + protected abstract TUser CreateUser(IUserData externalUser); private async Task WithNewUowAsync(Func func) diff --git a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs index 5d0958101c..f5eb013c93 100644 --- a/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs +++ b/modules/users/src/Volo.Abp.Users.EntityFrameworkCore/Volo/Abp/Users/EntityFrameworkCore/EfCoreAbpUserRepositoryBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq.Dynamic.Core; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -28,5 +29,24 @@ namespace Volo.Abp.Users.EntityFrameworkCore { return await DbSet.Where(u => ids.Contains(u.Id)).ToListAsync(GetCancellationToken(cancellationToken)); } + + public async Task> SearchAsync( + string sorting = null, + int maxResultCount = Int32.MaxValue, + int skipCount = 0, + string filter = null, + CancellationToken cancellationToken = default) + { + return await DbSet + .WhereIf( + !filter.IsNullOrWhiteSpace(), + u => + u.UserName.Contains(filter) || + u.Email.Contains(filter) + ) + .OrderBy(sorting ?? nameof(IUser.UserName)) + .PageBy(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } } } \ No newline at end of file diff --git a/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs b/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs index 50d892d8dd..3cb6e567ae 100644 --- a/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs +++ b/modules/users/src/Volo.Abp.Users.MongoDB/Volo/Abp/Users/MongoDB/MongoUserRepositoryBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Dynamic.Core; using System.Threading; using System.Threading.Tasks; using MongoDB.Driver; @@ -29,5 +30,21 @@ namespace Volo.Abp.Users.MongoDB { return await GetMongoQueryable().Where(u => ids.Contains(u.Id)).ToListAsync(GetCancellationToken(cancellationToken)); } + + public async Task> SearchAsync(string sorting = null, int maxResultCount = Int32.MaxValue, int skipCount = 0, string filter = null, + CancellationToken cancellationToken = default) + { + return await GetMongoQueryable() + .WhereIf>( + !filter.IsNullOrWhiteSpace(), + u => + u.UserName.Contains(filter) || + u.Email.Contains(filter) + ) + .OrderBy(sorting ?? nameof(IUserData.UserName)) + .As>() + .PageBy>(skipCount, maxResultCount) + .ToListAsync(GetCancellationToken(cancellationToken)); + } } } \ No newline at end of file