Browse Source

Merge remote-tracking branch 'abpframework/dev' into docs

pull/3809/head
liangshiwei 7 years ago
parent
commit
0d6551c63a
  1. 17
      abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json
  2. 6
      common.DotSettings
  3. 16
      framework/Volo.Abp.sln
  4. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json
  5. 14
      framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs
  6. 9
      framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj
  7. 51
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs
  8. 18
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs
  9. 24
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs
  10. 1
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl
  11. 42
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs
  12. 22
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs
  13. 36
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs
  14. 38
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs
  15. 22
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs
  16. 74
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs
  17. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs
  18. 18
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs
  19. 121
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs
  20. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs
  21. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs
  22. 15
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs
  23. 7
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs
  24. 11
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs
  25. 9
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs
  26. 2
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl
  27. 1
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl
  28. 4
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs
  29. 15
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs
  30. 19
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs
  31. 68
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs
  32. 34
      framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs
  33. 5
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs
  34. 11
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs
  35. 2
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs
  36. 18
      framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs
  37. 3
      framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml
  38. 30
      framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd
  39. 26
      framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj
  40. 46
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs
  41. 16
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs
  42. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs
  43. 22
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs
  44. 13
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs
  45. 17
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs
  46. 11
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs
  47. 25
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs
  48. 27
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs
  49. 170
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs
  50. 88
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs
  51. 43
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs
  52. 31
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs
  53. 116
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs
  54. 19
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs
  55. 155
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs
  56. 25
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs
  57. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs
  58. 9
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs
  59. 83
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs
  60. 17
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs
  61. 28
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs
  62. 46
      framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs
  63. 2
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl-PL.json
  64. 2
      framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json
  65. 2
      framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json
  66. 24
      framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs
  67. 2
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl-PL.json
  68. 6
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs
  69. 39
      framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs
  70. 7
      framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj
  71. 10
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs
  72. 65
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs
  73. 54
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs
  74. 10
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs
  75. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json
  76. 2
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl-PL.json
  77. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json
  78. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json
  79. 6
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json
  80. 24
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs
  81. 4
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl
  82. 4
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl
  83. 4
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl
  84. 1
      framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl
  85. 63
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs
  86. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl-PL.json
  87. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl-PL.json
  88. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl-PL.json
  89. 2
      framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl-PL.json
  90. 25
      framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj
  91. 24
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs
  92. 12
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs
  93. 32
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs
  94. 6
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs
  95. 4
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json
  96. 4
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json
  97. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl
  98. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl
  99. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl
  100. 1
      framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl

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

@ -16,7 +16,7 @@
"Permission:Accounting": "Accounting",
"Permission:Accounting:Quotation": "Quotation",
"Menu:Organizations": "Organizations",
"Menu:Accounting": "Accounting",
"Menu:Accounting": "Accounting",
"Menu:Packages": "Packages",
"NpmPackageDeletionWarningMessage": "This NPM Package will be deleted. Do you confirm that?",
"NugetPackageDeletionWarningMessage": "This Nuget Package will be deleted. Do you confirm that?",
@ -101,10 +101,25 @@
"CompanyName": "Company name",
"CompanyAddress": "Company address",
"Price": "Price",
<<<<<<< Updated upstream
"DiscountText": "Discount text",
"DiscountQuantity": "Discount quantity",
"DiscountPrice": "Discount price",
"Quotation": "Quotation",
"Generate": "Generate"
=======
"ExtraText": "Extra Text",
"ExtraAmount": "Extra Amount",
"DownloadQuotation": "Download Quotation",
"Invoice": "Invoice",
"TaxNumber": "Tax Number",
"InvoiceNumber": "Invoice Number",
"InvoiceDate": "Invoice Date",
"Quantity": "Quantity",
"AddProduct": "Add Product",
"NeedToAddProduct": "Need to add product to generate invoice !",
"TotalPrice": "Total Price",
"Generate": "Generate"
>>>>>>> Stashed changes
}
}

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}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl.json → framework/src/Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy/Volo/Abp/AspNetCore/Mvc/UI/MultiTenancy/Localization/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"GivenTenantIsNotAvailable": "Podany tenant jest niedostępny: {0}",
"Tenant": "Tenant",

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();
}
}
}

5
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/EntityHistory/EntityHistoryHelper.cs

@ -32,7 +32,7 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory
IAuditingStore auditingStore,
IOptions<AbpAuditingOptions> options,
IClock clock,
IJsonSerializer jsonSerializer,
IJsonSerializer jsonSerializer,
IAuditingHelper auditingHelper)
{
_clock = clock;
@ -241,7 +241,8 @@ namespace Volo.Abp.EntityFrameworkCore.EntityHistory
}
}
if (propertyEntry.IsModified)
var isModified = !(propertyEntry.OriginalValue?.Equals(propertyEntry.CurrentValue) ?? propertyEntry.CurrentValue == null);
if (isModified)
{
return true;
}

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);
}
}
}

2
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl.json → framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"Menu:Administration": "Administracja"
}

2
framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl.json → framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"InternalServerErrorMessage": "Błąd wewnętrzny serwera podczas przetwarzania żądania!",
"ValidationErrorMessage": "Twoje żądanie jest niepoprawnie!",

2
framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl.json → framework/src/Volo.Abp.Validation/Volo/Abp/Validation/Localization/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"'{0}' and '{1}' do not match.": "'{0}' i '{1}' nie są takie same.",
"The {0} field is not a valid credit card number.": "Pole {0} nie jest poprawnym numerem karty kredytowej.",

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>

2
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl.json → framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Localization/Resource/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"BirthDate": "Data urodzenia",
"Value1": "Wartość jeden"

6
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/App/Entities/AppEntityWithDisableAuditingAndPropertyHasAudited.cs

@ -11,16 +11,20 @@ namespace Volo.Abp.Auditing.App.Entities
}
public AppEntityWithDisableAuditingAndPropertyHasAudited(Guid id, string name, string name2)
public AppEntityWithDisableAuditingAndPropertyHasAudited(Guid id, string name, string name2, string name3)
: base(id)
{
Name = name;
Name2 = name2;
Name3 = name3;
}
[Audited]
public string Name { get; set; }
public string Name2 { get; set; }
[Audited]
public string Name3 { get; set; }
}
}

39
framework/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/Auditing_Tests.cs

@ -7,6 +7,7 @@ using NSubstitute;
using Volo.Abp.Auditing.App.Entities;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Uow;
using Xunit;
namespace Volo.Abp.Auditing
@ -15,10 +16,12 @@ namespace Volo.Abp.Auditing
{
private IAuditingStore _auditingStore;
private IAuditingManager _auditingManager;
private IUnitOfWorkManager _unitOfWorkManager;
public Auditing_Tests()
{
_auditingManager = GetRequiredService<IAuditingManager>();
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
}
protected override void AfterAddApplication(IServiceCollection services)
@ -158,18 +161,46 @@ namespace Volo.Abp.Auditing
using (var scope = _auditingManager.BeginScope())
{
var repository = ServiceProvider.GetRequiredService<IBasicRepository<AppEntityWithDisableAuditingAndPropertyHasAudited, Guid>>();
await repository.InsertAsync(new AppEntityWithDisableAuditingAndPropertyHasAudited(Guid.NewGuid(), "test name", "test name2"));
await repository.InsertAsync(new AppEntityWithDisableAuditingAndPropertyHasAudited(Guid.NewGuid(), "test name", "test name2", "test name3"));
await scope.SaveAsync();
}
#pragma warning disable 4014
_auditingStore.Received().SaveAsync(Arg.Is<AuditLogInfo>(x =>
x.EntityChanges.Count == 1 && x.EntityChanges[0].PropertyChanges.Count == 1 &&
x.EntityChanges[0].PropertyChanges[0].PropertyName ==
nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name)));
x.EntityChanges.Count == 1 && x.EntityChanges[0].PropertyChanges.Count == 2 &&
x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name) &&
x.EntityChanges[0].PropertyChanges[1].PropertyName == nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name3)));
#pragma warning restore 4014
}
[Fact]
public virtual async Task Should_Write_AuditLog_For_Entity_That_Property_Has_Audited_Attribute_And_Has_Changed_Even_Entity_Has_DisableAuditing_Attribute()
{
var entityId = Guid.NewGuid();
var repository = ServiceProvider.GetRequiredService<IBasicRepository<AppEntityWithDisableAuditingAndPropertyHasAudited, Guid>>();
await repository.InsertAsync(new AppEntityWithDisableAuditingAndPropertyHasAudited(entityId, "test name", "test name2", "test name3"));
using (var scope = _auditingManager.BeginScope())
{
using (var uow = _unitOfWorkManager.Begin())
{
var entity = await repository.GetAsync(entityId);
entity.Name = "new name1";
await repository.UpdateAsync(entity);
await uow.CompleteAsync();
}
await scope.SaveAsync();
}
#pragma warning disable 4014
_auditingStore.Received().SaveAsync(Arg.Is<AuditLogInfo>(x =>
x.EntityChanges.Count == 1 && x.EntityChanges[0].PropertyChanges.Count == 1 &&
x.EntityChanges[0].PropertyChanges[0].PropertyName == nameof(AppEntityWithDisableAuditingAndPropertyHasAudited.Name)));
#pragma warning restore 4014
}
[Fact]
public virtual async Task Should_Write_AuditLog_If_There_No_Action_And_No_EntityChanges()

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"
}
}

2
framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json → framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-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");
});
}
}
}
}

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl.json → framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/CountryNames/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"USA": "Stany Zjednoczone Ameryki",
"Brazil": "Brazylia"

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl.json → framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Base/Validation/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"ThisFieldIsRequired": "To pole jest wymagane",
"MaxLenghtErrorMessage": "To pole może mieć maksymalnie '{0}' znaków"

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl.json → framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/Source/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"Hello <b>{0}</b>.": "Witaj <b>{0}</b>.",
"Car": "Samochód",

2
framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl.json → framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TestResources/SourceExt/pl-PL.json

@ -1,5 +1,5 @@
{
"culture": "pl",
"culture": "pl-PL",
"texts": {
"SeeYou": "Do zobaczenia"
}

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!

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

Loading…
Cancel
Save