diff --git a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj index ab1b4ed370..b2394ea229 100644 --- a/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj +++ b/framework/src/Volo.Abp.Emailing/Volo.Abp.Emailing.csproj @@ -14,8 +14,12 @@ - - + + + + + + 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 b861a3898c..d3a9598da1 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/AbpEmailingModule.cs @@ -1,6 +1,8 @@ -using Volo.Abp.BackgroundJobs; +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.BackgroundJobs; using Volo.Abp.Emailing.Templates; -using Volo.Abp.Emailing.Templates.Virtual; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.Settings; @@ -16,6 +18,11 @@ namespace Volo.Abp.Emailing )] public class AbpEmailingModule : AbpModule { + public override void PreConfigureServices(ServiceConfigurationContext context) + { + AutoAddDefinitionProviders(context.Services); + } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => @@ -27,18 +34,42 @@ namespace Volo.Abp.Emailing { options.AddJob(); }); + } - Configure(options => + private static void AutoAddDefinitionProviders(IServiceCollection services) + { + var definitionProviders = new List(); + + services.OnRegistred(context => { - options.Templates - .Add( - new EmailTemplateDefinition(StandardEmailTemplates.DefaultLayout, isLayout: true, layout: null) - .SetVirtualFilePath("/Volo/Abp/Emailing/Templates/DefaultLayout.html") - ).Add( - new EmailTemplateDefinition(StandardEmailTemplates.SimpleMessage) - .SetVirtualFilePath("/Volo/Abp/Emailing/Templates/SimpleMessageTemplate.html") - ); + + 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/DefaultEmailTemplateProvider.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs new file mode 100644 index 0000000000..fdc7fa5375 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplateProvider.cs @@ -0,0 +1,16 @@ +using Volo.Abp.Emailing.Templates.VirtualFiles; + +namespace Volo.Abp.Emailing.Templates +{ + public class DefaultEmailTemplateProvider : EmailTemplateDefinitionProvider + { + public override void Define(IEmailTemplateDefinitionContext context) + { + context.Add(new EmailTemplateDefinition(StandardEmailTemplates.DefaultLayout, isLayout: true, layout: null) + .AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout")); + + context.Add(new EmailTemplateDefinition(StandardEmailTemplates.SimpleMessage) + .AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message")); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultLayout.html b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl similarity index 100% rename from framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultLayout.html rename to framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout/en.tpl diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/SimpleMessageTemplate.html b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl similarity index 100% rename from framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/SimpleMessageTemplate.html rename to framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message/en.tpl 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 new file mode 100644 index 0000000000..44a91212ea --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateContributorList.cs @@ -0,0 +1,22 @@ +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 index 9ef2ae3b1b..19f0eb2fa7 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinition.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using JetBrains.Annotations; namespace Volo.Abp.Emailing.Templates @@ -16,29 +15,22 @@ namespace Volo.Abp.Emailing.Templates public Type LocalizationResource { get; set; } - public Dictionary Properties { 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. - /// - public object this[string name] - { - get => Properties.GetOrDefault(name); - set => Properties[name] = value; - } + 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) + 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)); - Properties = new Dictionary(); 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 new file mode 100644 index 0000000000..03a6c95d8b --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionContext.cs @@ -0,0 +1,38 @@ +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/EmailTemplateDefinitionManager.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs new file mode 100644 index 0000000000..a6a58aef27 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionManager.cs @@ -0,0 +1,74 @@ +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 EmailTemplateOptions 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 new file mode 100644 index 0000000000..e53505fafc --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateDefinitionProvider.cs @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000000..8cd4b95bbd --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateInitializationContext.cs @@ -0,0 +1,18 @@ +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/EmailTemplateOptions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateOptions.cs index 6d10bd562e..57533b1aff 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateOptions.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateOptions.cs @@ -1,26 +1,18 @@ -using System.Collections.Generic; -using Volo.Abp.Emailing.Templates.Virtual; +using Volo.Abp.Collections; namespace Volo.Abp.Emailing.Templates { public class EmailTemplateOptions { - public List Providers { get; } - - public EmailTemplateDefinitionDictionary Templates { get; } - public string DefaultLayout { get; set; } + public ITypeList DefinitionProviders { get; } + public EmailTemplateOptions() { - Providers = new List - { - new VirtualFileEmailTemplateProviderContributor() - }; - - Templates = new EmailTemplateDefinitionDictionary(); - DefaultLayout = StandardEmailTemplates.DefaultLayout; + + DefinitionProviders = new TypeList(); } } } \ 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 index c5fb44e0ed..28abdfc213 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProvider.cs @@ -1,6 +1,6 @@ using System; +using System.Globalization; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -10,80 +10,105 @@ namespace Volo.Abp.Emailing.Templates { public class EmailTemplateProvider : IEmailTemplateProvider, ITransientDependency { - protected IServiceProvider ServiceProvider { get; } + protected IEmailTemplateDefinitionManager EmailTemplateDefinitionManager; protected ITemplateLocalizer TemplateLocalizer { get; } protected EmailTemplateOptions Options { get; } + protected IStringLocalizerFactory StringLocalizerFactory; - public EmailTemplateProvider( - IOptions options, - IServiceProvider serviceProvider, - ITemplateLocalizer templateLocalizer) + public EmailTemplateProvider(IEmailTemplateDefinitionManager emailTemplateDefinitionManager, + ITemplateLocalizer templateLocalizer, IStringLocalizerFactory stringLocalizerFactory, + IOptions options) { - ServiceProvider = serviceProvider; + EmailTemplateDefinitionManager = emailTemplateDefinitionManager; TemplateLocalizer = templateLocalizer; + StringLocalizerFactory = stringLocalizerFactory; Options = options.Value; } - public async Task GetAsync(string name) + public async Task GetAsync(string name, string cultureName) { - using (var scope = ServiceProvider.CreateScope()) - { - return await GetInternalAsync(scope.ServiceProvider, name); - } + return await GetInternalAsync(name, cultureName); } - protected virtual async Task GetInternalAsync(IServiceProvider serviceProvider, string name) + protected virtual async Task GetInternalAsync(string name, string cultureName) { - var context = new EmailTemplateProviderContributorContext(name, serviceProvider); + var emailTemplateDefinition = EmailTemplateDefinitionManager.GetOrNull(name); + if (emailTemplateDefinition == null) + { + // TODO: Localized message + throw new AbpException($"email template {name} not definition"); + } - foreach (var provider in Options.Providers) + var emailTemplateString = emailTemplateDefinition.Contributors.GetOrNull(cultureName); + if (emailTemplateString == null && emailTemplateDefinition.DefaultCultureName != null) { - await provider.ProvideAsync(context); + emailTemplateString = + emailTemplateDefinition.Contributors.GetOrNull(emailTemplateDefinition.DefaultCultureName); + if (emailTemplateString != null) + { + cultureName = emailTemplateDefinition.DefaultCultureName; + } } - if (context.Template == null) + if (emailTemplateString != null) { - throw new AbpException($"Could not found the template: {name}"); + var emailTemplate = new EmailTemplate(emailTemplateString, emailTemplateDefinition); + + await SetLayoutAsync(emailTemplateDefinition, emailTemplate, cultureName); + + if (emailTemplateDefinition.SingleTemplateFile) + { + await LocalizeAsync(emailTemplateDefinition, emailTemplate, cultureName); + } + + return emailTemplate; } - await SetLayoutAsync(serviceProvider, context); - await LocalizeAsync(serviceProvider, context); - - return context.Template; + // TODO: Localized message + throw new AbpException($"{cultureName} template not exist!"); } - protected virtual async Task SetLayoutAsync(IServiceProvider serviceProvider, EmailTemplateProviderContributorContext context) + protected virtual async Task SetLayoutAsync(EmailTemplateDefinition emailTemplateDefinition, + EmailTemplate emailTemplate, string cultureName) { - var layout = context.Template.Definition.Layout; - - if (layout.IsNullOrEmpty()) + var layout = emailTemplateDefinition.Layout; + if (layout.IsNullOrWhiteSpace()) { return; } - + if (layout == EmailTemplateDefinition.DefaultLayoutPlaceHolder) { layout = Options.DefaultLayout; } - var layoutTemplate = await GetInternalAsync(serviceProvider, layout); - context.Template.SetLayout(layoutTemplate); + var layoutTemplate = await GetInternalAsync(layout, cultureName); + + emailTemplate.SetLayout(layoutTemplate); } - protected virtual Task LocalizeAsync(IServiceProvider serviceProvider, EmailTemplateProviderContributorContext context) + protected virtual Task LocalizeAsync(EmailTemplateDefinition emailTemplateDefinition, + EmailTemplate emailTemplate, string cultureName) { - if (context.Template.Definition.LocalizationResource == null) + if (emailTemplateDefinition.LocalizationResource == null) { return Task.CompletedTask; } - var localizer = serviceProvider - .GetRequiredService() - .Create(context.Template.Definition.LocalizationResource); - - context.Template.SetContent( - TemplateLocalizer.Localize(localizer, context.Template.Content) - ); + var localizer = StringLocalizerFactory.Create(emailTemplateDefinition.LocalizationResource); + if (cultureName != null) + { + emailTemplate.SetContent( + TemplateLocalizer.Localize(localizer.WithCulture(new CultureInfo(cultureName)), + emailTemplate.Content) + ); + } + else + { + emailTemplate.SetContent( + TemplateLocalizer.Localize(localizer, emailTemplate.Content) + ); + } return Task.CompletedTask; } diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProviderContributorContext.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProviderContributorContext.cs deleted file mode 100644 index 9893ddc789..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/EmailTemplateProviderContributorContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Emailing.Templates -{ - public class EmailTemplateProviderContributorContext : IServiceProviderAccessor - { - public string Name { get; } - - public IServiceProvider ServiceProvider { get; } - - public EmailTemplate Template { get; set; } - - public EmailTemplateProviderContributorContext(string name, IServiceProvider serviceProvider) - { - Name = name; - ServiceProvider = serviceProvider; - } - } -} \ 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 new file mode 100644 index 0000000000..d2c2775845 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateContributor.cs @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000000..1641562ccf --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionContext.cs @@ -0,0 +1,9 @@ +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 new file mode 100644 index 0000000000..0936a2fe93 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionManager.cs @@ -0,0 +1,15 @@ +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 new file mode 100644 index 0000000000..691d3874d6 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateDefinitionProvider.cs @@ -0,0 +1,7 @@ +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 index aad1fe4f05..4fc8381833 100644 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProvider.cs @@ -4,6 +4,6 @@ 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/IEmailTemplateProviderContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProviderContributor.cs deleted file mode 100644 index 6bc63f9661..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/IEmailTemplateProviderContributor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Volo.Abp.Emailing.Templates -{ - public interface IEmailTemplateProviderContributor - { - Task ProvideAsync(EmailTemplateProviderContributorContext contributorContext); - } -} \ 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 new file mode 100644 index 0000000000..35ac14c8fd --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/ITemplateRender.cs @@ -0,0 +1,9 @@ +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/TemplateRender.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs new file mode 100644 index 0000000000..8c4e24017c --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/TemplateRender.cs @@ -0,0 +1,15 @@ +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/Virtual/EmailTemplateDefinitionExtensions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/EmailTemplateDefinitionExtensions.cs deleted file mode 100644 index cb49491b0f..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/EmailTemplateDefinitionExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Volo.Abp.Emailing.Templates.Virtual -{ - public static class EmailTemplateDefinitionExtensions - { - public static EmailTemplateDefinition SetVirtualFilePath(this EmailTemplateDefinition emailTemplateDefinition, string path) - { - emailTemplateDefinition[VirtualFileEmailTemplateProviderContributor.VirtualFilePathKey] = path; - return emailTemplateDefinition; - } - - public static string GetVirtualFilePathOrNull(this EmailTemplateDefinition emailTemplateDefinition) - { - return emailTemplateDefinition[VirtualFileEmailTemplateProviderContributor.VirtualFilePathKey] as string; - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateOptions.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateOptions.cs deleted file mode 100644 index 0103ac27dc..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace Volo.Abp.Emailing.Templates.Virtual -{ - public class VirtualFileEmailTemplateOptions - { - public IDictionary Templates { get; } - - public VirtualFileEmailTemplateOptions() - { - Templates = new Dictionary(); - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateProviderContributor.cs b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateProviderContributor.cs deleted file mode 100644 index d54f4c94cb..0000000000 --- a/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/Virtual/VirtualFileEmailTemplateProviderContributor.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Options; -using Volo.Abp.VirtualFileSystem; - -namespace Volo.Abp.Emailing.Templates.Virtual -{ - public class VirtualFileEmailTemplateProviderContributor : IEmailTemplateProviderContributor - { - public const string VirtualFilePathKey = "VirtualFilePath"; - - public Task ProvideAsync(EmailTemplateProviderContributorContext contributorContext) - { - var templateDefinition = FindTemplateDefinition(contributorContext); - if (templateDefinition == null) - { - return Task.CompletedTask; - } - - var fileInfo = FindVirtualFileInfo(contributorContext, templateDefinition); - if (fileInfo == null) - { - return Task.CompletedTask; - } - - contributorContext.Template = new EmailTemplate(fileInfo.ReadAsString(), templateDefinition); - return Task.CompletedTask; - } - - protected virtual EmailTemplateDefinition FindTemplateDefinition(EmailTemplateProviderContributorContext contributorContext) - { - return contributorContext - .ServiceProvider - .GetRequiredService>() - .Value - .Templates - .GetOrDefault(contributorContext.Name); - } - - protected virtual IFileInfo FindVirtualFileInfo(EmailTemplateProviderContributorContext contributorContext, EmailTemplateDefinition templateDefinition) - { - var virtualFilePath = templateDefinition?.GetVirtualFilePathOrNull(); - if (virtualFilePath == null) - { - return null; - } - - var virtualFileProvider = contributorContext.ServiceProvider.GetRequiredService(); - - var fileInfo = virtualFileProvider.GetFileInfo(virtualFilePath); - if (fileInfo?.Exists != true) - { - return null; - } - - return fileInfo; - } - } -} \ 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 new file mode 100644 index 0000000000..7bee611d28 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/EmailTemplateDefinitionExtensions.cs @@ -0,0 +1,19 @@ +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 new file mode 100644 index 0000000000..2ae5f88cc5 --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/MultipleVirtualFilesEmailTemplateContributor.cs @@ -0,0 +1,68 @@ +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 new file mode 100644 index 0000000000..d72d18e99a --- /dev/null +++ b/framework/src/Volo.Abp.Emailing/Volo/Abp/Emailing/Templates/VirtualFiles/SingleVirtualFileEmailTemplateContributor.cs @@ -0,0 +1,34 @@ +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/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj b/framework/test/Volo.Abp.Emailing.Tests/Volo.Abp.Emailing.Tests.csproj index 32b4f25816..ca155ab951 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 @@ -11,12 +11,15 @@ - - + + + + + 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 a36c664e8a..831258c5d5 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,5 @@ -using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.Emailing.Templates; -using Volo.Abp.Emailing.Templates.Virtual; +using Volo.Abp.Emailing.Localization; +using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -18,12 +17,13 @@ namespace Volo.Abp.Emailing options.FileSets.AddEmbedded(); }); - Configure(options => + Configure(options => { - options.Templates["template1"] = - new EmailTemplateDefinition("template1") - .SetVirtualFilePath("/Volo/Abp/Emailing/TestTemplates/template1.html"); + 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 new file mode 100644 index 0000000000..403aaad0bd --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/EmailTemplateRender_Tests.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Emailing.Templates; +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 index a09fb48ed1..62a2cb3eb6 100644 --- 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 @@ -7,6 +7,11 @@ namespace Volo.Abp.Emailing { public class EmailTemplateStore_Tests : AbpIntegratedTest { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + private readonly IEmailTemplateProvider _emailTemplateProvider; public EmailTemplateStore_Tests() @@ -17,8 +22,31 @@ namespace Volo.Abp.Emailing [Fact] public async Task Should_Get_Registered_Template() { - var template = await _emailTemplateProvider.GetAsync("template1"); - template.Content.ShouldContain("This is a test 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("\r\n " + "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 new file mode 100644 index 0000000000..bdc75f9f2f --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/AbpEmailingTestResource.cs @@ -0,0 +1,10 @@ +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/en.json b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json new file mode 100644 index 0000000000..38f7cf7e5b --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "hello": "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.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json new file mode 100644 index 0000000000..6c3c94cfdf --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/Localization/tr.json @@ -0,0 +1,6 @@ +{ + "culture": "tr", + "texts": { + "hello": "Merhaba" + } +} \ 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 new file mode 100644 index 0000000000..60c9a2ccf3 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestEmailTemplateProvider.cs @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000000..49a951e8c0 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/en.tpl @@ -0,0 +1,4 @@ +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 new file mode 100644 index 0000000000..3e572aedbb --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template1/tr.tpl @@ -0,0 +1,4 @@ +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 new file mode 100644 index 0000000000..49a951e8c0 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template2/en.tpl @@ -0,0 +1,4 @@ +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 new file mode 100644 index 0000000000..f30f512848 --- /dev/null +++ b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/Template3/Template.tpl @@ -0,0 +1 @@ +{{#L:hello}} Abp \ No newline at end of file diff --git a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/template1.html b/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/template1.html deleted file mode 100644 index 5c98393c69..0000000000 --- a/framework/test/Volo.Abp.Emailing.Tests/Volo/Abp/Emailing/TestTemplates/template1.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - This is a test template! - - \ No newline at end of file