From 919a2e2e01892f70722a4eb6ba8fd19016e33da1 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Fri, 8 May 2020 23:18:08 +0300 Subject: [PATCH 01/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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 0e492d23df67ff3a8cd7f1e4f850ccf684bc1697 Mon Sep 17 00:00:00 2001 From: Ahmet Date: Tue, 12 May 2020 21:05:25 +0300 Subject: [PATCH 07/16] 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 08/16] 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 09/16] 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 10/16] 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 11/16] 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 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 12/16] 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 13/16] 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 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 Date: Thu, 14 May 2020 00:54:12 +0300 Subject: [PATCH 14/16] 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 15/16] 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 16/16] 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