mirror of https://github.com/abpframework/abp.git
23 changed files with 326 additions and 80 deletions
@ -0,0 +1,9 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
</head> |
|||
<body> |
|||
{{#content}} |
|||
</body> |
|||
</html> |
|||
@ -1,12 +1,42 @@ |
|||
namespace Volo.Abp.Emailing.Templates |
|||
using System.Text; |
|||
|
|||
namespace Volo.Abp.Emailing.Templates |
|||
{ |
|||
public class EmailTemplate |
|||
{ |
|||
public string Content { get; } |
|||
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 EmailTemplate(string content) |
|||
public virtual void Replace(string name, string value) |
|||
{ |
|||
Content = content; |
|||
ContentBuilder.Replace("{{" + name + "}}", value); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
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 IServiceProvider ServiceProvider { get; } |
|||
protected ITemplateLocalizer TemplateLocalizer { get; } |
|||
protected EmailTemplateOptions Options { get; } |
|||
|
|||
public EmailTemplateProvider( |
|||
IOptions<EmailTemplateOptions> options, |
|||
IServiceProvider serviceProvider, |
|||
ITemplateLocalizer templateLocalizer) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
TemplateLocalizer = templateLocalizer; |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public async Task<EmailTemplate> GetAsync(string name) |
|||
{ |
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
return await GetInternalAsync(scope.ServiceProvider, name); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task<EmailTemplate> GetInternalAsync(IServiceProvider serviceProvider, string name) |
|||
{ |
|||
var context = new EmailTemplateProviderContributorContext(name, serviceProvider); |
|||
|
|||
foreach (var provider in Options.Providers) |
|||
{ |
|||
await provider.ProvideAsync(context); |
|||
} |
|||
|
|||
if (context.Template == null) |
|||
{ |
|||
throw new AbpException($"Could not found the template: {name}"); |
|||
} |
|||
|
|||
await SetLayoutAsync(serviceProvider, context); |
|||
await LocalizeAsync(serviceProvider, context); |
|||
|
|||
return context.Template; |
|||
} |
|||
|
|||
protected virtual async Task SetLayoutAsync(IServiceProvider serviceProvider, EmailTemplateProviderContributorContext context) |
|||
{ |
|||
var layout = context.Template.Definition.Layout; |
|||
|
|||
if (layout.IsNullOrEmpty()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (layout == EmailTemplateDefinition.DefaultLayoutPlaceHolder) |
|||
{ |
|||
layout = Options.DefaultLayout; |
|||
} |
|||
|
|||
var layoutTemplate = await GetInternalAsync(serviceProvider, layout); |
|||
context.Template.SetLayout(layoutTemplate); |
|||
} |
|||
|
|||
protected virtual Task LocalizeAsync(IServiceProvider serviceProvider, EmailTemplateProviderContributorContext context) |
|||
{ |
|||
if (context.Template.Definition.LocalizationResource == null) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
var localizer = serviceProvider |
|||
.GetRequiredService<IStringLocalizerFactory>() |
|||
.Create(context.Template.Definition.LocalizationResource); |
|||
|
|||
context.Template.SetContent( |
|||
TemplateLocalizer.Localize(localizer, context.Template.Content) |
|||
); |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
@ -1,40 +0,0 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.Emailing.Templates |
|||
{ |
|||
public class EmailTemplateStore : IEmailTemplateStore, ITransientDependency |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected EmailTemplateOptions Options { get; } |
|||
|
|||
public EmailTemplateStore(IOptions<EmailTemplateOptions> options, IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public async Task<EmailTemplate> GetAsync(string name) |
|||
{ |
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var context = new EmailTemplateProviderContext(name, scope.ServiceProvider); |
|||
foreach (var provider in Options.Providers) |
|||
{ |
|||
await provider.ProvideAsync(context); |
|||
} |
|||
|
|||
if (context.Template == null) |
|||
{ |
|||
//TODO: Return a default email template!
|
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
return context.Template; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Emailing.Templates |
|||
{ |
|||
public interface IEmailTemplateProviderContributor |
|||
{ |
|||
Task ProvideAsync(EmailTemplateProviderContributorContext contributorContext); |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.Emailing.Templates |
|||
{ |
|||
public interface IEmailTemplateStore |
|||
{ |
|||
Task<EmailTemplate> GetAsync(string name); |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
{{message}} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.Emailing.Templates |
|||
{ |
|||
public static class StandardEmailTemplates |
|||
{ |
|||
public const string DefaultLayout = "Abp.DefaultLayout"; |
|||
public const string SimpleMessage = "Abp.SimpleMessage"; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Microsoft.Extensions.Localization; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public interface ITemplateLocalizer |
|||
{ |
|||
string Localize(IStringLocalizer localizer, string text); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
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)] |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Shouldly; |
|||
using Volo.Abp.Localization.TestResources.Source; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Localization |
|||
{ |
|||
public class TemplateLocalizer_Tests : AbpIntegratedTest<TemplateLocalizer_Tests.TestModule> |
|||
{ |
|||
private readonly ITemplateLocalizer _templateLocalizer; |
|||
private readonly IStringLocalizer<LocalizationTestResource> _testResource; |
|||
|
|||
public TemplateLocalizer_Tests() |
|||
{ |
|||
_testResource = GetRequiredService<IStringLocalizer<LocalizationTestResource>>(); |
|||
_templateLocalizer = GetRequiredService<ITemplateLocalizer>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Localize() |
|||
{ |
|||
using (AbpCultureHelper.Use("en")) |
|||
{ |
|||
_templateLocalizer.Localize(_testResource, "<p>{{#L:CarPlural}} <b>{{#L:Universe}}</b></p>") |
|||
.ShouldBe("<p>Cars <b>Universe</b></p>"); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Work_Even_If_No_Text_To_Localize() |
|||
{ |
|||
using (AbpCultureHelper.Use("en")) |
|||
{ |
|||
_templateLocalizer.Localize(_testResource, "<p>test</p>") |
|||
.ShouldBe("<p>test</p>"); |
|||
} |
|||
} |
|||
|
|||
[DependsOn(typeof(AbpTestBaseModule))] |
|||
[DependsOn(typeof(AbpLocalizationModule))] |
|||
public class TestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.Configure<VirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpLocalization_Tests.TestModule>(); |
|||
}); |
|||
|
|||
context.Services.Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<LocalizationTestResource>("en") |
|||
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Source"); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue