Browse Source

Merge pull request #3792 from abpframework/text-templating

Abp.TextTemplating package is created
pull/3809/head
Halil İbrahim Kalkan 6 years ago
committed by GitHub
parent
commit
83871b6c32
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      common.DotSettings
  2. 16
      framework/Volo.Abp.sln
  3. 14
      framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
  4. 9
      framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj
  5. 51
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs
  6. 18
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs
  7. 24
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs
  8. 1
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl
  9. 42
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs
  10. 22
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs
  11. 36
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs
  12. 38
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs
  13. 22
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs
  14. 74
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs
  15. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs
  16. 18
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs
  17. 121
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs
  18. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs
  19. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs
  20. 15
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs
  21. 7
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs
  22. 11
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs
  23. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs
  24. 2
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl
  25. 1
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl
  26. 4
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs
  27. 15
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs
  28. 19
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs
  29. 68
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs
  30. 34
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs
  31. 11
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs
  32. 2
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs
  33. 18
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs
  34. 3
      framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml
  35. 30
      framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd
  36. 26
      framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj
  37. 46
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs
  38. 16
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs
  39. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs
  40. 22
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs
  41. 13
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs
  42. 17
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs
  43. 11
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs
  44. 25
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs
  45. 27
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs
  46. 170
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs
  47. 88
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs
  48. 43
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs
  49. 31
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs
  50. 116
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs
  51. 19
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs
  52. 155
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs
  53. 25
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs
  54. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs
  55. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs
  56. 83
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs
  57. 17
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs
  58. 28
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs
  59. 46
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs
  60. 24
      framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs
  61. 7
      framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj
  62. 10
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs
  63. 65
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs
  64. 54
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs
  65. 10
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs
  66. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json
  67. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json
  68. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json
  69. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json
  70. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json
  71. 24
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs
  72. 4
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl
  73. 4
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl
  74. 4
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl
  75. 1
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl
  76. 63
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs
  77. 25
      framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj
  78. 24
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs
  79. 12
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs
  80. 32
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs
  81. 6
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs
  82. 4
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json
  83. 4
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json
  84. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl
  85. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl
  86. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl
  87. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl
  88. 41
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs
  89. 108
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs
  90. 32
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs
  91. 9
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs
  92. 49
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs
  93. 1
      nupkg/common.ps1

6
common.DotSettings

@ -20,5 +20,11 @@
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Async/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Generate/=Overrides/Options/=Mutable/@EntryIndexedValue">False</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SQL/@EntryIndexedValue">SQL</s:String>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/HideTypeNameHintsWhenTypeNameIsEvidentFromVariableName/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowMethodReturnTypeNameHints/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForImplicitlyTypedVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLambdaExpressionParameters/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForLinqQueryRangeVariables/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/TypeNameHintsOptions/ShowTypeNameHintsForPatternMatchingExpressions/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Volo/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

16
framework/Volo.Abp.sln

@ -277,7 +277,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Http.Client.Identi
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending", "src\Volo.Abp.ObjectExtending\Volo.Abp.ObjectExtending.csproj", "{D1815C77-16D6-4F99-8814-69065CD89FB3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.ObjectExtending.Tests", "test\Volo.Abp.ObjectExtending.Tests\Volo.Abp.ObjectExtending.Tests.csproj", "{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.ObjectExtending.Tests", "test\Volo.Abp.ObjectExtending.Tests\Volo.Abp.ObjectExtending.Tests.csproj", "{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating", "src\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj", "{9E53F91F-EACD-4191-A487-E727741F1311}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TextTemplating.Tests", "test\Volo.Abp.TextTemplating.Tests\Volo.Abp.TextTemplating.Tests.csproj", "{251C7FD3-D313-4BCE-8068-352EC7EEA275}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -829,6 +833,14 @@ Global
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5}.Release|Any CPU.Build.0 = Release|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E53F91F-EACD-4191-A487-E727741F1311}.Release|Any CPU.Build.0 = Release|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Debug|Any CPU.Build.0 = Debug|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.ActiveCfg = Release|Any CPU
{251C7FD3-D313-4BCE-8068-352EC7EEA275}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -970,6 +982,8 @@ Global
{E1963439-2BE5-4DB5-8438-2A9A792A1ADA} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{D1815C77-16D6-4F99-8814-69065CD89FB3} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{17F8CA89-D9A2-4863-A5BD-B8E4D2901FD5} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{9E53F91F-EACD-4191-A487-E727741F1311} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{251C7FD3-D313-4BCE-8068-352EC7EEA275} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

14
framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs

@ -10,7 +10,12 @@ namespace Volo.Abp.Localization
{
Check.NotNull(culture, nameof(culture));
return Use(new CultureInfo(culture), uiCulture == null ? null : new CultureInfo(uiCulture));
return Use(
new CultureInfo(culture),
uiCulture == null
? null
: new CultureInfo(uiCulture)
);
}
public static IDisposable Use([NotNull] CultureInfo culture, CultureInfo uiCulture = null)
@ -29,5 +34,12 @@ namespace Volo.Abp.Localization
CultureInfo.CurrentUICulture = currentUiCulture;
});
}
public static string GetBaseCultureName(string cultureName)
{
return cultureName.Contains("-")
? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal))
: cultureName;
}
}
}

9
framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj

@ -14,24 +14,21 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\Emailing\Templates\DefaultEmailTemplates\*\*.tpl" />
<None Remove="Volo\Abp\Emailing\Templates\DefaultEmailTemplates\*\*.tpl" />
</ItemGroup>
<ItemGroup>
<Content Remove="Volo\Abp\Emailing\Localization\*.json" />
<EmbeddedResource Include="Volo\Abp\Emailing\Localization\*.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Scriban" Version="2.1.1" />
<Content Remove="Volo\Abp\Emailing\Templates\**\*.tpl" />
<EmbeddedResource Include="Volo\Abp\Emailing\Templates\**\*.tpl" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.BackgroundJobs.Abstractions\Volo.Abp.BackgroundJobs.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
<ProjectReference Include="..\Volo.Abp.Settings\Volo.Abp.Settings.csproj" />
<ProjectReference Include="..\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj" />
<ProjectReference Include="..\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" />
</ItemGroup>

51
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs

@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Emailing.Localization;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Settings;
using Volo.Abp.TextTemplating;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Emailing
@ -15,15 +12,11 @@ namespace Volo.Abp.Emailing
typeof(AbpSettingsModule),
typeof(AbpVirtualFileSystemModule),
typeof(AbpBackgroundJobsAbstractionsModule),
typeof(AbpLocalizationModule)
typeof(AbpLocalizationModule),
typeof(AbpTextTemplatingModule)
)]
public class AbpEmailingModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddDefinitionProviders(context.Services);
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
@ -43,41 +36,5 @@ namespace Volo.Abp.Emailing
options.AddJob<BackgroundEmailSendingJob>();
});
}
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();
services.OnRegistred(context =>
{
if (typeof(IEmailTemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
});
services.Configure<AbpEmailTemplateOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
using (var scope = context.ServiceProvider.CreateScope())
{
var emailTemplateDefinitionManager =
scope.ServiceProvider.GetRequiredService<IEmailTemplateDefinitionManager>();
foreach (var templateDefinition in emailTemplateDefinitionManager.GetAll())
{
foreach (var contributor in templateDefinition.Contributors)
{
contributor.Initialize(new EmailTemplateInitializationContext(templateDefinition, scope.ServiceProvider));
}
}
}
}
}
}

18
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs

@ -1,18 +0,0 @@
using Volo.Abp.Collections;
namespace Volo.Abp.Emailing.Templates
{
public class AbpEmailTemplateOptions
{
public string DefaultLayout { get; set; }
public ITypeList<IEmailTemplateDefinitionProvider> DefinitionProviders { get; }
public AbpEmailTemplateOptions()
{
DefaultLayout = StandardEmailTemplates.DefaultLayout;
DefinitionProviders = new TypeList<IEmailTemplateDefinitionProvider>();
}
}
}

24
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs

@ -1,16 +1,26 @@
using Volo.Abp.Emailing.Templates.VirtualFiles;
using Volo.Abp.TextTemplating;
namespace Volo.Abp.Emailing.Templates
{
public class DefaultEmailTemplateProvider : EmailTemplateDefinitionProvider
public class DefaultEmailTemplateProvider : TemplateDefinitionProvider
{
public override void Define(IEmailTemplateDefinitionContext context)
public override void Define(ITemplateDefinitionContext context)
{
context.Add(new EmailTemplateDefinition(StandardEmailTemplates.DefaultLayout, defaultCultureName: "en", isLayout: true, layout: null)
.AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout"));
context.Add(
new TemplateDefinition(
StandardEmailTemplates.Layout,
defaultCultureName: "en",
isLayout: true
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Layout")
);
context.Add(new EmailTemplateDefinition(StandardEmailTemplates.SimpleMessage, defaultCultureName: "en")
.AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message"));
context.Add(
new TemplateDefinition(
StandardEmailTemplates.Message,
defaultCultureName: "en",
layout: StandardEmailTemplates.Layout
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Message")
);
}
}
}

1
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl

@ -1 +0,0 @@
{{message}}

42
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs

@ -1,42 +0,0 @@
using System.Text;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplate
{
public EmailTemplateDefinition Definition { get; }
public string Content => ContentBuilder.ToString();
protected StringBuilder ContentBuilder { get; set; }
public EmailTemplate(string content, EmailTemplateDefinition definition)
{
ContentBuilder = new StringBuilder(content);
Definition = definition;
}
public virtual void SetLayout(EmailTemplate layoutTemplate)
{
if (!layoutTemplate.Definition.IsLayout)
{
throw new AbpException($"Given template is not a layout template: {layoutTemplate.Definition.Name}");
}
var newStrBuilder = new StringBuilder(layoutTemplate.Content);
newStrBuilder.Replace("{{#content}}", ContentBuilder.ToString());
ContentBuilder = newStrBuilder;
}
public virtual void SetContent(string content)
{
ContentBuilder = new StringBuilder(content);
}
public virtual void Replace(string name, string value)
{
ContentBuilder.Replace("{{" + name + "}}", value);
}
}
}

22
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs

@ -1,22 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplateContributorList : List<IEmailTemplateContributor>
{
public string GetOrNull(string cultureName)
{
foreach (var contributor in this.AsQueryable().Reverse())
{
var templateString = contributor.GetOrNull(cultureName);
if (templateString != null)
{
return templateString;
}
}
return null;
}
}
}

36
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs

@ -1,36 +0,0 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplateDefinition
{
public const string DefaultLayoutPlaceHolder = "_";
public string Name { get; }
public bool IsLayout { get; }
public string Layout { get; set; }
public Type LocalizationResource { get; set; }
public EmailTemplateContributorList Contributors { get; }
public string DefaultCultureName { get; }
public bool SingleTemplateFile { get; }
public EmailTemplateDefinition([NotNull] string name, Type localizationResource = null, bool isLayout = false,
string layout = DefaultLayoutPlaceHolder, string defaultCultureName = null, bool singleTemplateFile = false)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
LocalizationResource = localizationResource;
Contributors = new EmailTemplateContributorList();
IsLayout = isLayout;
Layout = layout;
DefaultCultureName = defaultCultureName;
SingleTemplateFile = singleTemplateFile;
}
}
}

38
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs

@ -1,38 +0,0 @@
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplateDefinitionContext : IEmailTemplateDefinitionContext
{
protected Dictionary<string, EmailTemplateDefinition> EmailTemplates { get; }
public EmailTemplateDefinitionContext(Dictionary<string, EmailTemplateDefinition> emailTemplates)
{
EmailTemplates = emailTemplates;
}
public virtual EmailTemplateDefinition GetOrNull(string name)
{
return EmailTemplates.GetOrDefault(name);
}
public virtual IReadOnlyList<EmailTemplateDefinition> GetAll()
{
return EmailTemplates.Values.ToImmutableList();
}
public virtual void Add(params EmailTemplateDefinition[] definitions)
{
if (definitions.IsNullOrEmpty())
{
return;
}
foreach (var definition in definitions)
{
EmailTemplates[definition.Name] = definition;
}
}
}
}

22
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs

@ -1,22 +0,0 @@
using System.Collections.Generic;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplateDefinitionDictionary : Dictionary<string, EmailTemplateDefinition>
{
public EmailTemplateDefinitionDictionary Add(EmailTemplateDefinition emailTemplateDefinition)
{
if (ContainsKey(emailTemplateDefinition.Name))
{
throw new AbpException(
"There is already an email template definition with given name: " +
emailTemplateDefinition.Name
);
}
this[emailTemplateDefinition.Name] = emailTemplateDefinition;
return this;
}
}
}

74
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs

@ -1,74 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplateDefinitionManager : IEmailTemplateDefinitionManager, ISingletonDependency
{
protected Lazy<IDictionary<string, EmailTemplateDefinition>> EmailTemplateDefinitions { get; }
protected AbpEmailTemplateOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
public EmailTemplateDefinitionManager(
IOptions<AbpEmailTemplateOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
EmailTemplateDefinitions =
new Lazy<IDictionary<string, EmailTemplateDefinition>>(CreateEmailTemplateDefinitions, true);
}
public virtual EmailTemplateDefinition Get(string name)
{
Check.NotNull(name, nameof(name));
var template = GetOrNull(name);
if (template == null)
{
throw new AbpException("Undefined template: " + name);
}
return template;
}
public virtual IReadOnlyList<EmailTemplateDefinition> GetAll()
{
return EmailTemplateDefinitions.Value.Values.ToImmutableList();
}
public virtual EmailTemplateDefinition GetOrNull(string name)
{
return EmailTemplateDefinitions.Value.GetOrDefault(name);
}
protected virtual IDictionary<string, EmailTemplateDefinition> CreateEmailTemplateDefinitions()
{
var templates = new Dictionary<string, EmailTemplateDefinition>();
using (var scope = ServiceProvider.CreateScope())
{
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IEmailTemplateDefinitionProvider)
.ToList();
foreach (var provider in providers)
{
provider.Define(new EmailTemplateDefinitionContext(templates));
}
}
return templates;
}
}
}

9
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs

@ -1,9 +0,0 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Emailing.Templates
{
public abstract class EmailTemplateDefinitionProvider : IEmailTemplateDefinitionProvider, ITransientDependency
{
public abstract void Define(IEmailTemplateDefinitionContext context);
}
}

18
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs

@ -1,18 +0,0 @@
using System;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplateInitializationContext
{
public EmailTemplateDefinition EmailTemplateDefinition { get; }
public IServiceProvider ServiceProvider { get; }
public EmailTemplateInitializationContext(EmailTemplateDefinition emailTemplateDefinition,
IServiceProvider serviceProvider)
{
EmailTemplateDefinition = emailTemplateDefinition;
ServiceProvider = serviceProvider;
}
}
}

121
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs

@ -1,121 +0,0 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace Volo.Abp.Emailing.Templates
{
public class EmailTemplateProvider : IEmailTemplateProvider, ITransientDependency
{
protected IEmailTemplateDefinitionManager EmailTemplateDefinitionManager;
protected ITemplateLocalizer TemplateLocalizer { get; }
protected AbpEmailTemplateOptions Options { get; }
protected IStringLocalizerFactory StringLocalizerFactory;
public EmailTemplateProvider(IEmailTemplateDefinitionManager emailTemplateDefinitionManager,
ITemplateLocalizer templateLocalizer, IStringLocalizerFactory stringLocalizerFactory,
IOptions<AbpEmailTemplateOptions> options)
{
EmailTemplateDefinitionManager = emailTemplateDefinitionManager;
TemplateLocalizer = templateLocalizer;
StringLocalizerFactory = stringLocalizerFactory;
Options = options.Value;
}
public async Task<EmailTemplate> GetAsync(string name)
{
return await GetAsync(name, CultureInfo.CurrentUICulture.Name);
}
public async Task<EmailTemplate> GetAsync(string name, string cultureName)
{
return await GetInternalAsync(name, cultureName);
}
protected virtual async Task<EmailTemplate> GetInternalAsync(string name, string cultureName)
{
var emailTemplateDefinition = EmailTemplateDefinitionManager.GetOrNull(name);
if (emailTemplateDefinition == null)
{
// TODO: Localized message
throw new AbpException($"email template {name} not definition");
}
var emailTemplateString = emailTemplateDefinition.Contributors.GetOrNull(cultureName);
if (emailTemplateString == null && emailTemplateDefinition.DefaultCultureName != null)
{
emailTemplateString =
emailTemplateDefinition.Contributors.GetOrNull(emailTemplateDefinition.DefaultCultureName);
if (emailTemplateString != null)
{
cultureName = emailTemplateDefinition.DefaultCultureName;
}
}
if (emailTemplateString != null)
{
var emailTemplate = new EmailTemplate(emailTemplateString, emailTemplateDefinition);
await SetLayoutAsync(emailTemplateDefinition, emailTemplate, cultureName);
if (emailTemplateDefinition.SingleTemplateFile)
{
await LocalizeAsync(emailTemplateDefinition, emailTemplate, cultureName);
}
return emailTemplate;
}
// TODO: Localized message
throw new AbpException($"{cultureName} template not exist!");
}
protected virtual async Task SetLayoutAsync(EmailTemplateDefinition emailTemplateDefinition,
EmailTemplate emailTemplate, string cultureName)
{
var layout = emailTemplateDefinition.Layout;
if (layout.IsNullOrWhiteSpace())
{
return;
}
if (layout == EmailTemplateDefinition.DefaultLayoutPlaceHolder)
{
layout = Options.DefaultLayout;
}
var layoutTemplate = await GetInternalAsync(layout, cultureName);
emailTemplate.SetLayout(layoutTemplate);
}
protected virtual Task LocalizeAsync(EmailTemplateDefinition emailTemplateDefinition,
EmailTemplate emailTemplate, string cultureName)
{
if (emailTemplateDefinition.LocalizationResource == null)
{
return Task.CompletedTask;
}
var localizer = StringLocalizerFactory.Create(emailTemplateDefinition.LocalizationResource);
if (cultureName != null)
{
using (CultureHelper.Use(new CultureInfo(cultureName)))
{
emailTemplate.SetContent(TemplateLocalizer.Localize(localizer, emailTemplate.Content));
}
}
else
{
emailTemplate.SetContent(
TemplateLocalizer.Localize(localizer, emailTemplate.Content)
);
}
return Task.CompletedTask;
}
}
}

9
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs

@ -1,9 +0,0 @@
namespace Volo.Abp.Emailing.Templates
{
public interface IEmailTemplateContributor
{
void Initialize(EmailTemplateInitializationContext context);
string GetOrNull(string cultureName);
}
}

9
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs

@ -1,9 +0,0 @@
namespace Volo.Abp.Emailing.Templates
{
public interface IEmailTemplateDefinitionContext
{
EmailTemplateDefinition GetOrNull(string name);
void Add(params EmailTemplateDefinition[] definitions);
}
}

15
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs

@ -1,15 +0,0 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Emailing.Templates
{
public interface IEmailTemplateDefinitionManager
{
[NotNull]
EmailTemplateDefinition Get([NotNull] string name);
IReadOnlyList<EmailTemplateDefinition> GetAll();
EmailTemplateDefinition GetOrNull(string name);
}
}

7
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs

@ -1,7 +0,0 @@
namespace Volo.Abp.Emailing.Templates
{
public interface IEmailTemplateDefinitionProvider
{
void Define(IEmailTemplateDefinitionContext context);
}
}

11
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs

@ -1,11 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.Emailing.Templates
{
public interface IEmailTemplateProvider
{
Task<EmailTemplate> GetAsync(string name);
Task<EmailTemplate> GetAsync(string name, string cultureName);
}
}

9
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs

@ -1,9 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.Emailing.Templates
{
public interface ITemplateRender
{
Task<string> RenderAsync(string template, object model = null);
}
}

2
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl → framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl

@ -4,6 +4,6 @@
<meta charset="utf-8" />
</head>
<body>
{{#content}}
{{content}}
</body>
</html>

1
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl

@ -0,0 +1 @@
{{model.message}}

4
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs

@ -2,7 +2,7 @@
{
public static class StandardEmailTemplates
{
public const string DefaultLayout = "Abp.DefaultLayout";
public const string SimpleMessage = "Abp.SimpleMessage";
public const string Layout = "Abp.StandardEmailTemplates.Layout";
public const string Message = "Abp.StandardEmailTemplates.Message";
}
}

15
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs

@ -1,15 +0,0 @@
using System.Threading.Tasks;
using Scriban;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Emailing.Templates
{
public class TemplateRender : ITemplateRender, ITransientDependency
{
public async Task<string> RenderAsync(string template, object model = null)
{
var scribanTemplate = Template.Parse(template);
return await scribanTemplate.RenderAsync(model);
}
}
}

19
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs

@ -1,19 +0,0 @@
namespace Volo.Abp.Emailing.Templates.VirtualFiles
{
public static class EmailTemplateDefinitionExtensions
{
public static EmailTemplateDefinition AddTemplateVirtualFile(
this EmailTemplateDefinition emailTemplateDefinition, string path)
{
emailTemplateDefinition.Contributors.Add(new SingleVirtualFileEmailTemplateContributor(path));
return emailTemplateDefinition;
}
public static EmailTemplateDefinition AddTemplateVirtualFiles(
this EmailTemplateDefinition emailTemplateDefinition, string path)
{
emailTemplateDefinition.Contributors.Add(new MultipleVirtualFilesEmailTemplateContributor(path));
return emailTemplateDefinition;
}
}
}

68
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs

@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Emailing.Templates.VirtualFiles
{
public class MultipleVirtualFilesEmailTemplateContributor : IEmailTemplateContributor
{
private readonly string _virtualPath;
private IVirtualFileProvider _virtualFileProvider;
private Dictionary<string, string> _templateDictionary;
private readonly object _syncObj = new object();
public MultipleVirtualFilesEmailTemplateContributor(string virtualPath)
{
_virtualPath = virtualPath;
}
public void Initialize(EmailTemplateInitializationContext context)
{
_virtualFileProvider = context.ServiceProvider.GetRequiredService<IVirtualFileProvider>();
}
public string GetOrNull(string cultureName)
{
return GetTemplateDictionary().GetOrDefault(cultureName);
}
private Dictionary<string, string> GetTemplateDictionary()
{
var dictionaries = _templateDictionary;
if (dictionaries != null)
{
return dictionaries;
}
lock (_syncObj)
{
dictionaries = _templateDictionary;
if (dictionaries != null)
{
return dictionaries;
}
_templateDictionary = new Dictionary<string, string>();
foreach (var file in _virtualFileProvider.GetDirectoryContents(_virtualPath))
{
if (file.IsDirectory)
{
continue;
}
// TODO: How to normalize file names?
_templateDictionary.Add(file.Name.RemovePostFix(".tpl"), file.ReadAsString());
}
dictionaries = _templateDictionary;
}
return dictionaries;
}
}
}

34
framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs

@ -1,34 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.Emailing.Templates.VirtualFiles
{
public class SingleVirtualFileEmailTemplateContributor : IEmailTemplateContributor
{
private readonly string _virtualPath;
private IVirtualFileProvider _virtualFileProvider;
public SingleVirtualFileEmailTemplateContributor(string virtualPath)
{
_virtualPath = virtualPath;
}
public void Initialize(EmailTemplateInitializationContext context)
{
_virtualFileProvider = context.ServiceProvider.GetRequiredService<IVirtualFileProvider>();
}
public string GetOrNull(string cultureName)
{
var file = _virtualFileProvider.GetFileInfo(_virtualPath);
if (file == null || !file.Exists || file.IsDirectory)
{
return null;
}
return file.ReadAsString();
}
}
}

11
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs

@ -104,7 +104,7 @@ namespace Volo.Abp.Localization
//Try to get from same language dictionary (without country code)
if (cultureName.Contains("-")) //Example: "tr-TR" (length=5)
{
var strLang = Resource.Contributors.GetOrNull(GetBaseCultureName(cultureName), name);
var strLang = Resource.Contributors.GetOrNull(CultureHelper.GetBaseCultureName(cultureName), name);
if (strLang != null)
{
return strLang;
@ -168,7 +168,7 @@ namespace Volo.Abp.Localization
//Overwrite all strings from the language based on country culture
if (cultureName.Contains("-"))
{
Resource.Contributors.Fill(GetBaseCultureName(cultureName), allStrings);
Resource.Contributors.Fill(CultureHelper.GetBaseCultureName(cultureName), allStrings);
}
}
@ -178,12 +178,7 @@ namespace Volo.Abp.Localization
return allStrings.Values.ToImmutableList();
}
protected virtual string GetBaseCultureName(string cultureName)
{
return cultureName.Contains("-")
? cultureName.Left(cultureName.IndexOf("-", StringComparison.Ordinal))
: cultureName;
}
public class CultureWrapperStringLocalizer : IStringLocalizer, IStringLocalizerSupportsInheritance
{

2
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.Localization
{
public LocalizedString GetOrNull(string cultureName, string name)
{
foreach (var contributor in this.AsQueryable().Reverse()) //TODO: Reverse?
foreach (var contributor in this.AsQueryable().Reverse())
{
var localString = contributor.GetOrNull(cultureName, name);
if (localString != null)

18
framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs

@ -1,18 +0,0 @@
using System.Text.RegularExpressions;
using Microsoft.Extensions.Localization;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Localization
{
public class TemplateLocalizer : ITemplateLocalizer, ITransientDependency
{
public string Localize(IStringLocalizer localizer, string text)
{
return new Regex("\\{\\{#L:.+?\\}\\}")
.Replace(
text,
match => localizer[match.Value.Substring(5, match.Length - 7)]
);
}
}
}

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

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

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

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

26
framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.TextTemplating</AssemblyName>
<PackageId>Volo.Abp.TextTemplating</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Scriban" Version="2.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj" />
<ProjectReference Include="..\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" />
</ItemGroup>
</Project>

46
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.TextTemplating
{
[DependsOn(
typeof(AbpVirtualFileSystemModule),
typeof(AbpLocalizationAbstractionsModule)
)]
public class AbpTextTemplatingModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AutoAddProvidersAndContributors(context.Services);
}
private static void AutoAddProvidersAndContributors(IServiceCollection services)
{
var definitionProviders = new List<Type>();
var contentContributors = new List<Type>();
services.OnRegistred(context =>
{
if (typeof(ITemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType))
{
definitionProviders.Add(context.ImplementationType);
}
if (typeof(ITemplateContentContributor).IsAssignableFrom(context.ImplementationType))
{
contentContributors.Add(context.ImplementationType);
}
});
services.Configure<AbpTextTemplatingOptions>(options =>
{
options.DefinitionProviders.AddIfNotContains(definitionProviders);
options.ContentContributors.AddIfNotContains(contentContributors);
});
}
}
}

16
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs

@ -0,0 +1,16 @@
using Volo.Abp.Collections;
namespace Volo.Abp.TextTemplating
{
public class AbpTextTemplatingOptions
{
public ITypeList<ITemplateDefinitionProvider> DefinitionProviders { get; }
public ITypeList<ITemplateContentContributor> ContentContributors { get; }
public AbpTextTemplatingOptions()
{
DefinitionProviders = new TypeList<ITemplateDefinitionProvider>();
ContentContributors = new TypeList<ITemplateContentContributor>();
}
}
}

9
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.TextTemplating
{
public interface ITemplateContentContributor
{
Task<string> GetOrNullAsync(TemplateContentContributorContext context);
}
}

22
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs

@ -0,0 +1,22 @@
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.TextTemplating
{
public interface ITemplateContentProvider
{
Task<string> GetContentOrNullAsync(
[NotNull] string templateName,
[CanBeNull] string cultureName = null,
bool tryDefaults = true,
bool useCurrentCultureIfCultureNameIsNull = true
);
Task<string> GetContentOrNullAsync(
[NotNull] TemplateDefinition templateDefinition,
[CanBeNull] string cultureName = null,
bool tryDefaults = true,
bool useCurrentCultureIfCultureNameIsNull = true
);
}
}

13
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Volo.Abp.TextTemplating
{
public interface ITemplateDefinitionContext
{
IReadOnlyList<TemplateDefinition> GetAll(string name);
TemplateDefinition GetOrNull(string name);
void Add(params TemplateDefinition[] definitions);
}
}

17
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs

@ -0,0 +1,17 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.TextTemplating
{
public interface ITemplateDefinitionManager
{
[NotNull]
TemplateDefinition Get([NotNull] string name);
[NotNull]
IReadOnlyList<TemplateDefinition> GetAll();
[CanBeNull]
TemplateDefinition GetOrNull(string name);
}
}

11
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs

@ -0,0 +1,11 @@
namespace Volo.Abp.TextTemplating
{
public interface ITemplateDefinitionProvider
{
void PreDefine(ITemplateDefinitionContext context);
void Define(ITemplateDefinitionContext context);
void PostDefine(ITemplateDefinitionContext context);
}
}

25
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using JetBrains.Annotations;
namespace Volo.Abp.TextTemplating
{
public interface ITemplateRenderer
{
/// <summary>
/// Renders a text template.
/// </summary>
/// <param name="templateName">The template name</param>
/// <param name="model">An optional model object that is used in the template</param>
/// <param name="cultureName">Culture name. Uses the <see cref="CultureInfo.CurrentUICulture"/> if not specified</param>
/// <param name="globalContext">A dictionary which can be used to import global objects to the template</param>
/// <returns></returns>
Task<string> RenderAsync(
[NotNull] string templateName,
[CanBeNull] object model = null,
[CanBeNull] string cultureName = null,
[CanBeNull] Dictionary<string, object> globalContext = null
);
}
}

27
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs

@ -0,0 +1,27 @@
using System;
using JetBrains.Annotations;
namespace Volo.Abp.TextTemplating
{
public class TemplateContentContributorContext
{
[NotNull]
public TemplateDefinition TemplateDefinition { get; }
[NotNull]
public IServiceProvider ServiceProvider { get; }
[CanBeNull]
public string Culture { get; }
public TemplateContentContributorContext(
[NotNull] TemplateDefinition templateDefinition,
[NotNull] IServiceProvider serviceProvider,
[CanBeNull] string culture)
{
TemplateDefinition = Check.NotNull(templateDefinition, nameof(templateDefinition));
ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider));
Culture = culture;
}
}
}

170
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs

@ -0,0 +1,170 @@
using System;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace Volo.Abp.TextTemplating
{
public class TemplateContentProvider : ITemplateContentProvider, ITransientDependency
{
public IHybridServiceScopeFactory ServiceScopeFactory { get; }
public AbpTextTemplatingOptions Options { get; }
private readonly ITemplateDefinitionManager _templateDefinitionManager;
public TemplateContentProvider(
ITemplateDefinitionManager templateDefinitionManager,
IHybridServiceScopeFactory serviceScopeFactory,
IOptions<AbpTextTemplatingOptions> options)
{
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
_templateDefinitionManager = templateDefinitionManager;
}
public virtual Task<string> GetContentOrNullAsync(
[NotNull] string templateName,
[CanBeNull] string cultureName = null,
bool tryDefaults = true,
bool useCurrentCultureIfCultureNameIsNull = true)
{
var template = _templateDefinitionManager.Get(templateName);
return GetContentOrNullAsync(template, cultureName);
}
public virtual async Task<string> GetContentOrNullAsync(
[NotNull] TemplateDefinition templateDefinition,
[CanBeNull] string cultureName = null,
bool tryDefaults = true,
bool useCurrentCultureIfCultureNameIsNull = true)
{
Check.NotNull(templateDefinition, nameof(templateDefinition));
if (!Options.ContentContributors.Any())
{
throw new AbpException(
$"No template content contributor was registered. Use {nameof(AbpTextTemplatingOptions)} to register contributors!"
);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
string templateString = null;
if (cultureName == null && useCurrentCultureIfCultureNameIsNull)
{
cultureName = CultureInfo.CurrentUICulture.Name;
}
var contributors = CreateTemplateContentContributors(scope.ServiceProvider);
//Try to get from the requested culture
templateString = await GetContentOrNullAsync(
contributors,
new TemplateContentContributorContext(
templateDefinition,
scope.ServiceProvider,
cultureName
)
);
if (templateString != null)
{
return templateString;
}
if (!tryDefaults)
{
return null;
}
//Try to get from same culture without country code
if (cultureName != null && cultureName.Contains("-")) //Example: "tr-TR"
{
templateString = await GetContentOrNullAsync(
contributors,
new TemplateContentContributorContext(
templateDefinition,
scope.ServiceProvider,
CultureHelper.GetBaseCultureName(cultureName)
)
);
if (templateString != null)
{
return templateString;
}
}
if (templateDefinition.IsInlineLocalized)
{
//Try to get culture independent content
templateString = await GetContentOrNullAsync(
contributors,
new TemplateContentContributorContext(
templateDefinition,
scope.ServiceProvider,
null
)
);
if (templateString != null)
{
return templateString;
}
}
else
{
//Try to get from default culture
if (templateDefinition.DefaultCultureName != null)
{
templateString = await GetContentOrNullAsync(
contributors,
new TemplateContentContributorContext(
templateDefinition,
scope.ServiceProvider,
templateDefinition.DefaultCultureName
)
);
if (templateString != null)
{
return templateString;
}
}
}
}
//Not found
return null;
}
protected virtual ITemplateContentContributor[] CreateTemplateContentContributors(IServiceProvider serviceProvider)
{
return Options.ContentContributors
.Select(type => (ITemplateContentContributor)serviceProvider.GetRequiredService(type))
.Reverse()
.ToArray();
}
protected virtual async Task<string> GetContentOrNullAsync(
ITemplateContentContributor[] contributors,
TemplateContentContributorContext context)
{
foreach (var contributor in contributors)
{
var templateString = await contributor.GetOrNullAsync(context);
if (templateString != null)
{
return templateString;
}
}
return null;
}
}
}

88
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Localization;
namespace Volo.Abp.TextTemplating
{
public class TemplateDefinition
{
public const int MaxNameLength = 128;
[NotNull]
public string Name { get; }
[NotNull]
public ILocalizableString DisplayName
{
get => _displayName;
set
{
Check.NotNull(value, nameof(value));
_displayName = value;
}
}
private ILocalizableString _displayName;
public bool IsLayout { get; }
[CanBeNull]
public string Layout { get; set; }
[CanBeNull]
public Type LocalizationResource { get; set; }
public bool IsInlineLocalized { get; set; }
[CanBeNull]
public string DefaultCultureName { get; }
/// <summary>
/// Gets/sets a key-value on the <see cref="Properties"/>.
/// </summary>
/// <param name="name">Name of the property</param>
/// <returns>
/// Returns the value in the <see cref="Properties"/> dictionary by given <see cref="name"/>.
/// Returns null if given <see cref="name"/> is not present in the <see cref="Properties"/> dictionary.
/// </returns>
[CanBeNull]
public object this[string name]
{
get => Properties.GetOrDefault(name);
set => Properties[name] = value;
}
/// <summary>
/// Can be used to get/set custom properties for this feature.
/// </summary>
[NotNull]
public Dictionary<string, object> Properties { get; }
public TemplateDefinition(
[NotNull] string name,
[CanBeNull] Type localizationResource = null,
[CanBeNull] ILocalizableString displayName = null,
bool isLayout = false,
string layout = null,
string defaultCultureName = null)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), MaxNameLength);
LocalizationResource = localizationResource;
DisplayName = displayName ?? new FixedLocalizableString(Name);
IsLayout = isLayout;
Layout = layout;
DefaultCultureName = defaultCultureName;
Properties = new Dictionary<string, object>();
}
/// <summary>
/// Sets a property in the <see cref="Properties"/> dictionary.
/// This is a shortcut for nested calls on this object.
/// </summary>
public virtual TemplateDefinition WithProperty(string key, object value)
{
Properties[key] = value;
return this;
}
}
}

43
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs

@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Volo.Abp.TextTemplating
{
public class TemplateDefinitionContext : ITemplateDefinitionContext
{
protected Dictionary<string, TemplateDefinition> Templates { get; }
public TemplateDefinitionContext(Dictionary<string, TemplateDefinition> templates)
{
Templates = templates;
}
public IReadOnlyList<TemplateDefinition> GetAll(string name)
{
return Templates.Values.ToImmutableList();
}
public virtual TemplateDefinition GetOrNull(string name)
{
return Templates.GetOrDefault(name);
}
public virtual IReadOnlyList<TemplateDefinition> GetAll()
{
return Templates.Values.ToImmutableList();
}
public virtual void Add(params TemplateDefinition[] definitions)
{
if (definitions.IsNullOrEmpty())
{
return;
}
foreach (var definition in definitions)
{
Templates[definition.Name] = definition;
}
}
}
}

31
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs

@ -0,0 +1,31 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.TextTemplating.VirtualFiles;
namespace Volo.Abp.TextTemplating
{
public static class TemplateDefinitionExtensions
{
public static TemplateDefinition WithVirtualFilePath(
[NotNull] this TemplateDefinition templateDefinition,
[NotNull] string virtualPath)
{
Check.NotNull(templateDefinition, nameof(templateDefinition));
return templateDefinition.WithProperty(
VirtualFileTemplateContentContributor.VirtualPathPropertyName,
virtualPath
);
}
public static string GetVirtualFilePathOrNull(
[NotNull] this TemplateDefinition templateDefinition)
{
Check.NotNull(templateDefinition, nameof(templateDefinition));
return templateDefinition
.Properties
.GetOrDefault(VirtualFileTemplateContentContributor.VirtualPathPropertyName) as string;
}
}
}

116
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.TextTemplating
{
public class TemplateDefinitionManager : ITemplateDefinitionManager, ISingletonDependency
{
protected Lazy<IDictionary<string, TemplateDefinition>> TemplateDefinitions { get; }
protected AbpTextTemplatingOptions Options { get; }
protected IServiceProvider ServiceProvider { get; }
public TemplateDefinitionManager(
IOptions<AbpTextTemplatingOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
TemplateDefinitions =
new Lazy<IDictionary<string, TemplateDefinition>>(CreateTextTemplateDefinitions, true);
}
public virtual TemplateDefinition Get(string name)
{
Check.NotNull(name, nameof(name));
var template = GetOrNull(name);
if (template == null)
{
throw new AbpException("Undefined template: " + name);
}
return template;
}
public virtual IReadOnlyList<TemplateDefinition> GetAll()
{
return TemplateDefinitions.Value.Values.ToImmutableList();
}
public virtual TemplateDefinition GetOrNull(string name)
{
return TemplateDefinitions.Value.GetOrDefault(name);
}
protected virtual IDictionary<string, TemplateDefinition> CreateTextTemplateDefinitions()
{
var templates = new Dictionary<string, TemplateDefinition>();
using (var scope = ServiceProvider.CreateScope())
{
var providers = Options
.DefinitionProviders
.Select(p => scope.ServiceProvider.GetRequiredService(p) as ITemplateDefinitionProvider)
.ToList();
var context = new TemplateDefinitionContext(templates);
foreach (var provider in providers)
{
provider.PreDefine(context);
}
foreach (var provider in providers)
{
provider.Define(context);
}
foreach (var provider in providers)
{
provider.PostDefine(context);
}
SetIsInlineLocalized(
templates,
scope.ServiceProvider
);
}
return templates;
}
protected virtual void SetIsInlineLocalized(
Dictionary<string, TemplateDefinition> templates,
IServiceProvider serviceProvider)
{
var virtualFileProvider = serviceProvider.GetRequiredService<IVirtualFileProvider>();
foreach (var templateDefinition in templates.Values)
{
var virtualPath = templateDefinition.GetVirtualFilePathOrNull();
if (virtualPath == null)
{
continue;
}
var fileInfo = virtualFileProvider.GetFileInfo(virtualPath);
if (!fileInfo.Exists)
{
throw new AbpException("Could not find a file/folder at the location: " + virtualPath);
}
templateDefinition.IsInlineLocalized = !fileInfo.IsDirectory;
}
}
}
}

19
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs

@ -0,0 +1,19 @@
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.TextTemplating
{
public abstract class TemplateDefinitionProvider : ITemplateDefinitionProvider, ITransientDependency
{
public virtual void PreDefine(ITemplateDefinitionContext context)
{
}
public abstract void Define(ITemplateDefinitionContext context);
public virtual void PostDefine(ITemplateDefinitionContext context)
{
}
}
}

155
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs

@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Localization;
using Scriban;
using Scriban.Runtime;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace Volo.Abp.TextTemplating
{
public class TemplateRenderer : ITemplateRenderer, ITransientDependency
{
private readonly ITemplateContentProvider _templateContentProvider;
private readonly ITemplateDefinitionManager _templateDefinitionManager;
private readonly IStringLocalizerFactory _stringLocalizerFactory;
public TemplateRenderer(
ITemplateContentProvider templateContentProvider,
ITemplateDefinitionManager templateDefinitionManager,
IStringLocalizerFactory stringLocalizerFactory)
{
_templateContentProvider = templateContentProvider;
_templateDefinitionManager = templateDefinitionManager;
_stringLocalizerFactory = stringLocalizerFactory;
}
public virtual async Task<string> RenderAsync(
[NotNull] string templateName,
[CanBeNull] object model = null,
[CanBeNull] string cultureName = null,
[CanBeNull] Dictionary<string, object> globalContext = null)
{
Check.NotNullOrWhiteSpace(templateName, nameof(templateName));
if (globalContext == null)
{
globalContext = new Dictionary<string, object>();
}
if (cultureName == null)
{
return await RenderInternalAsync(
templateName,
globalContext,
model
);
}
else
{
using (CultureHelper.Use(cultureName))
{
return await RenderInternalAsync(
templateName,
globalContext,
model
);
}
}
}
protected virtual async Task<string> RenderInternalAsync(
string templateName,
Dictionary<string, object> globalContext,
object model = null)
{
var templateDefinition = _templateDefinitionManager.Get(templateName);
var renderedContent = await RenderSingleTemplateAsync(
templateDefinition,
globalContext,
model
);
if (templateDefinition.Layout != null)
{
globalContext["content"] = renderedContent;
renderedContent = await RenderInternalAsync(
templateDefinition.Layout,
globalContext
);
}
return renderedContent;
}
protected virtual async Task<string> RenderSingleTemplateAsync(
TemplateDefinition templateDefinition,
Dictionary<string, object> globalContext,
object model = null)
{
var rawTemplateContent = await _templateContentProvider
.GetContentOrNullAsync(
templateDefinition
);
return await RenderTemplateContentWithScribanAsync(
templateDefinition,
rawTemplateContent,
globalContext,
model
);
}
protected virtual async Task<string> RenderTemplateContentWithScribanAsync(
TemplateDefinition templateDefinition,
string templateContent,
Dictionary<string, object> globalContext,
object model = null)
{
var context = CreateScribanTemplateContext(
templateDefinition,
globalContext,
model
);
return await Template
.Parse(templateContent)
.RenderAsync(context);
}
protected virtual TemplateContext CreateScribanTemplateContext(
TemplateDefinition templateDefinition,
Dictionary<string, object> globalContext,
object model = null)
{
var context = new TemplateContext();
var scriptObject = new ScriptObject();
scriptObject.Import(globalContext);
if (model != null)
{
scriptObject["model"] = model;
}
if (templateDefinition.LocalizationResource != null)
{
var localizer = _stringLocalizerFactory.Create(templateDefinition.LocalizationResource);
scriptObject.Import(
"L",
new Func<string, string>(
name => localizer[name]
)
);
}
context.PushGlobal(scriptObject);
return context;
}
}
}

25
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs

@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
namespace Volo.Abp.TextTemplating.VirtualFiles
{
public class FileInfoLocalizedTemplateContentReader : ILocalizedTemplateContentReader
{
private string _content;
public async Task ReadContentsAsync(IFileInfo fileInfo)
{
_content = await fileInfo.ReadAsStringAsync();
}
public string GetContentOrNull(string culture)
{
if (culture == null)
{
return _content;
}
return null;
}
}
}

9
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs

@ -0,0 +1,9 @@
using JetBrains.Annotations;
namespace Volo.Abp.TextTemplating.VirtualFiles
{
public interface ILocalizedTemplateContentReader
{
public string GetContentOrNull([CanBeNull] string culture);
}
}

9
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Volo.Abp.TextTemplating.VirtualFiles
{
public interface ILocalizedTemplateContentReaderFactory
{
Task<ILocalizedTemplateContentReader> CreateAsync(TemplateDefinition templateDefinition);
}
}

83
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.TextTemplating.VirtualFiles
{
public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency
{
private readonly IVirtualFileProvider _virtualFileProvider;
private readonly Dictionary<string, ILocalizedTemplateContentReader> _readerCache;
private readonly ReaderWriterLockSlim _lock;
public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider)
{
_virtualFileProvider = virtualFileProvider;
_readerCache = new Dictionary<string, ILocalizedTemplateContentReader>();
_lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}
public async Task<ILocalizedTemplateContentReader> CreateAsync(TemplateDefinition templateDefinition)
{
_lock.EnterUpgradeableReadLock();
try
{
var reader = _readerCache.GetOrDefault(templateDefinition.Name);
if (reader != null)
{
return reader;
}
_lock.EnterWriteLock();
try
{
reader = await CreateInternalAsync(templateDefinition);
_readerCache[templateDefinition.Name] = reader;
return reader;
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
protected virtual async Task<ILocalizedTemplateContentReader> CreateInternalAsync(
TemplateDefinition templateDefinition)
{
var virtualPath = templateDefinition.GetVirtualFilePathOrNull();
if (virtualPath == null)
{
return NullLocalizedTemplateContentReader.Instance;
}
var fileInfo = _virtualFileProvider.GetFileInfo(virtualPath);
if (!fileInfo.Exists)
{
throw new AbpException("Could not find a file/folder at the location: " + virtualPath);
}
if (fileInfo.IsDirectory)
{
var folderReader = new VirtualFolderLocalizedTemplateContentReader();
await folderReader.ReadContentsAsync(_virtualFileProvider, virtualPath);
return folderReader;
}
else //File
{
var singleFileReader = new FileInfoLocalizedTemplateContentReader();
await singleFileReader.ReadContentsAsync(fileInfo);
return singleFileReader;
}
}
}
}

17
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs

@ -0,0 +1,17 @@
namespace Volo.Abp.TextTemplating.VirtualFiles
{
public class NullLocalizedTemplateContentReader : ILocalizedTemplateContentReader
{
public static NullLocalizedTemplateContentReader Instance { get; } = new NullLocalizedTemplateContentReader();
private NullLocalizedTemplateContentReader()
{
}
public string GetContentOrNull(string culture)
{
return null;
}
}
}

28
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs

@ -0,0 +1,28 @@
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.TextTemplating.VirtualFiles
{
public class VirtualFileTemplateContentContributor : ITemplateContentContributor, ITransientDependency
{
public const string VirtualPathPropertyName = "VirtualPath";
private readonly ILocalizedTemplateContentReaderFactory _localizedTemplateContentReaderFactory;
public VirtualFileTemplateContentContributor(
ILocalizedTemplateContentReaderFactory localizedTemplateContentReaderFactory)
{
_localizedTemplateContentReaderFactory = localizedTemplateContentReaderFactory;
}
public virtual async Task<string> GetOrNullAsync(TemplateContentContributorContext context)
{
var localizedReader = await _localizedTemplateContentReaderFactory
.CreateAsync(context.TemplateDefinition);
return localizedReader.GetContentOrNull(
context.Culture
);
}
}
}

46
framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.TextTemplating.VirtualFiles
{
public class VirtualFolderLocalizedTemplateContentReader : ILocalizedTemplateContentReader
{
private Dictionary<string, string> _dictionary;
public async Task ReadContentsAsync(
IVirtualFileProvider virtualFileProvider,
string virtualPath)
{
_dictionary = new Dictionary<string, string>();
var directoryInfo = virtualFileProvider.GetFileInfo(virtualPath);
if (!directoryInfo.IsDirectory)
{
throw new AbpException("Given virtual path is not a folder: " + virtualPath);
}
foreach (var file in virtualFileProvider.GetDirectoryContents(virtualPath))
{
if (file.IsDirectory)
{
continue;
}
_dictionary.Add(file.Name.RemovePostFix(".tpl"), await file.ReadAsStringAsync());
}
}
public string GetContentOrNull(string cultureName)
{
if (cultureName == null)
{
return null;
}
return _dictionary.GetOrDefault(cultureName);
}
}
}

24
framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs

@ -18,6 +18,14 @@ namespace Microsoft.Extensions.FileProviders
return fileInfo.ReadAsString(Encoding.UTF8);
}
/// <summary>
/// Reads file content as string using <see cref="Encoding.UTF8"/> encoding.
/// </summary>
public static Task<string> ReadAsStringAsync([NotNull] this IFileInfo fileInfo)
{
return fileInfo.ReadAsStringAsync(Encoding.UTF8);
}
/// <summary>
/// Reads file content as string using the given <paramref name="encoding"/>.
/// </summary>
@ -34,6 +42,22 @@ namespace Microsoft.Extensions.FileProviders
}
}
/// <summary>
/// Reads file content as string using the given <paramref name="encoding"/>.
/// </summary>
public static async Task<string> ReadAsStringAsync([NotNull] this IFileInfo fileInfo, Encoding encoding)
{
Check.NotNull(fileInfo, nameof(fileInfo));
using (var stream = fileInfo.CreateReadStream())
{
using (var streamReader = new StreamReader(stream, encoding, true))
{
return await streamReader.ReadToEndAsync();
}
}
}
/// <summary>
/// Reads file content as byte[].
/// </summary>

7
framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj

@ -7,13 +7,6 @@
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\Emailing\TestTemplates\*\*.tpl" />
<None Remove="Volo\Abp\Emailing\TestTemplates\*\*.tpl" />
<EmbeddedResource Include="Volo\Abp\Emailing\Localization\*.json" />
<None Remove="Volo\Abp\Emailing\Localization\*.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Emailing\Volo.Abp.Emailing.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />

10
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs

@ -1,6 +1,4 @@
using Volo.Abp.Autofac;
using Volo.Abp.Emailing.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
@ -18,14 +16,6 @@ namespace Volo.Abp.Emailing
{
options.FileSets.AddEmbedded<AbpEmailingTestModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<AbpEmailingTestResource>()
.AddVirtualJson("/Volo/Abp/Emailing/Localization");
});
}
}
}

65
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs

@ -1,65 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Emailing
{
public class EmailTemplateRender_Tests : AbpIntegratedTest<AbpEmailingTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
private readonly ITemplateRender _templateRender;
public EmailTemplateRender_Tests()
{
_templateRender = GetRequiredService<ITemplateRender>();
}
[Fact]
public async Task RenderAsync()
{
var template = "Hello {{email}} {{ for order in orders }}{{ order.id }}:{{ order.name }},{{ end }}";
var model = new ModelClass
{
Email = "john@abp.io",
Orders = new List<ModelClass.Order>
{
new ModelClass.Order
{
Id = "1",
Name = "iphone"
},
new ModelClass.Order
{
Id = "2",
Name = "ipad"
}
}
};
var result = await _templateRender.RenderAsync(template, model);
result.ShouldBe("Hello john@abp.io 1:iphone,2:ipad,");
}
public class ModelClass
{
public string Email { get; set; }
public List<Order> Orders { get; set; }
public class Order
{
public string Id { get; set; }
public string Name { get; set; }
}
}
}
}

54
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs

@ -1,54 +0,0 @@
using System;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.Emailing
{
public class EmailTemplateStore_Tests : AbpIntegratedTest<AbpEmailingTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
private readonly IEmailTemplateProvider _emailTemplateProvider;
public EmailTemplateStore_Tests()
{
_emailTemplateProvider = GetRequiredService<IEmailTemplateProvider>();
}
[Fact]
public async Task Should_Get_Registered_Template()
{
var template = await _emailTemplateProvider.GetAsync("template1", "tr");
template.Content.ShouldContain("Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın.");
}
[Fact]
public async Task Should_Get_Default_Culture_Template()
{
var template = await _emailTemplateProvider.GetAsync("template1", "zh-Hans");
template.Content.ShouldContain("Please confirm your email address by clicking the link below.");
}
[Fact]
public async Task Should_Get_Registered_Template_With_Layout()
{
var template = await _emailTemplateProvider.GetAsync("template2", "en");
template.Content.ShouldContain($"<body>{Environment.NewLine} " + "Please confirm your email address by clicking the link below.");
}
[Fact]
public async Task Should_Get_Registered_Template_With_Localize()
{
var template = await _emailTemplateProvider.GetAsync("template3", "tr");
template.Content.ShouldContain("Merhaba Abp");
}
}
}

10
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs

@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Volo.Abp.Emailing.Localization
{
public class AbpEmailingTestResource
{
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json

@ -1,6 +0,0 @@
{
"culture": "cs",
"texts": {
"hello": "ahoj"
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json

@ -1,6 +0,0 @@
{
"culture": "pl",
"texts": {
"hello": "witaj"
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json

@ -1,6 +0,0 @@
{
"culture": "pt-BR",
"texts": {
"hello": "Olá"
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json

@ -1,6 +0,0 @@
{
"culture": "vi",
"texts": {
"hello": "xin chào"
}
}

6
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json

@ -1,6 +0,0 @@
{
"culture": "zh-Hant",
"texts": {
"hello": "哈囉"
}
}

24
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs

@ -1,24 +0,0 @@
using Volo.Abp.Emailing.Localization;
using Volo.Abp.Emailing.Templates;
using Volo.Abp.Emailing.Templates.VirtualFiles;
namespace Volo.Abp.Emailing
{
public class TestEmailTemplateProvider : EmailTemplateDefinitionProvider
{
public override void Define(IEmailTemplateDefinitionContext context)
{
var template1 = new EmailTemplateDefinition("template1", defaultCultureName: "en", layout: null)
.AddTemplateVirtualFiles("/Volo/Abp/Emailing/TestTemplates/Template1");
context.Add(template1);
var template2 = new EmailTemplateDefinition("template2", layout: StandardEmailTemplates.DefaultLayout)
.AddTemplateVirtualFiles("/Volo/Abp/Emailing/TestTemplates/Template2");
context.Add(template2);
var template3 = new EmailTemplateDefinition("template3", layout: null, singleTemplateFile: true, localizationResource: typeof(AbpEmailingTestResource))
.AddTemplateVirtualFile("/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl");
context.Add(template3);
}
}
}

4
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl

@ -1,4 +0,0 @@
Please confirm your email address by clicking the link below.
We may need to send you critical information about our service and it is important that we have an accurate email address.
<a href="#">Confirm email address</a>

4
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl

@ -1,4 +0,0 @@
Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın.
Size hizmetimizle ilgili kritik bilgileri göndermemiz gerekebilir ve doğru bir e-posta adresimizin olması önemlidir.
<a href="#">E-posta adresini onayla</a>

4
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl

@ -1,4 +0,0 @@
Please confirm your email address by clicking the link below.
We may need to send you critical information about our service and it is important that we have an accurate email address.
<a href="#">Confirm email address</a>

1
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl

@ -1 +0,0 @@
{{#L:hello}} Abp

63
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs

@ -1,63 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Shouldly;
using Volo.Abp.Localization.TestResources.Source;
using Volo.Abp.Modularity;
using Volo.Abp.Testing;
using Volo.Abp.VirtualFileSystem;
using Xunit;
namespace Volo.Abp.Localization
{
public class TemplateLocalizer_Tests : AbpIntegratedTest<TemplateLocalizer_Tests.TestModule>
{
private readonly ITemplateLocalizer _templateLocalizer;
private readonly IStringLocalizer<LocalizationTestResource> _testResource;
public TemplateLocalizer_Tests()
{
_testResource = GetRequiredService<IStringLocalizer<LocalizationTestResource>>();
_templateLocalizer = GetRequiredService<ITemplateLocalizer>();
}
[Fact]
public void Should_Localize()
{
using (CultureHelper.Use("en"))
{
_templateLocalizer.Localize(_testResource, "<p>{{#L:CarPlural}} <b>{{#L:Universe}}</b></p>")
.ShouldBe("<p>Cars <b>Universe</b></p>");
}
}
[Fact]
public void Should_Work_Even_If_No_Text_To_Localize()
{
using (CultureHelper.Use("en"))
{
_templateLocalizer.Localize(_testResource, "<p>test</p>")
.ShouldBe("<p>test</p>");
}
}
[DependsOn(typeof(AbpTestBaseModule))]
[DependsOn(typeof(AbpLocalizationModule))]
public class TestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpLocalization_Tests.TestModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<LocalizationTestResource>("en")
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Source");
});
}
}
}
}

25
framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\common.test.props" />
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Volo\Abp\TextTemplating\Localization\**\*.json" />
<None Remove="Volo\Abp\TextTemplating\Localization\**\*.json" />
<EmbeddedResource Include="Volo\Abp\TextTemplating\SampleTemplates\**\*.tpl" />
<None Remove="Volo\Abp\TextTemplating\SampleTemplates\**\*.tpl" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Localization\Volo.Abp.Localization.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>
</Project>

24
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs

@ -0,0 +1,24 @@
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;
namespace Volo.Abp.TextTemplating
{
public class AbpTextTemplatingOptions_Tests : AbpTextTemplatingTestBase
{
private readonly AbpTextTemplatingOptions _options;
public AbpTextTemplatingOptions_Tests()
{
_options = GetRequiredService<IOptions<AbpTextTemplatingOptions>>().Value;
}
[Fact]
public void Should_Auto_Add_TemplateDefinitionProviders_To_Options()
{
_options
.DefinitionProviders
.ShouldContain(typeof(TestTemplateDefinitionProvider));
}
}
}

12
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs

@ -0,0 +1,12 @@
using Volo.Abp.Testing;
namespace Volo.Abp.TextTemplating
{
public abstract class AbpTextTemplatingTestBase : AbpIntegratedTest<AbpTextTemplatingTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
}

32
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs

@ -0,0 +1,32 @@
using Volo.Abp.Autofac;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.TextTemplating.Localization;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.TextTemplating
{
[DependsOn(
typeof(AbpTextTemplatingModule),
typeof(AbpTestBaseModule),
typeof(AbpAutofacModule),
typeof(AbpLocalizationModule)
)]
public class AbpTextTemplatingTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpTextTemplatingTestModule>("Volo.Abp.TextTemplating");
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<TestLocalizationSource>("en")
.AddVirtualJson("/Localization");
});
}
}
}

6
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs

@ -0,0 +1,6 @@
namespace Volo.Abp.TextTemplating.Localization
{
public class TestLocalizationSource
{
}
}

4
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json → framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json

@ -1,6 +1,6 @@
{
"culture": "en",
"texts": {
"hello": "hello"
}
"HelloText": "Hello"
}
}

4
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json → framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json

@ -1,6 +1,6 @@
{
"culture": "tr",
"texts": {
"hello": "Merhaba"
}
"HelloText": "Merhaba"
}
}

1
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl

@ -0,0 +1 @@
{{L "HelloText"}}. Please click to the following link to get an email to reset your password!

1
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl

@ -0,0 +1 @@
*BEGIN*{{content}}*END*

1
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl

@ -0,0 +1 @@
Welcome {{model.name}} to the abp.io!

1
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl

@ -0,0 +1 @@
Merhaba {{model.name}}, abp.io'ya hoşgeldiniz!

41
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs

@ -0,0 +1,41 @@
using Shouldly;
using Xunit;
namespace Volo.Abp.TextTemplating
{
public class TemplateDefinitionTests : AbpTextTemplatingTestBase
{
private readonly ITemplateDefinitionManager _templateDefinitionManager;
public TemplateDefinitionTests()
{
_templateDefinitionManager = GetRequiredService<ITemplateDefinitionManager>();
}
[Fact]
public void Should_Retrieve_Template_Definition_By_Name()
{
var welcomeEmailTemplate = _templateDefinitionManager.Get(TestTemplates.WelcomeEmail);
welcomeEmailTemplate.Name.ShouldBe(TestTemplates.WelcomeEmail);
welcomeEmailTemplate.IsInlineLocalized.ShouldBeFalse();
var forgotPasswordEmailTemplate = _templateDefinitionManager.Get(TestTemplates.ForgotPasswordEmail);
forgotPasswordEmailTemplate.Name.ShouldBe(TestTemplates.ForgotPasswordEmail);
forgotPasswordEmailTemplate.IsInlineLocalized.ShouldBeTrue();
}
[Fact]
public void Should_Get_Null_If_Template_Not_Found()
{
var definition = _templateDefinitionManager.GetOrNull("undefined-template");
definition.ShouldBeNull();
}
[Fact]
public void Should_Retrieve_All_Template_Definitions()
{
var definitions = _templateDefinitionManager.GetAll();
definitions.Count.ShouldBeGreaterThan(1);
}
}
}

108
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs

@ -0,0 +1,108 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.TextTemplating
{
public class TemplateRenderer_Tests : AbpTextTemplatingTestBase
{
private readonly ITemplateRenderer _templateRenderer;
public TemplateRenderer_Tests()
{
_templateRenderer = GetRequiredService<ITemplateRenderer>();
}
[Fact]
public async Task Should_Get_Rendered_Localized_Template_Content_With_Different_Cultures()
{
(await _templateRenderer.RenderAsync(
TestTemplates.WelcomeEmail,
model: new
{
name = "John"
},
cultureName: "en"
)).ShouldBe("Welcome John to the abp.io!");
(await _templateRenderer.RenderAsync(
TestTemplates.WelcomeEmail,
model: new
{
name = "John"
},
cultureName: "tr"
)).ShouldBe("Merhaba John, abp.io'ya hoşgeldiniz!");
//"en-US" fallbacks to "en" since "en-US" doesn't exists and "en" is the fallback culture
(await _templateRenderer.RenderAsync(
TestTemplates.WelcomeEmail,
model: new
{
name = "John"
},
cultureName: "en-US"
)).ShouldBe("Welcome John to the abp.io!");
//"fr" fallbacks to "en" since "fr" doesn't exists and "en" is the default culture
(await _templateRenderer.RenderAsync(
TestTemplates.WelcomeEmail,
model: new
{
Name = "John" //Intentionally written as PascalCase since Scriban supports it
},
cultureName: "fr"
)).ShouldBe("Welcome John to the abp.io!");
}
[Fact]
public async Task Should_Get_Rendered_Localized_Template_Content_With_Stronly_Typed_Model()
{
(await _templateRenderer.RenderAsync(
TestTemplates.WelcomeEmail,
model: new WelcomeEmailModel("John"),
cultureName: "en"
)).ShouldBe("Welcome John to the abp.io!");
}
[Fact]
public async Task Should_Get_Rendered_Localized_Template_Content_With_Dictionary_Model()
{
(await _templateRenderer.RenderAsync(
TestTemplates.WelcomeEmail,
model: new Dictionary<string, object>() { { "name", "John" } },
cultureName: "en"
)).ShouldBe("Welcome John to the abp.io!");
}
[Fact]
public async Task Should_Get_Rendered_Inline_Localized_Template()
{
(await _templateRenderer.RenderAsync(
TestTemplates.ForgotPasswordEmail,
cultureName: "en"
)).ShouldBe("*BEGIN*Hello. Please click to the following link to get an email to reset your password!*END*");
(await _templateRenderer.RenderAsync(
TestTemplates.ForgotPasswordEmail,
cultureName: "tr"
)).ShouldBe("*BEGIN*Merhaba. Please click to the following link to get an email to reset your password!*END*");
}
private class WelcomeEmailModel
{
public string Name { get; set; }
public WelcomeEmailModel()
{
}
public WelcomeEmailModel(string name)
{
Name = name;
}
}
}
}

32
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs

@ -0,0 +1,32 @@
using Volo.Abp.TextTemplating.Localization;
namespace Volo.Abp.TextTemplating
{
public class TestTemplateDefinitionProvider : TemplateDefinitionProvider
{
public override void Define(ITemplateDefinitionContext context)
{
context.Add(
new TemplateDefinition(
TestTemplates.WelcomeEmail,
defaultCultureName: "en"
).WithVirtualFilePath("/SampleTemplates/WelcomeEmail")
);
context.Add(
new TemplateDefinition(
TestTemplates.ForgotPasswordEmail,
localizationResource: typeof(TestLocalizationSource),
layout: TestTemplates.TestTemplateLayout1
).WithVirtualFilePath("/SampleTemplates/ForgotPasswordEmail.tpl")
);
context.Add(
new TemplateDefinition(
TestTemplates.TestTemplateLayout1,
isLayout: true
).WithVirtualFilePath("/SampleTemplates/TestTemplateLayout1.tpl")
);
}
}
}

9
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.TextTemplating
{
public static class TestTemplates
{
public const string WelcomeEmail = "WelcomeEmail";
public const string ForgotPasswordEmail = "ForgotPasswordEmail";
public const string TestTemplateLayout1 = "TestTemplateLayout1";
}
}

49
framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs

@ -0,0 +1,49 @@
using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.TextTemplating.VirtualFiles
{
//TODO: Make tests running again!
//public class VirtualFileTemplateContributor_Tests : AbpTextTemplatingTestBase
//{
// [Fact]
// public async Task Should_Get_Localized_Content_By_Culture()
// {
// var contributor = new VirtualFileTemplateContentContributor(
// "/SampleTemplates/WelcomeEmail"
// );
// contributor.Initialize(
// new TemplateContentContributorInitializationContext(
// new TemplateDefinition("Test"),
// ServiceProvider
// )
// );
// (await contributor
// .GetOrNullAsync("en")).ShouldBe("Welcome {{model.name}} to the abp.io!");
// (await contributor
// .GetOrNullAsync("tr")).ShouldBe("Merhaba {{model.name}}, abp.io'ya hoşgeldiniz!");
// }
// [Fact]
// public async Task Should_Get_Non_Localized_Template_Content()
// {
// var contributor = new VirtualFileTemplateContentContributor(
// "/SampleTemplates/ForgotPasswordEmail.tpl"
// );
// contributor.Initialize(
// new TemplateContentContributorInitializationContext(
// new TemplateDefinition("Test"),
// ServiceProvider
// )
// );
// (await contributor
// .GetOrNullAsync()).ShouldBe("{{l \"HelloText\"}}. Please click to the following link to get an email to reset your password!");
// }
//}
}

1
nupkg/common.ps1

@ -101,6 +101,7 @@ $projects = (
"framework/src/Volo.Abp.Sms",
"framework/src/Volo.Abp.Specifications",
"framework/src/Volo.Abp.TestBase",
"framework/src/Volo.Abp.TextTemplating",
"framework/src/Volo.Abp.Threading",
"framework/src/Volo.Abp.Timing",
"framework/src/Volo.Abp.UI",

Loading…
Cancel
Save