diff --git a/common.DotSettings b/common.DotSettings index 0eb4875d49..6f40d029a7 100644 --- a/common.DotSettings +++ b/common.DotSettings @@ -20,5 +20,11 @@ False False SQL + False + False + False + False + False + False True \ No newline at end of file diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 3db0998855..5159f0a86e 100644 --- a/framework/Volo.Abp.sln +++ b/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} diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs index 92320f737c..d4d9cfb1c0 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Localization/CultureHelper.cs +++ b/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; + } } } diff --git a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj index be953a1f26..a2f83399a0 100644 --- a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj +++ b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj @@ -14,24 +14,21 @@ - - - - - - + + + diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs index c2e29a7ff7..afcbaf6aef 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs +++ b/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(options => @@ -43,41 +36,5 @@ namespace Volo.Abp.Emailing options.AddJob(); }); } - - private static void AutoAddDefinitionProviders(IServiceCollection services) - { - var definitionProviders = new List(); - - services.OnRegistred(context => - { - - if (typeof(IEmailTemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType)) - { - definitionProviders.Add(context.ImplementationType); - } - }); - - services.Configure(options => - { - options.DefinitionProviders.AddIfNotContains(definitionProviders); - }); - } - - public override void OnApplicationInitialization(ApplicationInitializationContext context) - { - using (var scope = context.ServiceProvider.CreateScope()) - { - var emailTemplateDefinitionManager = - scope.ServiceProvider.GetRequiredService(); - - foreach (var templateDefinition in emailTemplateDefinitionManager.GetAll()) - { - foreach (var contributor in templateDefinition.Contributors) - { - contributor.Initialize(new EmailTemplateInitializationContext(templateDefinition, scope.ServiceProvider)); - } - } - } - } } } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs deleted file mode 100644 index cb5a9d370b..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/AbpEmailTemplateOptions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Volo.Abp.Collections; - -namespace Volo.Abp.Emailing.Templates -{ - public class AbpEmailTemplateOptions - { - public string DefaultLayout { get; set; } - - public ITypeList DefinitionProviders { get; } - - public AbpEmailTemplateOptions() - { - DefaultLayout = StandardEmailTemplates.DefaultLayout; - - DefinitionProviders = new TypeList(); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs index c15012f0bf..385e0ef178 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs +++ b/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") + ); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl deleted file mode 100644 index 6454b0b93a..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl +++ /dev/null @@ -1 +0,0 @@ -{{message}} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs deleted file mode 100644 index ad6f8c4839..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplate.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs deleted file mode 100644 index 44a91212ea..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateContributorList : List - { - public string GetOrNull(string cultureName) - { - foreach (var contributor in this.AsQueryable().Reverse()) - { - var templateString = contributor.GetOrNull(cultureName); - if (templateString != null) - { - return templateString; - } - } - - return null; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs deleted file mode 100644 index 19f0eb2fa7..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs deleted file mode 100644 index 03a6c95d8b..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateDefinitionContext : IEmailTemplateDefinitionContext - { - protected Dictionary EmailTemplates { get; } - - public EmailTemplateDefinitionContext(Dictionary emailTemplates) - { - EmailTemplates = emailTemplates; - } - - public virtual EmailTemplateDefinition GetOrNull(string name) - { - return EmailTemplates.GetOrDefault(name); - } - - public virtual IReadOnlyList 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; - } - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs deleted file mode 100644 index aa36232156..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionDictionary.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateDefinitionDictionary : Dictionary - { - 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; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs deleted file mode 100644 index 0491dc867e..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs +++ /dev/null @@ -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> EmailTemplateDefinitions { get; } - - protected AbpEmailTemplateOptions Options { get; } - - protected IServiceProvider ServiceProvider { get; } - - public EmailTemplateDefinitionManager( - IOptions options, - IServiceProvider serviceProvider) - { - ServiceProvider = serviceProvider; - Options = options.Value; - - EmailTemplateDefinitions = - new Lazy>(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 GetAll() - { - return EmailTemplateDefinitions.Value.Values.ToImmutableList(); - } - - public virtual EmailTemplateDefinition GetOrNull(string name) - { - return EmailTemplateDefinitions.Value.GetOrDefault(name); - } - - protected virtual IDictionary CreateEmailTemplateDefinitions() - { - var templates = new Dictionary(); - - 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; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs deleted file mode 100644 index e53505fafc..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs deleted file mode 100644 index 8cd4b95bbd..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs deleted file mode 100644 index 29fcf08664..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs +++ /dev/null @@ -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 options) - { - EmailTemplateDefinitionManager = emailTemplateDefinitionManager; - TemplateLocalizer = templateLocalizer; - StringLocalizerFactory = stringLocalizerFactory; - Options = options.Value; - } - - public async Task GetAsync(string name) - { - return await GetAsync(name, CultureInfo.CurrentUICulture.Name); - } - - public async Task GetAsync(string name, string cultureName) - { - return await GetInternalAsync(name, cultureName); - } - - protected virtual async Task 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; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs deleted file mode 100644 index d2c2775845..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateContributor - { - void Initialize(EmailTemplateInitializationContext context); - - string GetOrNull(string cultureName); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs deleted file mode 100644 index 1641562ccf..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateDefinitionContext - { - EmailTemplateDefinition GetOrNull(string name); - - void Add(params EmailTemplateDefinition[] definitions); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs deleted file mode 100644 index 0936a2fe93..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs +++ /dev/null @@ -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 GetAll(); - - EmailTemplateDefinition GetOrNull(string name); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs deleted file mode 100644 index 691d3874d6..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateDefinitionProvider - { - void Define(IEmailTemplateDefinitionContext context); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs deleted file mode 100644 index ab68dbe2ca..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateProvider - { - Task GetAsync(string name); - - Task GetAsync(string name, string cultureName); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs deleted file mode 100644 index 35ac14c8fd..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.Emailing.Templates -{ - public interface ITemplateRender - { - Task RenderAsync(string template, object model = null); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl similarity index 84% rename from framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl rename to framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl index 107fbb5230..57453a027f 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Layout/en.tpl @@ -4,6 +4,6 @@ - {{#content}} + {{content}} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl new file mode 100644 index 0000000000..349de66b36 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Message/en.tpl @@ -0,0 +1 @@ +{{model.message}} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs index 7e8cbedc68..6a60219a5e 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/StandardEmailTemplates.cs +++ b/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"; } } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs deleted file mode 100644 index 8c4e24017c..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs +++ /dev/null @@ -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 RenderAsync(string template, object model = null) - { - var scribanTemplate = Template.Parse(template); - return await scribanTemplate.RenderAsync(model); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs deleted file mode 100644 index 7bee611d28..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs deleted file mode 100644 index 2ae5f88cc5..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs +++ /dev/null @@ -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 _templateDictionary; - - private readonly object _syncObj = new object(); - - public MultipleVirtualFilesEmailTemplateContributor(string virtualPath) - { - _virtualPath = virtualPath; - } - - public void Initialize(EmailTemplateInitializationContext context) - { - _virtualFileProvider = context.ServiceProvider.GetRequiredService(); - } - - public string GetOrNull(string cultureName) - { - return GetTemplateDictionary().GetOrDefault(cultureName); - } - - private Dictionary GetTemplateDictionary() - { - var dictionaries = _templateDictionary; - if (dictionaries != null) - { - return dictionaries; - } - - lock (_syncObj) - { - dictionaries = _templateDictionary; - if (dictionaries != null) - { - return dictionaries; - } - - _templateDictionary = new Dictionary(); - 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; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs deleted file mode 100644 index d72d18e99a..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs +++ /dev/null @@ -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(); - } - - public string GetOrNull(string cultureName) - { - var file = _virtualFileProvider.GetFileInfo(_virtualPath); - if (file == null || !file.Exists || file.IsDirectory) - { - return null; - } - - return file.ReadAsString(); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs index 79c164eb5f..53a2d7714e 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/AbpDictionaryBasedStringLocalizer.cs +++ b/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 { diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs index daa6abe667..a81adf3562 100644 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/LocalizationResourceContributorList.cs +++ b/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) diff --git a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs b/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs deleted file mode 100644 index 5c4220c45f..0000000000 --- a/framework/src/Volo.Abp.Localization/Volo/Abp/Localization/TemplateLocalizer.cs +++ /dev/null @@ -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)] - ); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj b/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj new file mode 100644 index 0000000000..7bcc0400b5 --- /dev/null +++ b/framework/src/Volo.Abp.TextTemplating/Volo.Abp.TextTemplating.csproj @@ -0,0 +1,26 @@ + + + + + + + netstandard2.0 + Volo.Abp.TextTemplating + Volo.Abp.TextTemplating + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingModule.cs new file mode 100644 index 0000000000..8d76391829 --- /dev/null +++ b/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(); + var contentContributors = new List(); + + 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(options => + { + options.DefinitionProviders.AddIfNotContains(definitionProviders); + options.ContentContributors.AddIfNotContains(contentContributors); + }); + } + } +} diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/AbpTextTemplatingOptions.cs new file mode 100644 index 0000000000..b217094974 --- /dev/null +++ b/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 DefinitionProviders { get; } + public ITypeList ContentContributors { get; } + + public AbpTextTemplatingOptions() + { + DefinitionProviders = new TypeList(); + ContentContributors = new TypeList(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentContributor.cs new file mode 100644 index 0000000000..746b29a2f0 --- /dev/null +++ b/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 GetOrNullAsync(TemplateContentContributorContext context); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateContentProvider.cs new file mode 100644 index 0000000000..19248dc161 --- /dev/null +++ b/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 GetContentOrNullAsync( + [NotNull] string templateName, + [CanBeNull] string cultureName = null, + bool tryDefaults = true, + bool useCurrentCultureIfCultureNameIsNull = true + ); + + Task GetContentOrNullAsync( + [NotNull] TemplateDefinition templateDefinition, + [CanBeNull] string cultureName = null, + bool tryDefaults = true, + bool useCurrentCultureIfCultureNameIsNull = true + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionContext.cs new file mode 100644 index 0000000000..515cee74ca --- /dev/null +++ b/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 GetAll(string name); + + TemplateDefinition GetOrNull(string name); + + void Add(params TemplateDefinition[] definitions); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionManager.cs new file mode 100644 index 0000000000..cbd2d15463 --- /dev/null +++ b/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 GetAll(); + + [CanBeNull] + TemplateDefinition GetOrNull(string name); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateDefinitionProvider.cs new file mode 100644 index 0000000000..aeb6a3c2da --- /dev/null +++ b/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); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/ITemplateRenderer.cs new file mode 100644 index 0000000000..b153a5130b --- /dev/null +++ b/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 + { + /// + /// Renders a text template. + /// + /// The template name + /// An optional model object that is used in the template + /// Culture name. Uses the if not specified + /// A dictionary which can be used to import global objects to the template + /// + Task RenderAsync( + [NotNull] string templateName, + [CanBeNull] object model = null, + [CanBeNull] string cultureName = null, + [CanBeNull] Dictionary globalContext = null + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentContributorContext.cs new file mode 100644 index 0000000000..773bf1a0a4 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateContentProvider.cs new file mode 100644 index 0000000000..55a3979e21 --- /dev/null +++ b/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 options) + { + ServiceScopeFactory = serviceScopeFactory; + Options = options.Value; + _templateDefinitionManager = templateDefinitionManager; + } + + public virtual Task 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 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 GetContentOrNullAsync( + ITemplateContentContributor[] contributors, + TemplateContentContributorContext context) + { + foreach (var contributor in contributors) + { + var templateString = await contributor.GetOrNullAsync(context); + if (templateString != null) + { + return templateString; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinition.cs new file mode 100644 index 0000000000..db2ccf6f63 --- /dev/null +++ b/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; } + + /// + /// Gets/sets a key-value on the . + /// + /// Name of the property + /// + /// Returns the value in the dictionary by given . + /// Returns null if given is not present in the dictionary. + /// + [CanBeNull] + public object this[string name] + { + get => Properties.GetOrDefault(name); + set => Properties[name] = value; + } + + /// + /// Can be used to get/set custom properties for this feature. + /// + [NotNull] + public Dictionary 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(); + } + + /// + /// Sets a property in the dictionary. + /// This is a shortcut for nested calls on this object. + /// + public virtual TemplateDefinition WithProperty(string key, object value) + { + Properties[key] = value; + return this; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionContext.cs new file mode 100644 index 0000000000..04c3876531 --- /dev/null +++ b/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 Templates { get; } + + public TemplateDefinitionContext(Dictionary templates) + { + Templates = templates; + } + + public IReadOnlyList GetAll(string name) + { + return Templates.Values.ToImmutableList(); + } + + public virtual TemplateDefinition GetOrNull(string name) + { + return Templates.GetOrDefault(name); + } + + public virtual IReadOnlyList 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; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionExtensions.cs new file mode 100644 index 0000000000..0d17e9969c --- /dev/null +++ b/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; + } + } +} diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionManager.cs new file mode 100644 index 0000000000..11081f6dff --- /dev/null +++ b/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> TemplateDefinitions { get; } + + protected AbpTextTemplatingOptions Options { get; } + + protected IServiceProvider ServiceProvider { get; } + + public TemplateDefinitionManager( + IOptions options, + IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + Options = options.Value; + + TemplateDefinitions = + new Lazy>(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 GetAll() + { + return TemplateDefinitions.Value.Values.ToImmutableList(); + } + + public virtual TemplateDefinition GetOrNull(string name) + { + return TemplateDefinitions.Value.GetOrDefault(name); + } + + protected virtual IDictionary CreateTextTemplateDefinitions() + { + var templates = new Dictionary(); + + 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 templates, + IServiceProvider serviceProvider) + { + var virtualFileProvider = serviceProvider.GetRequiredService(); + + 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; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateDefinitionProvider.cs new file mode 100644 index 0000000000..924a6dfdd8 --- /dev/null +++ b/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) + { + + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/TemplateRenderer.cs new file mode 100644 index 0000000000..f51e8447b0 --- /dev/null +++ b/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 RenderAsync( + [NotNull] string templateName, + [CanBeNull] object model = null, + [CanBeNull] string cultureName = null, + [CanBeNull] Dictionary globalContext = null) + { + Check.NotNullOrWhiteSpace(templateName, nameof(templateName)); + + if (globalContext == null) + { + globalContext = new Dictionary(); + } + + if (cultureName == null) + { + return await RenderInternalAsync( + templateName, + globalContext, + model + ); + } + else + { + using (CultureHelper.Use(cultureName)) + { + return await RenderInternalAsync( + templateName, + globalContext, + model + ); + } + } + } + + protected virtual async Task RenderInternalAsync( + string templateName, + Dictionary 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 RenderSingleTemplateAsync( + TemplateDefinition templateDefinition, + Dictionary globalContext, + object model = null) + { + var rawTemplateContent = await _templateContentProvider + .GetContentOrNullAsync( + templateDefinition + ); + + return await RenderTemplateContentWithScribanAsync( + templateDefinition, + rawTemplateContent, + globalContext, + model + ); + } + + protected virtual async Task RenderTemplateContentWithScribanAsync( + TemplateDefinition templateDefinition, + string templateContent, + Dictionary globalContext, + object model = null) + { + var context = CreateScribanTemplateContext( + templateDefinition, + globalContext, + model + ); + + return await Template + .Parse(templateContent) + .RenderAsync(context); + } + + protected virtual TemplateContext CreateScribanTemplateContext( + TemplateDefinition templateDefinition, + Dictionary 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( + name => localizer[name] + ) + ); + } + + context.PushGlobal(scriptObject); + + return context; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/FileInfoLocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..cde913f307 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..7d837d731d --- /dev/null +++ b/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); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/ILocalizedTemplateContentReaderFactory.cs new file mode 100644 index 0000000000..2d4d9ef2d5 --- /dev/null +++ b/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 CreateAsync(TemplateDefinition templateDefinition); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/LocalizedTemplateContentReaderFactory.cs new file mode 100644 index 0000000000..b30e5771f4 --- /dev/null +++ b/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 _readerCache; + private readonly ReaderWriterLockSlim _lock; + + public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider) + { + _virtualFileProvider = virtualFileProvider; + _readerCache = new Dictionary(); + _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); + } + + public async Task 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 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; + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/NullLocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..475559c634 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContentContributor.cs new file mode 100644 index 0000000000..ede33a3652 --- /dev/null +++ b/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 GetOrNullAsync(TemplateContentContributorContext context) + { + var localizedReader = await _localizedTemplateContentReaderFactory + .CreateAsync(context.TemplateDefinition); + + return localizedReader.GetContentOrNull( + context.Culture + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs b/framework/src/Volo.Abp.TextTemplating/Volo/Abp/TextTemplating/VirtualFiles/VirtualFolderLocalizedTemplateContentReader.cs new file mode 100644 index 0000000000..638a7db190 --- /dev/null +++ b/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 _dictionary; + + public async Task ReadContentsAsync( + IVirtualFileProvider virtualFileProvider, + string virtualPath) + { + _dictionary = new Dictionary(); + + 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); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs b/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs index 7d3505d616..8a6d7d57a1 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Microsoft/Extensions/FileProviders/AbpFileInfoExtensions.cs @@ -18,6 +18,14 @@ namespace Microsoft.Extensions.FileProviders return fileInfo.ReadAsString(Encoding.UTF8); } + /// + /// Reads file content as string using encoding. + /// + public static Task ReadAsStringAsync([NotNull] this IFileInfo fileInfo) + { + return fileInfo.ReadAsStringAsync(Encoding.UTF8); + } + /// /// Reads file content as string using the given . /// @@ -34,6 +42,22 @@ namespace Microsoft.Extensions.FileProviders } } + /// + /// Reads file content as string using the given . + /// + public static async Task 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(); + } + } + } + /// /// Reads file content as byte[]. /// diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj index a86ea9f051..030eddb65a 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj @@ -7,13 +7,6 @@ - - - - - - - diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs index e4cdfe0556..7f1175bcbb 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/AbpEmailingTestModule.cs +++ b/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(); }); - - Configure(options => - { - options.Resources - .Add() - .AddVirtualJson("/Volo/Abp/Emailing/Localization"); - }); - } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs deleted file mode 100644 index 24d7298f6c..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs +++ /dev/null @@ -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 - { - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } - - private readonly ITemplateRender _templateRender; - - public EmailTemplateRender_Tests() - { - _templateRender = GetRequiredService(); - } - - [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 - { - 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 Orders { get; set; } - - public class Order - { - public string Id { get; set; } - - public string Name { get; set; } - } - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs deleted file mode 100644 index 1e4ea8c090..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateStore_Tests.cs +++ /dev/null @@ -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 - { - protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) - { - options.UseAutofac(); - } - - private readonly IEmailTemplateProvider _emailTemplateProvider; - - public EmailTemplateStore_Tests() - { - _emailTemplateProvider = GetRequiredService(); - } - - [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($"{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"); - } - } -} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs deleted file mode 100644 index bdc75f9f2f..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Volo.Abp.Emailing.Localization -{ - public class AbpEmailingTestResource - { - } -} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json deleted file mode 100644 index 5f6e35488e..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/cs.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "cs", - "texts": { - "hello": "ahoj" - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json deleted file mode 100644 index 6dd6654aa1..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pl.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "pl", - "texts": { - "hello": "witaj" - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json deleted file mode 100644 index 0e33dee138..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/pt-BR.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "pt-BR", - "texts": { - "hello": "Olá" - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json deleted file mode 100644 index 8261599d78..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/vi.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "vi", - "texts": { - "hello": "xin chào" - } -} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json deleted file mode 100644 index dd12964f70..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/zh-Hant.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "culture": "zh-Hant", - "texts": { - "hello": "哈囉" - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs deleted file mode 100644 index 60c9a2ccf3..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl deleted file mode 100644 index 49a951e8c0..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl +++ /dev/null @@ -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. - -Confirm email address \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl deleted file mode 100644 index 3e572aedbb..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl +++ /dev/null @@ -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. - -E-posta adresini onayla \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl deleted file mode 100644 index 49a951e8c0..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl +++ /dev/null @@ -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. - -Confirm email address \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl deleted file mode 100644 index f30f512848..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl +++ /dev/null @@ -1 +0,0 @@ -{{#L:hello}} Abp \ No newline at end of file diff --git a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs b/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs deleted file mode 100644 index 4b31554637..0000000000 --- a/framework/test/Volo.Abp.Localization.Tests/Volo/Abp/Localization/TemplateLocalizer_Tests.cs +++ /dev/null @@ -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 - { - private readonly ITemplateLocalizer _templateLocalizer; - private readonly IStringLocalizer _testResource; - - public TemplateLocalizer_Tests() - { - _testResource = GetRequiredService>(); - _templateLocalizer = GetRequiredService(); - } - - [Fact] - public void Should_Localize() - { - using (CultureHelper.Use("en")) - { - _templateLocalizer.Localize(_testResource, "

{{#L:CarPlural}} {{#L:Universe}}

") - .ShouldBe("

Cars Universe

"); - } - } - - [Fact] - public void Should_Work_Even_If_No_Text_To_Localize() - { - using (CultureHelper.Use("en")) - { - _templateLocalizer.Localize(_testResource, "

test

") - .ShouldBe("

test

"); - } - } - - [DependsOn(typeof(AbpTestBaseModule))] - [DependsOn(typeof(AbpLocalizationModule))] - public class TestModule : AbpModule - { - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.FileSets.AddEmbedded(); - }); - - Configure(options => - { - options.Resources - .Add("en") - .AddVirtualJson("/Volo/Abp/Localization/TestResources/Source"); - }); - } - } - } -} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj b/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj new file mode 100644 index 0000000000..0d230552e8 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo.Abp.TextTemplating.Tests.csproj @@ -0,0 +1,25 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingOptions_Tests.cs new file mode 100644 index 0000000000..294596c693 --- /dev/null +++ b/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>().Value; + } + + [Fact] + public void Should_Auto_Add_TemplateDefinitionProviders_To_Options() + { + _options + .DefinitionProviders + .ShouldContain(typeof(TestTemplateDefinitionProvider)); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestBase.cs new file mode 100644 index 0000000000..ca5dc20445 --- /dev/null +++ b/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 + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/AbpTextTemplatingTestModule.cs new file mode 100644 index 0000000000..8e8b69e116 --- /dev/null +++ b/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(options => + { + options.FileSets.AddEmbedded("Volo.Abp.TextTemplating"); + }); + + Configure(options => + { + options.Resources + .Add("en") + .AddVirtualJson("/Localization"); + }); + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs new file mode 100644 index 0000000000..a4e53e3861 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/TestLocalizationSource.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.TextTemplating.Localization +{ + public class TestLocalizationSource + { + } +} diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json similarity index 52% rename from framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json rename to framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json index 38f7cf7e5b..a0dff7e930 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/en.json @@ -1,6 +1,6 @@ { "culture": "en", "texts": { - "hello": "hello" - } + "HelloText": "Hello" + } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json similarity index 50% rename from framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json rename to framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json index 6c3c94cfdf..d09f02cd8f 100644 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/Localization/tr.json @@ -1,6 +1,6 @@ { "culture": "tr", "texts": { - "hello": "Merhaba" - } + "HelloText": "Merhaba" + } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/ForgotPasswordEmail.tpl new file mode 100644 index 0000000000..8bcab86d0f --- /dev/null +++ b/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! \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl new file mode 100644 index 0000000000..a780e210b0 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/TestTemplateLayout1.tpl @@ -0,0 +1 @@ +*BEGIN*{{content}}*END* \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl new file mode 100644 index 0000000000..1746eed52b --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/en.tpl @@ -0,0 +1 @@ +Welcome {{model.name}} to the abp.io! \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl new file mode 100644 index 0000000000..581016bc4d --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/SampleTemplates/WelcomeEmail/tr.tpl @@ -0,0 +1 @@ +Merhaba {{model.name}}, abp.io'ya hoşgeldiniz! \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs new file mode 100644 index 0000000000..51c78cc13e --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateDefinitionTests.cs @@ -0,0 +1,41 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateDefinitionTests : AbpTextTemplatingTestBase + { + private readonly ITemplateDefinitionManager _templateDefinitionManager; + + public TemplateDefinitionTests() + { + _templateDefinitionManager = GetRequiredService(); + } + + [Fact] + public void Should_Retrieve_Template_Definition_By_Name() + { + var welcomeEmailTemplate = _templateDefinitionManager.Get(TestTemplates.WelcomeEmail); + welcomeEmailTemplate.Name.ShouldBe(TestTemplates.WelcomeEmail); + welcomeEmailTemplate.IsInlineLocalized.ShouldBeFalse(); + + var forgotPasswordEmailTemplate = _templateDefinitionManager.Get(TestTemplates.ForgotPasswordEmail); + forgotPasswordEmailTemplate.Name.ShouldBe(TestTemplates.ForgotPasswordEmail); + forgotPasswordEmailTemplate.IsInlineLocalized.ShouldBeTrue(); + } + + [Fact] + public void Should_Get_Null_If_Template_Not_Found() + { + var definition = _templateDefinitionManager.GetOrNull("undefined-template"); + definition.ShouldBeNull(); + } + + [Fact] + public void Should_Retrieve_All_Template_Definitions() + { + var definitions = _templateDefinitionManager.GetAll(); + definitions.Count.ShouldBeGreaterThan(1); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs new file mode 100644 index 0000000000..14b3df3486 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TemplateRenderer_Tests.cs @@ -0,0 +1,108 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.TextTemplating +{ + public class TemplateRenderer_Tests : AbpTextTemplatingTestBase + { + private readonly ITemplateRenderer _templateRenderer; + + public TemplateRenderer_Tests() + { + _templateRenderer = GetRequiredService(); + } + + [Fact] + public async Task Should_Get_Rendered_Localized_Template_Content_With_Different_Cultures() + { + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + name = "John" + }, + cultureName: "en" + )).ShouldBe("Welcome John to the abp.io!"); + + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + name = "John" + }, + cultureName: "tr" + )).ShouldBe("Merhaba John, abp.io'ya hoşgeldiniz!"); + + //"en-US" fallbacks to "en" since "en-US" doesn't exists and "en" is the fallback culture + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + name = "John" + }, + cultureName: "en-US" + )).ShouldBe("Welcome John to the abp.io!"); + + //"fr" fallbacks to "en" since "fr" doesn't exists and "en" is the default culture + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new + { + Name = "John" //Intentionally written as PascalCase since Scriban supports it + }, + cultureName: "fr" + )).ShouldBe("Welcome John to the abp.io!"); + } + + [Fact] + public async Task Should_Get_Rendered_Localized_Template_Content_With_Stronly_Typed_Model() + { + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new WelcomeEmailModel("John"), + cultureName: "en" + )).ShouldBe("Welcome John to the abp.io!"); + } + + [Fact] + public async Task Should_Get_Rendered_Localized_Template_Content_With_Dictionary_Model() + { + (await _templateRenderer.RenderAsync( + TestTemplates.WelcomeEmail, + model: new Dictionary() { { "name", "John" } }, + cultureName: "en" + )).ShouldBe("Welcome John to the abp.io!"); + } + + [Fact] + public async Task Should_Get_Rendered_Inline_Localized_Template() + { + (await _templateRenderer.RenderAsync( + TestTemplates.ForgotPasswordEmail, + cultureName: "en" + )).ShouldBe("*BEGIN*Hello. Please click to the following link to get an email to reset your password!*END*"); + + (await _templateRenderer.RenderAsync( + TestTemplates.ForgotPasswordEmail, + cultureName: "tr" + )).ShouldBe("*BEGIN*Merhaba. Please click to the following link to get an email to reset your password!*END*"); + } + + private class WelcomeEmailModel + { + public string Name { get; set; } + + public WelcomeEmailModel() + { + + } + + public WelcomeEmailModel(string name) + { + Name = name; + } + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs new file mode 100644 index 0000000000..05df1378b2 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplateDefinitionProvider.cs @@ -0,0 +1,32 @@ +using Volo.Abp.TextTemplating.Localization; + +namespace Volo.Abp.TextTemplating +{ + public class TestTemplateDefinitionProvider : TemplateDefinitionProvider + { + public override void Define(ITemplateDefinitionContext context) + { + context.Add( + new TemplateDefinition( + TestTemplates.WelcomeEmail, + defaultCultureName: "en" + ).WithVirtualFilePath("/SampleTemplates/WelcomeEmail") + ); + + context.Add( + new TemplateDefinition( + TestTemplates.ForgotPasswordEmail, + localizationResource: typeof(TestLocalizationSource), + layout: TestTemplates.TestTemplateLayout1 + ).WithVirtualFilePath("/SampleTemplates/ForgotPasswordEmail.tpl") + ); + + context.Add( + new TemplateDefinition( + TestTemplates.TestTemplateLayout1, + isLayout: true + ).WithVirtualFilePath("/SampleTemplates/TestTemplateLayout1.tpl") + ); + } + } +} diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs new file mode 100644 index 0000000000..a2b605c213 --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/TestTemplates.cs @@ -0,0 +1,9 @@ +namespace Volo.Abp.TextTemplating +{ + public static class TestTemplates + { + public const string WelcomeEmail = "WelcomeEmail"; + public const string ForgotPasswordEmail = "ForgotPasswordEmail"; + public const string TestTemplateLayout1 = "TestTemplateLayout1"; + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs new file mode 100644 index 0000000000..da2d4179de --- /dev/null +++ b/framework/test/Volo.Abp.TextTemplating.Tests/Volo/Abp/TextTemplating/VirtualFiles/VirtualFileTemplateContributor_Tests.cs @@ -0,0 +1,49 @@ +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.TextTemplating.VirtualFiles +{ + //TODO: Make tests running again! + //public class VirtualFileTemplateContributor_Tests : AbpTextTemplatingTestBase + //{ + // [Fact] + // public async Task Should_Get_Localized_Content_By_Culture() + // { + // var contributor = new VirtualFileTemplateContentContributor( + // "/SampleTemplates/WelcomeEmail" + // ); + + // contributor.Initialize( + // new TemplateContentContributorInitializationContext( + // new TemplateDefinition("Test"), + // ServiceProvider + // ) + // ); + + // (await contributor + // .GetOrNullAsync("en")).ShouldBe("Welcome {{model.name}} to the abp.io!"); + + // (await contributor + // .GetOrNullAsync("tr")).ShouldBe("Merhaba {{model.name}}, abp.io'ya hoşgeldiniz!"); + // } + + // [Fact] + // public async Task Should_Get_Non_Localized_Template_Content() + // { + // var contributor = new VirtualFileTemplateContentContributor( + // "/SampleTemplates/ForgotPasswordEmail.tpl" + // ); + + // contributor.Initialize( + // new TemplateContentContributorInitializationContext( + // new TemplateDefinition("Test"), + // ServiceProvider + // ) + // ); + + // (await contributor + // .GetOrNullAsync()).ShouldBe("{{l \"HelloText\"}}. Please click to the following link to get an email to reset your password!"); + // } + //} +} diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index f9e1263908..9c77b05eef 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -101,6 +101,7 @@ $projects = ( "framework/src/Volo.Abp.Sms", "framework/src/Volo.Abp.Specifications", "framework/src/Volo.Abp.TestBase", + "framework/src/Volo.Abp.TextTemplating", "framework/src/Volo.Abp.Threading", "framework/src/Volo.Abp.Timing", "framework/src/Volo.Abp.UI",