mirror of https://github.com/abpframework/abp.git
committed by
GitHub
93 changed files with 1521 additions and 969 deletions
@ -1,18 +0,0 @@ |
|||||
using Volo.Abp.Collections; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public class AbpEmailTemplateOptions |
|
||||
{ |
|
||||
public string DefaultLayout { get; set; } |
|
||||
|
|
||||
public ITypeList<IEmailTemplateDefinitionProvider> DefinitionProviders { get; } |
|
||||
|
|
||||
public AbpEmailTemplateOptions() |
|
||||
{ |
|
||||
DefaultLayout = StandardEmailTemplates.DefaultLayout; |
|
||||
|
|
||||
DefinitionProviders = new TypeList<IEmailTemplateDefinitionProvider>(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,16 +1,26 @@ |
|||||
using Volo.Abp.Emailing.Templates.VirtualFiles; |
using Volo.Abp.TextTemplating; |
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
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) |
context.Add( |
||||
.AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Layout")); |
new TemplateDefinition( |
||||
|
StandardEmailTemplates.Layout, |
||||
|
defaultCultureName: "en", |
||||
|
isLayout: true |
||||
|
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Layout") |
||||
|
); |
||||
|
|
||||
context.Add(new EmailTemplateDefinition(StandardEmailTemplates.SimpleMessage, defaultCultureName: "en") |
context.Add( |
||||
.AddTemplateVirtualFiles("/Volo/Abp/Emailing/Templates/DefaultEmailTemplates/Message")); |
new TemplateDefinition( |
||||
|
StandardEmailTemplates.Message, |
||||
|
defaultCultureName: "en", |
||||
|
layout: StandardEmailTemplates.Layout |
||||
|
).WithVirtualFilePath("/Volo/Abp/Emailing/Templates/Message") |
||||
|
); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -1 +0,0 @@ |
|||||
{{message}} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public class EmailTemplateContributorList : List<IEmailTemplateContributor> |
|
||||
{ |
|
||||
public string GetOrNull(string cultureName) |
|
||||
{ |
|
||||
foreach (var contributor in this.AsQueryable().Reverse()) |
|
||||
{ |
|
||||
var templateString = contributor.GetOrNull(cultureName); |
|
||||
if (templateString != null) |
|
||||
{ |
|
||||
return templateString; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Collections.Immutable; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public class EmailTemplateDefinitionContext : IEmailTemplateDefinitionContext |
|
||||
{ |
|
||||
protected Dictionary<string, EmailTemplateDefinition> EmailTemplates { get; } |
|
||||
|
|
||||
public EmailTemplateDefinitionContext(Dictionary<string, EmailTemplateDefinition> emailTemplates) |
|
||||
{ |
|
||||
EmailTemplates = emailTemplates; |
|
||||
} |
|
||||
|
|
||||
public virtual EmailTemplateDefinition GetOrNull(string name) |
|
||||
{ |
|
||||
return EmailTemplates.GetOrDefault(name); |
|
||||
} |
|
||||
|
|
||||
public virtual IReadOnlyList<EmailTemplateDefinition> GetAll() |
|
||||
{ |
|
||||
return EmailTemplates.Values.ToImmutableList(); |
|
||||
} |
|
||||
|
|
||||
public virtual void Add(params EmailTemplateDefinition[] definitions) |
|
||||
{ |
|
||||
if (definitions.IsNullOrEmpty()) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
foreach (var definition in definitions) |
|
||||
{ |
|
||||
EmailTemplates[definition.Name] = definition; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,22 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public class EmailTemplateDefinitionDictionary : Dictionary<string, EmailTemplateDefinition> |
|
||||
{ |
|
||||
public EmailTemplateDefinitionDictionary Add(EmailTemplateDefinition emailTemplateDefinition) |
|
||||
{ |
|
||||
if (ContainsKey(emailTemplateDefinition.Name)) |
|
||||
{ |
|
||||
throw new AbpException( |
|
||||
"There is already an email template definition with given name: " + |
|
||||
emailTemplateDefinition.Name |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
this[emailTemplateDefinition.Name] = emailTemplateDefinition; |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,74 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Collections.Immutable; |
|
||||
using System.Linq; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public class EmailTemplateDefinitionManager : IEmailTemplateDefinitionManager, ISingletonDependency |
|
||||
{ |
|
||||
protected Lazy<IDictionary<string, EmailTemplateDefinition>> EmailTemplateDefinitions { get; } |
|
||||
|
|
||||
protected AbpEmailTemplateOptions Options { get; } |
|
||||
|
|
||||
protected IServiceProvider ServiceProvider { get; } |
|
||||
|
|
||||
public EmailTemplateDefinitionManager( |
|
||||
IOptions<AbpEmailTemplateOptions> options, |
|
||||
IServiceProvider serviceProvider) |
|
||||
{ |
|
||||
ServiceProvider = serviceProvider; |
|
||||
Options = options.Value; |
|
||||
|
|
||||
EmailTemplateDefinitions = |
|
||||
new Lazy<IDictionary<string, EmailTemplateDefinition>>(CreateEmailTemplateDefinitions, true); |
|
||||
} |
|
||||
|
|
||||
public virtual EmailTemplateDefinition Get(string name) |
|
||||
{ |
|
||||
Check.NotNull(name, nameof(name)); |
|
||||
|
|
||||
var template = GetOrNull(name); |
|
||||
|
|
||||
if (template == null) |
|
||||
{ |
|
||||
throw new AbpException("Undefined template: " + name); |
|
||||
} |
|
||||
|
|
||||
return template; |
|
||||
} |
|
||||
|
|
||||
public virtual IReadOnlyList<EmailTemplateDefinition> GetAll() |
|
||||
{ |
|
||||
return EmailTemplateDefinitions.Value.Values.ToImmutableList(); |
|
||||
} |
|
||||
|
|
||||
public virtual EmailTemplateDefinition GetOrNull(string name) |
|
||||
{ |
|
||||
return EmailTemplateDefinitions.Value.GetOrDefault(name); |
|
||||
} |
|
||||
|
|
||||
protected virtual IDictionary<string, EmailTemplateDefinition> CreateEmailTemplateDefinitions() |
|
||||
{ |
|
||||
var templates = new Dictionary<string, EmailTemplateDefinition>(); |
|
||||
|
|
||||
using (var scope = ServiceProvider.CreateScope()) |
|
||||
{ |
|
||||
var providers = Options |
|
||||
.DefinitionProviders |
|
||||
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IEmailTemplateDefinitionProvider) |
|
||||
.ToList(); |
|
||||
|
|
||||
foreach (var provider in providers) |
|
||||
{ |
|
||||
provider.Define(new EmailTemplateDefinitionContext(templates)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return templates; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,121 +0,0 @@ |
|||||
using System; |
|
||||
using System.Globalization; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.Extensions.Localization; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
using Volo.Abp.Localization; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public class EmailTemplateProvider : IEmailTemplateProvider, ITransientDependency |
|
||||
{ |
|
||||
protected IEmailTemplateDefinitionManager EmailTemplateDefinitionManager; |
|
||||
protected ITemplateLocalizer TemplateLocalizer { get; } |
|
||||
protected AbpEmailTemplateOptions Options { get; } |
|
||||
protected IStringLocalizerFactory StringLocalizerFactory; |
|
||||
|
|
||||
public EmailTemplateProvider(IEmailTemplateDefinitionManager emailTemplateDefinitionManager, |
|
||||
ITemplateLocalizer templateLocalizer, IStringLocalizerFactory stringLocalizerFactory, |
|
||||
IOptions<AbpEmailTemplateOptions> options) |
|
||||
{ |
|
||||
EmailTemplateDefinitionManager = emailTemplateDefinitionManager; |
|
||||
TemplateLocalizer = templateLocalizer; |
|
||||
StringLocalizerFactory = stringLocalizerFactory; |
|
||||
Options = options.Value; |
|
||||
} |
|
||||
|
|
||||
public async Task<EmailTemplate> GetAsync(string name) |
|
||||
{ |
|
||||
return await GetAsync(name, CultureInfo.CurrentUICulture.Name); |
|
||||
} |
|
||||
|
|
||||
public async Task<EmailTemplate> GetAsync(string name, string cultureName) |
|
||||
{ |
|
||||
return await GetInternalAsync(name, cultureName); |
|
||||
} |
|
||||
|
|
||||
protected virtual async Task<EmailTemplate> GetInternalAsync(string name, string cultureName) |
|
||||
{ |
|
||||
var emailTemplateDefinition = EmailTemplateDefinitionManager.GetOrNull(name); |
|
||||
if (emailTemplateDefinition == null) |
|
||||
{ |
|
||||
// TODO: Localized message
|
|
||||
throw new AbpException($"email template {name} not definition"); |
|
||||
} |
|
||||
|
|
||||
var emailTemplateString = emailTemplateDefinition.Contributors.GetOrNull(cultureName); |
|
||||
if (emailTemplateString == null && emailTemplateDefinition.DefaultCultureName != null) |
|
||||
{ |
|
||||
emailTemplateString = |
|
||||
emailTemplateDefinition.Contributors.GetOrNull(emailTemplateDefinition.DefaultCultureName); |
|
||||
if (emailTemplateString != null) |
|
||||
{ |
|
||||
cultureName = emailTemplateDefinition.DefaultCultureName; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (emailTemplateString != null) |
|
||||
{ |
|
||||
var emailTemplate = new EmailTemplate(emailTemplateString, emailTemplateDefinition); |
|
||||
|
|
||||
await SetLayoutAsync(emailTemplateDefinition, emailTemplate, cultureName); |
|
||||
|
|
||||
if (emailTemplateDefinition.SingleTemplateFile) |
|
||||
{ |
|
||||
await LocalizeAsync(emailTemplateDefinition, emailTemplate, cultureName); |
|
||||
} |
|
||||
|
|
||||
return emailTemplate; |
|
||||
} |
|
||||
|
|
||||
// TODO: Localized message
|
|
||||
throw new AbpException($"{cultureName} template not exist!"); |
|
||||
} |
|
||||
|
|
||||
protected virtual async Task SetLayoutAsync(EmailTemplateDefinition emailTemplateDefinition, |
|
||||
EmailTemplate emailTemplate, string cultureName) |
|
||||
{ |
|
||||
var layout = emailTemplateDefinition.Layout; |
|
||||
if (layout.IsNullOrWhiteSpace()) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (layout == EmailTemplateDefinition.DefaultLayoutPlaceHolder) |
|
||||
{ |
|
||||
layout = Options.DefaultLayout; |
|
||||
} |
|
||||
|
|
||||
var layoutTemplate = await GetInternalAsync(layout, cultureName); |
|
||||
|
|
||||
emailTemplate.SetLayout(layoutTemplate); |
|
||||
} |
|
||||
|
|
||||
protected virtual Task LocalizeAsync(EmailTemplateDefinition emailTemplateDefinition, |
|
||||
EmailTemplate emailTemplate, string cultureName) |
|
||||
{ |
|
||||
if (emailTemplateDefinition.LocalizationResource == null) |
|
||||
{ |
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
|
|
||||
var localizer = StringLocalizerFactory.Create(emailTemplateDefinition.LocalizationResource); |
|
||||
if (cultureName != null) |
|
||||
{ |
|
||||
using (CultureHelper.Use(new CultureInfo(cultureName))) |
|
||||
{ |
|
||||
emailTemplate.SetContent(TemplateLocalizer.Localize(localizer, emailTemplate.Content)); |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
emailTemplate.SetContent( |
|
||||
TemplateLocalizer.Localize(localizer, emailTemplate.Content) |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
return Task.CompletedTask; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,9 +0,0 @@ |
|||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public interface IEmailTemplateContributor |
|
||||
{ |
|
||||
void Initialize(EmailTemplateInitializationContext context); |
|
||||
|
|
||||
string GetOrNull(string cultureName); |
|
||||
} |
|
||||
} |
|
||||
@ -1,9 +0,0 @@ |
|||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public interface IEmailTemplateDefinitionContext |
|
||||
{ |
|
||||
EmailTemplateDefinition GetOrNull(string name); |
|
||||
|
|
||||
void Add(params EmailTemplateDefinition[] definitions); |
|
||||
} |
|
||||
} |
|
||||
@ -1,15 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using JetBrains.Annotations; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public interface IEmailTemplateDefinitionManager |
|
||||
{ |
|
||||
[NotNull] |
|
||||
EmailTemplateDefinition Get([NotNull] string name); |
|
||||
|
|
||||
IReadOnlyList<EmailTemplateDefinition> GetAll(); |
|
||||
|
|
||||
EmailTemplateDefinition GetOrNull(string name); |
|
||||
} |
|
||||
} |
|
||||
@ -1,7 +0,0 @@ |
|||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public interface IEmailTemplateDefinitionProvider |
|
||||
{ |
|
||||
void Define(IEmailTemplateDefinitionContext context); |
|
||||
} |
|
||||
} |
|
||||
@ -1,11 +0,0 @@ |
|||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public interface IEmailTemplateProvider |
|
||||
{ |
|
||||
Task<EmailTemplate> GetAsync(string name); |
|
||||
|
|
||||
Task<EmailTemplate> GetAsync(string name, string cultureName); |
|
||||
} |
|
||||
} |
|
||||
@ -1,9 +0,0 @@ |
|||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public interface ITemplateRender |
|
||||
{ |
|
||||
Task<string> RenderAsync(string template, object model = null); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1 @@ |
|||||
|
{{model.message}} |
||||
@ -1,15 +0,0 @@ |
|||||
using System.Threading.Tasks; |
|
||||
using Scriban; |
|
||||
using Volo.Abp.DependencyInjection; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates |
|
||||
{ |
|
||||
public class TemplateRender : ITemplateRender, ITransientDependency |
|
||||
{ |
|
||||
public async Task<string> RenderAsync(string template, object model = null) |
|
||||
{ |
|
||||
var scribanTemplate = Template.Parse(template); |
|
||||
return await scribanTemplate.RenderAsync(model); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,68 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.FileProviders; |
|
||||
using Volo.Abp.VirtualFileSystem; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates.VirtualFiles |
|
||||
{ |
|
||||
public class MultipleVirtualFilesEmailTemplateContributor : IEmailTemplateContributor |
|
||||
{ |
|
||||
private readonly string _virtualPath; |
|
||||
|
|
||||
private IVirtualFileProvider _virtualFileProvider; |
|
||||
|
|
||||
private Dictionary<string, string> _templateDictionary; |
|
||||
|
|
||||
private readonly object _syncObj = new object(); |
|
||||
|
|
||||
public MultipleVirtualFilesEmailTemplateContributor(string virtualPath) |
|
||||
{ |
|
||||
_virtualPath = virtualPath; |
|
||||
} |
|
||||
|
|
||||
public void Initialize(EmailTemplateInitializationContext context) |
|
||||
{ |
|
||||
_virtualFileProvider = context.ServiceProvider.GetRequiredService<IVirtualFileProvider>(); |
|
||||
} |
|
||||
|
|
||||
public string GetOrNull(string cultureName) |
|
||||
{ |
|
||||
return GetTemplateDictionary().GetOrDefault(cultureName); |
|
||||
} |
|
||||
|
|
||||
private Dictionary<string, string> GetTemplateDictionary() |
|
||||
{ |
|
||||
var dictionaries = _templateDictionary; |
|
||||
if (dictionaries != null) |
|
||||
{ |
|
||||
return dictionaries; |
|
||||
} |
|
||||
|
|
||||
lock (_syncObj) |
|
||||
{ |
|
||||
dictionaries = _templateDictionary; |
|
||||
if (dictionaries != null) |
|
||||
{ |
|
||||
return dictionaries; |
|
||||
} |
|
||||
|
|
||||
_templateDictionary = new Dictionary<string, string>(); |
|
||||
foreach (var file in _virtualFileProvider.GetDirectoryContents(_virtualPath)) |
|
||||
{ |
|
||||
if (file.IsDirectory) |
|
||||
{ |
|
||||
continue; |
|
||||
} |
|
||||
|
|
||||
// TODO: How to normalize file names?
|
|
||||
_templateDictionary.Add(file.Name.RemovePostFix(".tpl"), file.ReadAsString()); |
|
||||
} |
|
||||
|
|
||||
dictionaries = _templateDictionary; |
|
||||
} |
|
||||
|
|
||||
return dictionaries; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,34 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.FileProviders; |
|
||||
using Volo.Abp.VirtualFileSystem; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Templates.VirtualFiles |
|
||||
{ |
|
||||
public class SingleVirtualFileEmailTemplateContributor : IEmailTemplateContributor |
|
||||
{ |
|
||||
private readonly string _virtualPath; |
|
||||
|
|
||||
private IVirtualFileProvider _virtualFileProvider; |
|
||||
|
|
||||
public SingleVirtualFileEmailTemplateContributor(string virtualPath) |
|
||||
{ |
|
||||
_virtualPath = virtualPath; |
|
||||
} |
|
||||
|
|
||||
public void Initialize(EmailTemplateInitializationContext context) |
|
||||
{ |
|
||||
_virtualFileProvider = context.ServiceProvider.GetRequiredService<IVirtualFileProvider>(); |
|
||||
} |
|
||||
|
|
||||
public string GetOrNull(string cultureName) |
|
||||
{ |
|
||||
var file = _virtualFileProvider.GetFileInfo(_virtualPath); |
|
||||
if (file == null || !file.Exists || file.IsDirectory) |
|
||||
{ |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
return file.ReadAsString(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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)] |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,26 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<AssemblyName>Volo.Abp.TextTemplating</AssemblyName> |
||||
|
<PackageId>Volo.Abp.TextTemplating</PackageId> |
||||
|
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Scriban" Version="2.1.1" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj" /> |
||||
|
<ProjectReference Include="..\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,46 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpVirtualFileSystemModule), |
||||
|
typeof(AbpLocalizationAbstractionsModule) |
||||
|
)] |
||||
|
public class AbpTextTemplatingModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
AutoAddProvidersAndContributors(context.Services); |
||||
|
} |
||||
|
|
||||
|
private static void AutoAddProvidersAndContributors(IServiceCollection services) |
||||
|
{ |
||||
|
var definitionProviders = new List<Type>(); |
||||
|
var contentContributors = new List<Type>(); |
||||
|
|
||||
|
services.OnRegistred(context => |
||||
|
{ |
||||
|
if (typeof(ITemplateDefinitionProvider).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
definitionProviders.Add(context.ImplementationType); |
||||
|
} |
||||
|
|
||||
|
if (typeof(ITemplateContentContributor).IsAssignableFrom(context.ImplementationType)) |
||||
|
{ |
||||
|
contentContributors.Add(context.ImplementationType); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
services.Configure<AbpTextTemplatingOptions>(options => |
||||
|
{ |
||||
|
options.DefinitionProviders.AddIfNotContains(definitionProviders); |
||||
|
options.ContentContributors.AddIfNotContains(contentContributors); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using Volo.Abp.Collections; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class AbpTextTemplatingOptions |
||||
|
{ |
||||
|
public ITypeList<ITemplateDefinitionProvider> DefinitionProviders { get; } |
||||
|
public ITypeList<ITemplateContentContributor> ContentContributors { get; } |
||||
|
|
||||
|
public AbpTextTemplatingOptions() |
||||
|
{ |
||||
|
DefinitionProviders = new TypeList<ITemplateDefinitionProvider>(); |
||||
|
ContentContributors = new TypeList<ITemplateContentContributor>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public interface ITemplateContentContributor |
||||
|
{ |
||||
|
Task<string> GetOrNullAsync(TemplateContentContributorContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public interface ITemplateContentProvider |
||||
|
{ |
||||
|
Task<string> GetContentOrNullAsync( |
||||
|
[NotNull] string templateName, |
||||
|
[CanBeNull] string cultureName = null, |
||||
|
bool tryDefaults = true, |
||||
|
bool useCurrentCultureIfCultureNameIsNull = true |
||||
|
); |
||||
|
|
||||
|
Task<string> GetContentOrNullAsync( |
||||
|
[NotNull] TemplateDefinition templateDefinition, |
||||
|
[CanBeNull] string cultureName = null, |
||||
|
bool tryDefaults = true, |
||||
|
bool useCurrentCultureIfCultureNameIsNull = true |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public interface ITemplateDefinitionContext |
||||
|
{ |
||||
|
IReadOnlyList<TemplateDefinition> GetAll(string name); |
||||
|
|
||||
|
TemplateDefinition GetOrNull(string name); |
||||
|
|
||||
|
void Add(params TemplateDefinition[] definitions); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public interface ITemplateDefinitionManager |
||||
|
{ |
||||
|
[NotNull] |
||||
|
TemplateDefinition Get([NotNull] string name); |
||||
|
|
||||
|
[NotNull] |
||||
|
IReadOnlyList<TemplateDefinition> GetAll(); |
||||
|
|
||||
|
[CanBeNull] |
||||
|
TemplateDefinition GetOrNull(string name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public interface ITemplateDefinitionProvider |
||||
|
{ |
||||
|
void PreDefine(ITemplateDefinitionContext context); |
||||
|
|
||||
|
void Define(ITemplateDefinitionContext context); |
||||
|
|
||||
|
void PostDefine(ITemplateDefinitionContext context); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public interface ITemplateRenderer |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Renders a text template.
|
||||
|
/// </summary>
|
||||
|
/// <param name="templateName">The template name</param>
|
||||
|
/// <param name="model">An optional model object that is used in the template</param>
|
||||
|
/// <param name="cultureName">Culture name. Uses the <see cref="CultureInfo.CurrentUICulture"/> if not specified</param>
|
||||
|
/// <param name="globalContext">A dictionary which can be used to import global objects to the template</param>
|
||||
|
/// <returns></returns>
|
||||
|
Task<string> RenderAsync( |
||||
|
[NotNull] string templateName, |
||||
|
[CanBeNull] object model = null, |
||||
|
[CanBeNull] string cultureName = null, |
||||
|
[CanBeNull] Dictionary<string, object> globalContext = null |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,170 @@ |
|||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class TemplateContentProvider : ITemplateContentProvider, ITransientDependency |
||||
|
{ |
||||
|
public IHybridServiceScopeFactory ServiceScopeFactory { get; } |
||||
|
public AbpTextTemplatingOptions Options { get; } |
||||
|
private readonly ITemplateDefinitionManager _templateDefinitionManager; |
||||
|
|
||||
|
public TemplateContentProvider( |
||||
|
ITemplateDefinitionManager templateDefinitionManager, |
||||
|
IHybridServiceScopeFactory serviceScopeFactory, |
||||
|
IOptions<AbpTextTemplatingOptions> options) |
||||
|
{ |
||||
|
ServiceScopeFactory = serviceScopeFactory; |
||||
|
Options = options.Value; |
||||
|
_templateDefinitionManager = templateDefinitionManager; |
||||
|
} |
||||
|
|
||||
|
public virtual Task<string> GetContentOrNullAsync( |
||||
|
[NotNull] string templateName, |
||||
|
[CanBeNull] string cultureName = null, |
||||
|
bool tryDefaults = true, |
||||
|
bool useCurrentCultureIfCultureNameIsNull = true) |
||||
|
{ |
||||
|
var template = _templateDefinitionManager.Get(templateName); |
||||
|
return GetContentOrNullAsync(template, cultureName); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<string> GetContentOrNullAsync( |
||||
|
[NotNull] TemplateDefinition templateDefinition, |
||||
|
[CanBeNull] string cultureName = null, |
||||
|
bool tryDefaults = true, |
||||
|
bool useCurrentCultureIfCultureNameIsNull = true) |
||||
|
{ |
||||
|
Check.NotNull(templateDefinition, nameof(templateDefinition)); |
||||
|
|
||||
|
if (!Options.ContentContributors.Any()) |
||||
|
{ |
||||
|
throw new AbpException( |
||||
|
$"No template content contributor was registered. Use {nameof(AbpTextTemplatingOptions)} to register contributors!" |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
using (var scope = ServiceScopeFactory.CreateScope()) |
||||
|
{ |
||||
|
string templateString = null; |
||||
|
|
||||
|
if (cultureName == null && useCurrentCultureIfCultureNameIsNull) |
||||
|
{ |
||||
|
cultureName = CultureInfo.CurrentUICulture.Name; |
||||
|
} |
||||
|
|
||||
|
var contributors = CreateTemplateContentContributors(scope.ServiceProvider); |
||||
|
|
||||
|
//Try to get from the requested culture
|
||||
|
templateString = await GetContentOrNullAsync( |
||||
|
contributors, |
||||
|
new TemplateContentContributorContext( |
||||
|
templateDefinition, |
||||
|
scope.ServiceProvider, |
||||
|
cultureName |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
if (templateString != null) |
||||
|
{ |
||||
|
return templateString; |
||||
|
} |
||||
|
|
||||
|
if (!tryDefaults) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
//Try to get from same culture without country code
|
||||
|
if (cultureName != null && cultureName.Contains("-")) //Example: "tr-TR"
|
||||
|
{ |
||||
|
templateString = await GetContentOrNullAsync( |
||||
|
contributors, |
||||
|
new TemplateContentContributorContext( |
||||
|
templateDefinition, |
||||
|
scope.ServiceProvider, |
||||
|
CultureHelper.GetBaseCultureName(cultureName) |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
if (templateString != null) |
||||
|
{ |
||||
|
return templateString; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (templateDefinition.IsInlineLocalized) |
||||
|
{ |
||||
|
//Try to get culture independent content
|
||||
|
templateString = await GetContentOrNullAsync( |
||||
|
contributors, |
||||
|
new TemplateContentContributorContext( |
||||
|
templateDefinition, |
||||
|
scope.ServiceProvider, |
||||
|
null |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
if (templateString != null) |
||||
|
{ |
||||
|
return templateString; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
//Try to get from default culture
|
||||
|
if (templateDefinition.DefaultCultureName != null) |
||||
|
{ |
||||
|
templateString = await GetContentOrNullAsync( |
||||
|
contributors, |
||||
|
new TemplateContentContributorContext( |
||||
|
templateDefinition, |
||||
|
scope.ServiceProvider, |
||||
|
templateDefinition.DefaultCultureName |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
if (templateString != null) |
||||
|
{ |
||||
|
return templateString; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Not found
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected virtual ITemplateContentContributor[] CreateTemplateContentContributors(IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
return Options.ContentContributors |
||||
|
.Select(type => (ITemplateContentContributor)serviceProvider.GetRequiredService(type)) |
||||
|
.Reverse() |
||||
|
.ToArray(); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<string> GetContentOrNullAsync( |
||||
|
ITemplateContentContributor[] contributors, |
||||
|
TemplateContentContributorContext context) |
||||
|
{ |
||||
|
foreach (var contributor in contributors) |
||||
|
{ |
||||
|
var templateString = await contributor.GetOrNullAsync(context); |
||||
|
if (templateString != null) |
||||
|
{ |
||||
|
return templateString; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,88 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using JetBrains.Annotations; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class TemplateDefinition |
||||
|
{ |
||||
|
public const int MaxNameLength = 128; |
||||
|
|
||||
|
[NotNull] |
||||
|
public string Name { get; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public ILocalizableString DisplayName |
||||
|
{ |
||||
|
get => _displayName; |
||||
|
set |
||||
|
{ |
||||
|
Check.NotNull(value, nameof(value)); |
||||
|
_displayName = value; |
||||
|
} |
||||
|
} |
||||
|
private ILocalizableString _displayName; |
||||
|
|
||||
|
public bool IsLayout { get; } |
||||
|
|
||||
|
[CanBeNull] |
||||
|
public string Layout { get; set; } |
||||
|
|
||||
|
[CanBeNull] |
||||
|
public Type LocalizationResource { get; set; } |
||||
|
|
||||
|
public bool IsInlineLocalized { get; set; } |
||||
|
|
||||
|
[CanBeNull] |
||||
|
public string DefaultCultureName { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets/sets a key-value on the <see cref="Properties"/>.
|
||||
|
/// </summary>
|
||||
|
/// <param name="name">Name of the property</param>
|
||||
|
/// <returns>
|
||||
|
/// Returns the value in the <see cref="Properties"/> dictionary by given <see cref="name"/>.
|
||||
|
/// Returns null if given <see cref="name"/> is not present in the <see cref="Properties"/> dictionary.
|
||||
|
/// </returns>
|
||||
|
[CanBeNull] |
||||
|
public object this[string name] |
||||
|
{ |
||||
|
get => Properties.GetOrDefault(name); |
||||
|
set => Properties[name] = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Can be used to get/set custom properties for this feature.
|
||||
|
/// </summary>
|
||||
|
[NotNull] |
||||
|
public Dictionary<string, object> Properties { get; } |
||||
|
|
||||
|
public TemplateDefinition( |
||||
|
[NotNull] string name, |
||||
|
[CanBeNull] Type localizationResource = null, |
||||
|
[CanBeNull] ILocalizableString displayName = null, |
||||
|
bool isLayout = false, |
||||
|
string layout = null, |
||||
|
string defaultCultureName = null) |
||||
|
{ |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name), MaxNameLength); |
||||
|
LocalizationResource = localizationResource; |
||||
|
DisplayName = displayName ?? new FixedLocalizableString(Name); |
||||
|
IsLayout = isLayout; |
||||
|
Layout = layout; |
||||
|
DefaultCultureName = defaultCultureName; |
||||
|
Properties = new Dictionary<string, object>(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets a property in the <see cref="Properties"/> dictionary.
|
||||
|
/// This is a shortcut for nested calls on this object.
|
||||
|
/// </summary>
|
||||
|
public virtual TemplateDefinition WithProperty(string key, object value) |
||||
|
{ |
||||
|
Properties[key] = value; |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class TemplateDefinitionContext : ITemplateDefinitionContext |
||||
|
{ |
||||
|
protected Dictionary<string, TemplateDefinition> Templates { get; } |
||||
|
|
||||
|
public TemplateDefinitionContext(Dictionary<string, TemplateDefinition> templates) |
||||
|
{ |
||||
|
Templates = templates; |
||||
|
} |
||||
|
|
||||
|
public IReadOnlyList<TemplateDefinition> GetAll(string name) |
||||
|
{ |
||||
|
return Templates.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual TemplateDefinition GetOrNull(string name) |
||||
|
{ |
||||
|
return Templates.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
public virtual IReadOnlyList<TemplateDefinition> GetAll() |
||||
|
{ |
||||
|
return Templates.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual void Add(params TemplateDefinition[] definitions) |
||||
|
{ |
||||
|
if (definitions.IsNullOrEmpty()) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
foreach (var definition in definitions) |
||||
|
{ |
||||
|
Templates[definition.Name] = definition; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,116 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class TemplateDefinitionManager : ITemplateDefinitionManager, ISingletonDependency |
||||
|
{ |
||||
|
protected Lazy<IDictionary<string, TemplateDefinition>> TemplateDefinitions { get; } |
||||
|
|
||||
|
protected AbpTextTemplatingOptions Options { get; } |
||||
|
|
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
|
||||
|
public TemplateDefinitionManager( |
||||
|
IOptions<AbpTextTemplatingOptions> options, |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
Options = options.Value; |
||||
|
|
||||
|
TemplateDefinitions = |
||||
|
new Lazy<IDictionary<string, TemplateDefinition>>(CreateTextTemplateDefinitions, true); |
||||
|
} |
||||
|
|
||||
|
public virtual TemplateDefinition Get(string name) |
||||
|
{ |
||||
|
Check.NotNull(name, nameof(name)); |
||||
|
|
||||
|
var template = GetOrNull(name); |
||||
|
|
||||
|
if (template == null) |
||||
|
{ |
||||
|
throw new AbpException("Undefined template: " + name); |
||||
|
} |
||||
|
|
||||
|
return template; |
||||
|
} |
||||
|
|
||||
|
public virtual IReadOnlyList<TemplateDefinition> GetAll() |
||||
|
{ |
||||
|
return TemplateDefinitions.Value.Values.ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
public virtual TemplateDefinition GetOrNull(string name) |
||||
|
{ |
||||
|
return TemplateDefinitions.Value.GetOrDefault(name); |
||||
|
} |
||||
|
|
||||
|
protected virtual IDictionary<string, TemplateDefinition> CreateTextTemplateDefinitions() |
||||
|
{ |
||||
|
var templates = new Dictionary<string, TemplateDefinition>(); |
||||
|
|
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var providers = Options |
||||
|
.DefinitionProviders |
||||
|
.Select(p => scope.ServiceProvider.GetRequiredService(p) as ITemplateDefinitionProvider) |
||||
|
.ToList(); |
||||
|
|
||||
|
var context = new TemplateDefinitionContext(templates); |
||||
|
|
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
provider.PreDefine(context); |
||||
|
} |
||||
|
|
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
provider.Define(context); |
||||
|
} |
||||
|
|
||||
|
foreach (var provider in providers) |
||||
|
{ |
||||
|
provider.PostDefine(context); |
||||
|
} |
||||
|
|
||||
|
SetIsInlineLocalized( |
||||
|
templates, |
||||
|
scope.ServiceProvider |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return templates; |
||||
|
} |
||||
|
|
||||
|
protected virtual void SetIsInlineLocalized( |
||||
|
Dictionary<string, TemplateDefinition> templates, |
||||
|
IServiceProvider serviceProvider) |
||||
|
{ |
||||
|
var virtualFileProvider = serviceProvider.GetRequiredService<IVirtualFileProvider>(); |
||||
|
|
||||
|
foreach (var templateDefinition in templates.Values) |
||||
|
{ |
||||
|
var virtualPath = templateDefinition.GetVirtualFilePathOrNull(); |
||||
|
if (virtualPath == null) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var fileInfo = virtualFileProvider.GetFileInfo(virtualPath); |
||||
|
if (!fileInfo.Exists) |
||||
|
{ |
||||
|
throw new AbpException("Could not find a file/folder at the location: " + virtualPath); |
||||
|
} |
||||
|
|
||||
|
templateDefinition.IsInlineLocalized = !fileInfo.IsDirectory; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Scriban; |
||||
|
using Scriban.Runtime; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class TemplateRenderer : ITemplateRenderer, ITransientDependency |
||||
|
{ |
||||
|
private readonly ITemplateContentProvider _templateContentProvider; |
||||
|
private readonly ITemplateDefinitionManager _templateDefinitionManager; |
||||
|
private readonly IStringLocalizerFactory _stringLocalizerFactory; |
||||
|
|
||||
|
public TemplateRenderer( |
||||
|
ITemplateContentProvider templateContentProvider, |
||||
|
ITemplateDefinitionManager templateDefinitionManager, |
||||
|
IStringLocalizerFactory stringLocalizerFactory) |
||||
|
{ |
||||
|
_templateContentProvider = templateContentProvider; |
||||
|
_templateDefinitionManager = templateDefinitionManager; |
||||
|
_stringLocalizerFactory = stringLocalizerFactory; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<string> RenderAsync( |
||||
|
[NotNull] string templateName, |
||||
|
[CanBeNull] object model = null, |
||||
|
[CanBeNull] string cultureName = null, |
||||
|
[CanBeNull] Dictionary<string, object> globalContext = null) |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(templateName, nameof(templateName)); |
||||
|
|
||||
|
if (globalContext == null) |
||||
|
{ |
||||
|
globalContext = new Dictionary<string, object>(); |
||||
|
} |
||||
|
|
||||
|
if (cultureName == null) |
||||
|
{ |
||||
|
return await RenderInternalAsync( |
||||
|
templateName, |
||||
|
globalContext, |
||||
|
model |
||||
|
); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
using (CultureHelper.Use(cultureName)) |
||||
|
{ |
||||
|
return await RenderInternalAsync( |
||||
|
templateName, |
||||
|
globalContext, |
||||
|
model |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<string> RenderInternalAsync( |
||||
|
string templateName, |
||||
|
Dictionary<string, object> globalContext, |
||||
|
object model = null) |
||||
|
{ |
||||
|
var templateDefinition = _templateDefinitionManager.Get(templateName); |
||||
|
|
||||
|
var renderedContent = await RenderSingleTemplateAsync( |
||||
|
templateDefinition, |
||||
|
globalContext, |
||||
|
model |
||||
|
); |
||||
|
|
||||
|
if (templateDefinition.Layout != null) |
||||
|
{ |
||||
|
globalContext["content"] = renderedContent; |
||||
|
renderedContent = await RenderInternalAsync( |
||||
|
templateDefinition.Layout, |
||||
|
globalContext |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return renderedContent; |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<string> RenderSingleTemplateAsync( |
||||
|
TemplateDefinition templateDefinition, |
||||
|
Dictionary<string, object> globalContext, |
||||
|
object model = null) |
||||
|
{ |
||||
|
var rawTemplateContent = await _templateContentProvider |
||||
|
.GetContentOrNullAsync( |
||||
|
templateDefinition |
||||
|
); |
||||
|
|
||||
|
return await RenderTemplateContentWithScribanAsync( |
||||
|
templateDefinition, |
||||
|
rawTemplateContent, |
||||
|
globalContext, |
||||
|
model |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<string> RenderTemplateContentWithScribanAsync( |
||||
|
TemplateDefinition templateDefinition, |
||||
|
string templateContent, |
||||
|
Dictionary<string, object> globalContext, |
||||
|
object model = null) |
||||
|
{ |
||||
|
var context = CreateScribanTemplateContext( |
||||
|
templateDefinition, |
||||
|
globalContext, |
||||
|
model |
||||
|
); |
||||
|
|
||||
|
return await Template |
||||
|
.Parse(templateContent) |
||||
|
.RenderAsync(context); |
||||
|
} |
||||
|
|
||||
|
protected virtual TemplateContext CreateScribanTemplateContext( |
||||
|
TemplateDefinition templateDefinition, |
||||
|
Dictionary<string, object> globalContext, |
||||
|
object model = null) |
||||
|
{ |
||||
|
var context = new TemplateContext(); |
||||
|
|
||||
|
var scriptObject = new ScriptObject(); |
||||
|
|
||||
|
scriptObject.Import(globalContext); |
||||
|
|
||||
|
if (model != null) |
||||
|
{ |
||||
|
scriptObject["model"] = model; |
||||
|
} |
||||
|
|
||||
|
if (templateDefinition.LocalizationResource != null) |
||||
|
{ |
||||
|
var localizer = _stringLocalizerFactory.Create(templateDefinition.LocalizationResource); |
||||
|
scriptObject.Import( |
||||
|
"L", |
||||
|
new Func<string, string>( |
||||
|
name => localizer[name] |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
context.PushGlobal(scriptObject); |
||||
|
|
||||
|
return context; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating.VirtualFiles |
||||
|
{ |
||||
|
public interface ILocalizedTemplateContentReader |
||||
|
{ |
||||
|
public string GetContentOrNull([CanBeNull] string culture); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating.VirtualFiles |
||||
|
{ |
||||
|
public interface ILocalizedTemplateContentReaderFactory |
||||
|
{ |
||||
|
Task<ILocalizedTemplateContentReader> CreateAsync(TemplateDefinition templateDefinition); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating.VirtualFiles |
||||
|
{ |
||||
|
public class LocalizedTemplateContentReaderFactory : ILocalizedTemplateContentReaderFactory, ISingletonDependency |
||||
|
{ |
||||
|
private readonly IVirtualFileProvider _virtualFileProvider; |
||||
|
private readonly Dictionary<string, ILocalizedTemplateContentReader> _readerCache; |
||||
|
private readonly ReaderWriterLockSlim _lock; |
||||
|
|
||||
|
public LocalizedTemplateContentReaderFactory(IVirtualFileProvider virtualFileProvider) |
||||
|
{ |
||||
|
_virtualFileProvider = virtualFileProvider; |
||||
|
_readerCache = new Dictionary<string, ILocalizedTemplateContentReader>(); |
||||
|
_lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); |
||||
|
} |
||||
|
|
||||
|
public async Task<ILocalizedTemplateContentReader> CreateAsync(TemplateDefinition templateDefinition) |
||||
|
{ |
||||
|
_lock.EnterUpgradeableReadLock(); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
var reader = _readerCache.GetOrDefault(templateDefinition.Name); |
||||
|
if (reader != null) |
||||
|
{ |
||||
|
return reader; |
||||
|
} |
||||
|
|
||||
|
_lock.EnterWriteLock(); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
reader = await CreateInternalAsync(templateDefinition); |
||||
|
_readerCache[templateDefinition.Name] = reader; |
||||
|
return reader; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
_lock.ExitWriteLock(); |
||||
|
} |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
_lock.ExitUpgradeableReadLock(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<ILocalizedTemplateContentReader> CreateInternalAsync( |
||||
|
TemplateDefinition templateDefinition) |
||||
|
{ |
||||
|
var virtualPath = templateDefinition.GetVirtualFilePathOrNull(); |
||||
|
if (virtualPath == null) |
||||
|
{ |
||||
|
return NullLocalizedTemplateContentReader.Instance; |
||||
|
} |
||||
|
|
||||
|
var fileInfo = _virtualFileProvider.GetFileInfo(virtualPath); |
||||
|
if (!fileInfo.Exists) |
||||
|
{ |
||||
|
throw new AbpException("Could not find a file/folder at the location: " + virtualPath); |
||||
|
} |
||||
|
|
||||
|
if (fileInfo.IsDirectory) |
||||
|
{ |
||||
|
var folderReader = new VirtualFolderLocalizedTemplateContentReader(); |
||||
|
await folderReader.ReadContentsAsync(_virtualFileProvider, virtualPath); |
||||
|
return folderReader; |
||||
|
} |
||||
|
else //File
|
||||
|
{ |
||||
|
var singleFileReader = new FileInfoLocalizedTemplateContentReader(); |
||||
|
await singleFileReader.ReadContentsAsync(fileInfo); |
||||
|
return singleFileReader; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating.VirtualFiles |
||||
|
{ |
||||
|
public class VirtualFileTemplateContentContributor : ITemplateContentContributor, ITransientDependency |
||||
|
{ |
||||
|
public const string VirtualPathPropertyName = "VirtualPath"; |
||||
|
|
||||
|
private readonly ILocalizedTemplateContentReaderFactory _localizedTemplateContentReaderFactory; |
||||
|
|
||||
|
public VirtualFileTemplateContentContributor( |
||||
|
ILocalizedTemplateContentReaderFactory localizedTemplateContentReaderFactory) |
||||
|
{ |
||||
|
_localizedTemplateContentReaderFactory = localizedTemplateContentReaderFactory; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<string> GetOrNullAsync(TemplateContentContributorContext context) |
||||
|
{ |
||||
|
var localizedReader = await _localizedTemplateContentReaderFactory |
||||
|
.CreateAsync(context.TemplateDefinition); |
||||
|
|
||||
|
return localizedReader.GetContentOrNull( |
||||
|
context.Culture |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating.VirtualFiles |
||||
|
{ |
||||
|
public class VirtualFolderLocalizedTemplateContentReader : ILocalizedTemplateContentReader |
||||
|
{ |
||||
|
private Dictionary<string, string> _dictionary; |
||||
|
|
||||
|
public async Task ReadContentsAsync( |
||||
|
IVirtualFileProvider virtualFileProvider, |
||||
|
string virtualPath) |
||||
|
{ |
||||
|
_dictionary = new Dictionary<string, string>(); |
||||
|
|
||||
|
var directoryInfo = virtualFileProvider.GetFileInfo(virtualPath); |
||||
|
if (!directoryInfo.IsDirectory) |
||||
|
{ |
||||
|
throw new AbpException("Given virtual path is not a folder: " + virtualPath); |
||||
|
} |
||||
|
|
||||
|
foreach (var file in virtualFileProvider.GetDirectoryContents(virtualPath)) |
||||
|
{ |
||||
|
if (file.IsDirectory) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
_dictionary.Add(file.Name.RemovePostFix(".tpl"), await file.ReadAsStringAsync()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public string GetContentOrNull(string cultureName) |
||||
|
{ |
||||
|
if (cultureName == null) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return _dictionary.GetOrDefault(cultureName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,65 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.Threading.Tasks; |
|
||||
using Shouldly; |
|
||||
using Volo.Abp.Emailing.Templates; |
|
||||
using Volo.Abp.Testing; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing |
|
||||
{ |
|
||||
public class EmailTemplateRender_Tests : AbpIntegratedTest<AbpEmailingTestModule> |
|
||||
{ |
|
||||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|
||||
{ |
|
||||
options.UseAutofac(); |
|
||||
} |
|
||||
|
|
||||
private readonly ITemplateRender _templateRender; |
|
||||
|
|
||||
public EmailTemplateRender_Tests() |
|
||||
{ |
|
||||
_templateRender = GetRequiredService<ITemplateRender>(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task RenderAsync() |
|
||||
{ |
|
||||
var template = "Hello {{email}} {{ for order in orders }}{{ order.id }}:{{ order.name }},{{ end }}"; |
|
||||
|
|
||||
var model = new ModelClass |
|
||||
{ |
|
||||
Email = "john@abp.io", |
|
||||
Orders = new List<ModelClass.Order> |
|
||||
{ |
|
||||
new ModelClass.Order |
|
||||
{ |
|
||||
Id = "1", |
|
||||
Name = "iphone" |
|
||||
}, |
|
||||
new ModelClass.Order |
|
||||
{ |
|
||||
Id = "2", |
|
||||
Name = "ipad" |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
var result = await _templateRender.RenderAsync(template, model); |
|
||||
result.ShouldBe("Hello john@abp.io 1:iphone,2:ipad,"); |
|
||||
} |
|
||||
|
|
||||
public class ModelClass |
|
||||
{ |
|
||||
public string Email { get; set; } |
|
||||
|
|
||||
public List<Order> Orders { get; set; } |
|
||||
|
|
||||
public class Order |
|
||||
{ |
|
||||
public string Id { get; set; } |
|
||||
|
|
||||
public string Name { get; set; } |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,54 +0,0 @@ |
|||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Shouldly; |
|
||||
using Volo.Abp.Emailing.Templates; |
|
||||
using Volo.Abp.Testing; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing |
|
||||
{ |
|
||||
public class EmailTemplateStore_Tests : AbpIntegratedTest<AbpEmailingTestModule> |
|
||||
{ |
|
||||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|
||||
{ |
|
||||
options.UseAutofac(); |
|
||||
} |
|
||||
|
|
||||
private readonly IEmailTemplateProvider _emailTemplateProvider; |
|
||||
|
|
||||
public EmailTemplateStore_Tests() |
|
||||
{ |
|
||||
_emailTemplateProvider = GetRequiredService<IEmailTemplateProvider>(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_Get_Registered_Template() |
|
||||
{ |
|
||||
var template = await _emailTemplateProvider.GetAsync("template1", "tr"); |
|
||||
template.Content.ShouldContain("Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın."); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_Get_Default_Culture_Template() |
|
||||
{ |
|
||||
var template = await _emailTemplateProvider.GetAsync("template1", "zh-Hans"); |
|
||||
template.Content.ShouldContain("Please confirm your email address by clicking the link below."); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_Get_Registered_Template_With_Layout() |
|
||||
{ |
|
||||
var template = await _emailTemplateProvider.GetAsync("template2", "en"); |
|
||||
|
|
||||
template.Content.ShouldContain($"<body>{Environment.NewLine} " + "Please confirm your email address by clicking the link below."); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
[Fact] |
|
||||
public async Task Should_Get_Registered_Template_With_Localize() |
|
||||
{ |
|
||||
var template = await _emailTemplateProvider.GetAsync("template3", "tr"); |
|
||||
template.Content.ShouldContain("Merhaba Abp"); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,10 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Text; |
|
||||
|
|
||||
namespace Volo.Abp.Emailing.Localization |
|
||||
{ |
|
||||
public class AbpEmailingTestResource |
|
||||
{ |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
{ |
|
||||
"culture": "cs", |
|
||||
"texts": { |
|
||||
"hello": "ahoj" |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
{ |
|
||||
"culture": "pl", |
|
||||
"texts": { |
|
||||
"hello": "witaj" |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
{ |
|
||||
"culture": "pt-BR", |
|
||||
"texts": { |
|
||||
"hello": "Olá" |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
{ |
|
||||
"culture": "vi", |
|
||||
"texts": { |
|
||||
"hello": "xin chào" |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
{ |
|
||||
"culture": "zh-Hant", |
|
||||
"texts": { |
|
||||
"hello": "哈囉" |
|
||||
} |
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,4 +0,0 @@ |
|||||
Please confirm your email address by clicking the link below. |
|
||||
We may need to send you critical information about our service and it is important that we have an accurate email address. |
|
||||
|
|
||||
<a href="#">Confirm email address</a> |
|
||||
@ -1,4 +0,0 @@ |
|||||
Lütfen aşağıdaki bağlantıya tıklayarak e-posta adresinizi onaylayın. |
|
||||
Size hizmetimizle ilgili kritik bilgileri göndermemiz gerekebilir ve doğru bir e-posta adresimizin olması önemlidir. |
|
||||
|
|
||||
<a href="#">E-posta adresini onayla</a> |
|
||||
@ -1,4 +0,0 @@ |
|||||
Please confirm your email address by clicking the link below. |
|
||||
We may need to send you critical information about our service and it is important that we have an accurate email address. |
|
||||
|
|
||||
<a href="#">Confirm email address</a> |
|
||||
@ -1 +0,0 @@ |
|||||
{{#L:hello}} Abp |
|
||||
@ -1,63 +0,0 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Localization; |
|
||||
using Shouldly; |
|
||||
using Volo.Abp.Localization.TestResources.Source; |
|
||||
using Volo.Abp.Modularity; |
|
||||
using Volo.Abp.Testing; |
|
||||
using Volo.Abp.VirtualFileSystem; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace Volo.Abp.Localization |
|
||||
{ |
|
||||
public class TemplateLocalizer_Tests : AbpIntegratedTest<TemplateLocalizer_Tests.TestModule> |
|
||||
{ |
|
||||
private readonly ITemplateLocalizer _templateLocalizer; |
|
||||
private readonly IStringLocalizer<LocalizationTestResource> _testResource; |
|
||||
|
|
||||
public TemplateLocalizer_Tests() |
|
||||
{ |
|
||||
_testResource = GetRequiredService<IStringLocalizer<LocalizationTestResource>>(); |
|
||||
_templateLocalizer = GetRequiredService<ITemplateLocalizer>(); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_Localize() |
|
||||
{ |
|
||||
using (CultureHelper.Use("en")) |
|
||||
{ |
|
||||
_templateLocalizer.Localize(_testResource, "<p>{{#L:CarPlural}} <b>{{#L:Universe}}</b></p>") |
|
||||
.ShouldBe("<p>Cars <b>Universe</b></p>"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void Should_Work_Even_If_No_Text_To_Localize() |
|
||||
{ |
|
||||
using (CultureHelper.Use("en")) |
|
||||
{ |
|
||||
_templateLocalizer.Localize(_testResource, "<p>test</p>") |
|
||||
.ShouldBe("<p>test</p>"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[DependsOn(typeof(AbpTestBaseModule))] |
|
||||
[DependsOn(typeof(AbpLocalizationModule))] |
|
||||
public class TestModule : AbpModule |
|
||||
{ |
|
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
|
||||
{ |
|
||||
Configure<AbpVirtualFileSystemOptions>(options => |
|
||||
{ |
|
||||
options.FileSets.AddEmbedded<AbpLocalization_Tests.TestModule>(); |
|
||||
}); |
|
||||
|
|
||||
Configure<AbpLocalizationOptions>(options => |
|
||||
{ |
|
||||
options.Resources |
|
||||
.Add<LocalizationTestResource>("en") |
|
||||
.AddVirtualJson("/Volo/Abp/Localization/TestResources/Source"); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,25 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.test.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp3.1</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="Volo\Abp\TextTemplating\Localization\**\*.json" /> |
||||
|
<None Remove="Volo\Abp\TextTemplating\Localization\**\*.json" /> |
||||
|
<EmbeddedResource Include="Volo\Abp\TextTemplating\SampleTemplates\**\*.tpl" /> |
||||
|
<None Remove="Volo\Abp\TextTemplating\SampleTemplates\**\*.tpl" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\Volo.Abp.Localization\Volo.Abp.Localization.csproj" /> |
||||
|
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Volo.Abp.TextTemplating\Volo.Abp.TextTemplating.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,24 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class AbpTextTemplatingOptions_Tests : AbpTextTemplatingTestBase |
||||
|
{ |
||||
|
private readonly AbpTextTemplatingOptions _options; |
||||
|
|
||||
|
public AbpTextTemplatingOptions_Tests() |
||||
|
{ |
||||
|
_options = GetRequiredService<IOptions<AbpTextTemplatingOptions>>().Value; |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Auto_Add_TemplateDefinitionProviders_To_Options() |
||||
|
{ |
||||
|
_options |
||||
|
.DefinitionProviders |
||||
|
.ShouldContain(typeof(TestTemplateDefinitionProvider)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Volo.Abp.Testing; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public abstract class AbpTextTemplatingTestBase : AbpIntegratedTest<AbpTextTemplatingTestModule> |
||||
|
{ |
||||
|
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
||||
|
{ |
||||
|
options.UseAutofac(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
using Volo.Abp.Autofac; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.TextTemplating.Localization; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpTextTemplatingModule), |
||||
|
typeof(AbpTestBaseModule), |
||||
|
typeof(AbpAutofacModule), |
||||
|
typeof(AbpLocalizationModule) |
||||
|
)] |
||||
|
public class AbpTextTemplatingTestModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpTextTemplatingTestModule>("Volo.Abp.TextTemplating"); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Add<TestLocalizationSource>("en") |
||||
|
.AddVirtualJson("/Localization"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace Volo.Abp.TextTemplating.Localization |
||||
|
{ |
||||
|
public class TestLocalizationSource |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -1,6 +1,6 @@ |
|||||
{ |
{ |
||||
"culture": "en", |
"culture": "en", |
||||
"texts": { |
"texts": { |
||||
"hello": "hello" |
"HelloText": "Hello" |
||||
} |
} |
||||
} |
} |
||||
@ -1,6 +1,6 @@ |
|||||
{ |
{ |
||||
"culture": "tr", |
"culture": "tr", |
||||
"texts": { |
"texts": { |
||||
"hello": "Merhaba" |
"HelloText": "Merhaba" |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1 @@ |
|||||
|
{{L "HelloText"}}. Please click to the following link to get an email to reset your password! |
||||
@ -0,0 +1 @@ |
|||||
|
*BEGIN*{{content}}*END* |
||||
@ -0,0 +1 @@ |
|||||
|
Welcome {{model.name}} to the abp.io! |
||||
@ -0,0 +1 @@ |
|||||
|
Merhaba {{model.name}}, abp.io'ya hoşgeldiniz! |
||||
@ -0,0 +1,41 @@ |
|||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class TemplateDefinitionTests : AbpTextTemplatingTestBase |
||||
|
{ |
||||
|
private readonly ITemplateDefinitionManager _templateDefinitionManager; |
||||
|
|
||||
|
public TemplateDefinitionTests() |
||||
|
{ |
||||
|
_templateDefinitionManager = GetRequiredService<ITemplateDefinitionManager>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Retrieve_Template_Definition_By_Name() |
||||
|
{ |
||||
|
var welcomeEmailTemplate = _templateDefinitionManager.Get(TestTemplates.WelcomeEmail); |
||||
|
welcomeEmailTemplate.Name.ShouldBe(TestTemplates.WelcomeEmail); |
||||
|
welcomeEmailTemplate.IsInlineLocalized.ShouldBeFalse(); |
||||
|
|
||||
|
var forgotPasswordEmailTemplate = _templateDefinitionManager.Get(TestTemplates.ForgotPasswordEmail); |
||||
|
forgotPasswordEmailTemplate.Name.ShouldBe(TestTemplates.ForgotPasswordEmail); |
||||
|
forgotPasswordEmailTemplate.IsInlineLocalized.ShouldBeTrue(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Get_Null_If_Template_Not_Found() |
||||
|
{ |
||||
|
var definition = _templateDefinitionManager.GetOrNull("undefined-template"); |
||||
|
definition.ShouldBeNull(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Retrieve_All_Template_Definitions() |
||||
|
{ |
||||
|
var definitions = _templateDefinitionManager.GetAll(); |
||||
|
definitions.Count.ShouldBeGreaterThan(1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,108 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.TextTemplating |
||||
|
{ |
||||
|
public class TemplateRenderer_Tests : AbpTextTemplatingTestBase |
||||
|
{ |
||||
|
private readonly ITemplateRenderer _templateRenderer; |
||||
|
|
||||
|
public TemplateRenderer_Tests() |
||||
|
{ |
||||
|
_templateRenderer = GetRequiredService<ITemplateRenderer>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Get_Rendered_Localized_Template_Content_With_Different_Cultures() |
||||
|
{ |
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.WelcomeEmail, |
||||
|
model: new |
||||
|
{ |
||||
|
name = "John" |
||||
|
}, |
||||
|
cultureName: "en" |
||||
|
)).ShouldBe("Welcome John to the abp.io!"); |
||||
|
|
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.WelcomeEmail, |
||||
|
model: new |
||||
|
{ |
||||
|
name = "John" |
||||
|
}, |
||||
|
cultureName: "tr" |
||||
|
)).ShouldBe("Merhaba John, abp.io'ya hoşgeldiniz!"); |
||||
|
|
||||
|
//"en-US" fallbacks to "en" since "en-US" doesn't exists and "en" is the fallback culture
|
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.WelcomeEmail, |
||||
|
model: new |
||||
|
{ |
||||
|
name = "John" |
||||
|
}, |
||||
|
cultureName: "en-US" |
||||
|
)).ShouldBe("Welcome John to the abp.io!"); |
||||
|
|
||||
|
//"fr" fallbacks to "en" since "fr" doesn't exists and "en" is the default culture
|
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.WelcomeEmail, |
||||
|
model: new |
||||
|
{ |
||||
|
Name = "John" //Intentionally written as PascalCase since Scriban supports it
|
||||
|
}, |
||||
|
cultureName: "fr" |
||||
|
)).ShouldBe("Welcome John to the abp.io!"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Get_Rendered_Localized_Template_Content_With_Stronly_Typed_Model() |
||||
|
{ |
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.WelcomeEmail, |
||||
|
model: new WelcomeEmailModel("John"), |
||||
|
cultureName: "en" |
||||
|
)).ShouldBe("Welcome John to the abp.io!"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Get_Rendered_Localized_Template_Content_With_Dictionary_Model() |
||||
|
{ |
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.WelcomeEmail, |
||||
|
model: new Dictionary<string, object>() { { "name", "John" } }, |
||||
|
cultureName: "en" |
||||
|
)).ShouldBe("Welcome John to the abp.io!"); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Get_Rendered_Inline_Localized_Template() |
||||
|
{ |
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.ForgotPasswordEmail, |
||||
|
cultureName: "en" |
||||
|
)).ShouldBe("*BEGIN*Hello. Please click to the following link to get an email to reset your password!*END*"); |
||||
|
|
||||
|
(await _templateRenderer.RenderAsync( |
||||
|
TestTemplates.ForgotPasswordEmail, |
||||
|
cultureName: "tr" |
||||
|
)).ShouldBe("*BEGIN*Merhaba. Please click to the following link to get an email to reset your password!*END*"); |
||||
|
} |
||||
|
|
||||
|
private class WelcomeEmailModel |
||||
|
{ |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
public WelcomeEmailModel() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public WelcomeEmailModel(string name) |
||||
|
{ |
||||
|
Name = name; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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") |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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"; |
||||
|
} |
||||
|
} |
||||
@ -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!");
|
||||
|
// }
|
||||
|
//}
|
||||
|
} |
||||
Loading…
Reference in new issue