diff --git a/src/Nitride.Handlebars/ApplyStyleTemplate.cs b/src/Nitride.Handlebars/ApplyStyleTemplate.cs index 9f86737..debda6d 100644 --- a/src/Nitride.Handlebars/ApplyStyleTemplate.cs +++ b/src/Nitride.Handlebars/ApplyStyleTemplate.cs @@ -15,18 +15,18 @@ namespace Nitride.Handlebars; /// An operation that applies a common or shared template on the content of /// a document that includes theme or styling information. /// -public class ApplyStyleTemplate : OperationBase +[WithProperties] +public partial class ApplyStyleTemplate : OperationBase { - // TODO: This does not use [WithProperties] because the source generator hasn't been taught how to do generics. - private readonly HandlebarsTemplateCache cache; - private readonly IValidator> validator; + private readonly IValidator validator; - public ApplyStyleTemplate(HandlebarsTemplateCache cache) + public ApplyStyleTemplate( + IValidator validator, + HandlebarsTemplateCache cache) { - // TODO: Figure out why Autofac won't let us register IValidator of generic classes. - this.validator = new ApplyStyleTemplateValidator(); + this.validator = validator; this.cache = cache; } @@ -35,7 +35,7 @@ public class ApplyStyleTemplate : OperationBase /// entity. This allows for the website to customize what information is /// being passed to the template. /// - public Func? CreateModelCallback { get; set; } + public Func? CreateModelCallback { get; set; } /// /// Gets or sets the callback used to determine which template to use @@ -55,38 +55,11 @@ public class ApplyStyleTemplate : OperationBase return input.SelectEntity(this.Apply); } - /// - /// Sets the callback for the template and returns the operation to - /// chain operations. - /// - /// The callback to set. - /// The ApplyContentHandlebarsTemplate to chain requests. - public ApplyStyleTemplate WithCreateModelCallback(Func? callback) - { - this.CreateModelCallback = callback; - - return this; - } - - public ApplyStyleTemplate WithGetTemplateName(Func? callback) - { - this.GetTemplateName = callback; - - return this; - } - - public ApplyStyleTemplate WithHandlebars(IHandlebars? handlebars) - { - this.Handlebars = handlebars; - - return this; - } - private Entity Apply( Entity entity, ITextContent content) { - TModel model = this.CreateModelCallback!(entity); + object model = this.CreateModelCallback!(entity); string name = this.GetTemplateName!(entity); HandlebarsTemplate template = this.cache.GetNamedTemplate(name); string result = template(model!); diff --git a/src/Nitride.Handlebars/ApplyStyleTemplateValidator.cs b/src/Nitride.Handlebars/ApplyStyleTemplateValidator.cs index c755fcb..894bcab 100644 --- a/src/Nitride.Handlebars/ApplyStyleTemplateValidator.cs +++ b/src/Nitride.Handlebars/ApplyStyleTemplateValidator.cs @@ -2,7 +2,7 @@ using FluentValidation; namespace Nitride.Handlebars; -public class ApplyStyleTemplateValidator : AbstractValidator> +public class ApplyStyleTemplateValidator : AbstractValidator { public ApplyStyleTemplateValidator() { diff --git a/src/Nitride.Handlebars/Configuration/FileSystemHandlebarsTemplateLoader.cs b/src/Nitride.Handlebars/Configuration/FileSystemHandlebarsTemplateLoader.cs new file mode 100644 index 0000000..1c6b736 --- /dev/null +++ b/src/Nitride.Handlebars/Configuration/FileSystemHandlebarsTemplateLoader.cs @@ -0,0 +1,35 @@ +using System.IO; + +using HandlebarsDotNet; + +namespace Nitride.Handlebars.Configuration; + +/// +/// Loads the templates from the given directory. +/// +public class FileSystemHandlebarsTemplateLoader : IHandlebarsLoader +{ + private readonly DirectoryInfo directory; + + private readonly string pattern; + + public FileSystemHandlebarsTemplateLoader( + DirectoryInfo directory, + string pattern = "*.hbs") + { + this.directory = directory; + this.pattern = pattern; + } + + /// + public void Register(IHandlebars handlebars) + { + foreach (FileInfo file in this.directory.GetFiles(this.pattern)) + { + string name = Path.GetFileNameWithoutExtension(file.Name); + string content = File.ReadAllText(file.FullName); + + handlebars.RegisterTemplate(name, content); + } + } +} diff --git a/src/Nitride.Handlebars/Configuration/ForEachHandlebarsBlock.cs b/src/Nitride.Handlebars/Configuration/ForEachHandlebarsBlock.cs new file mode 100644 index 0000000..ed835e2 --- /dev/null +++ b/src/Nitride.Handlebars/Configuration/ForEachHandlebarsBlock.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; + +using HandlebarsDotNet; + +namespace Nitride.Handlebars.Configuration; + +/// +/// +/// A typesafe model of the template. +public class ForEachHandlebarsBlock : HandlebarsBlockBase +{ + public ForEachHandlebarsBlock(string helperName) + { + this.HelperName = helperName; + } + + /// + /// Gets or sets the list that needs to be rendered. + /// + public Func>? GetList { get; set; } + + /// + /// Gets or sets the callback that is called when nothing is found. + /// + public Action? NothingFound { get; set; } + + /// + protected override string HelperName { get; } + + public ForEachHandlebarsBlock WithGetList(Func>? callback) + { + this.GetList = callback; + + return this; + } + + public ForEachHandlebarsBlock WithNothingFoundText( + Action? callback) + { + this.NothingFound = callback; + + return this; + } + + /// + /// Sets the output to write out text when no items are found. + /// + /// The text to write out. + /// The same object for chained methods. + public ForEachHandlebarsBlock WithNothingFoundText(string text) + { + this.NothingFound = ( + output, + _1, + _2, + _3) => output.Write(text); + + return this; + } + + /// + protected override void Render( + EncodedTextWriter output, + BlockHelperOptions options, + Context context, + Arguments input) + { + if (context.Value is not TModel model) + { + throw new InvalidOperationException( + string.Format( + "Cannot apply the {0} on context value because it is {1}.", + nameof(ForEachHandlebarsBlock), + context.Value.GetType())); + } + + IEnumerable? list = this.GetList?.Invoke(model); + bool hasItems = false; + + if (list != null) + { + foreach (object item in list) + { + hasItems = true; + options.Template(output, item); + } + } + + if (!hasItems) + { + this.NothingFound?.Invoke(output, options, context, input); + } + } +} diff --git a/src/Nitride.Handlebars/Configuration/HandlebarsBlockBase.cs b/src/Nitride.Handlebars/Configuration/HandlebarsBlockBase.cs new file mode 100644 index 0000000..42f6427 --- /dev/null +++ b/src/Nitride.Handlebars/Configuration/HandlebarsBlockBase.cs @@ -0,0 +1,30 @@ +using HandlebarsDotNet; + +namespace Nitride.Handlebars.Configuration; + +/// +/// Describes a block helper which can be registered. +/// +public abstract class HandlebarsBlockBase : IHandlebarsLoader +{ + /// + /// Gets the name of the helper, which is how it is called in the template. + /// + protected abstract string HelperName { get; } + + public void Register(IHandlebars handlebars) + { + HandlebarsBlockHelper blockHelper = this.Render; + + handlebars.RegisterHelper(this.HelperName, blockHelper); + } + + /// + /// Renders the helper to the template. + /// + protected abstract void Render( + EncodedTextWriter output, + BlockHelperOptions options, + Context context, + Arguments input); +} diff --git a/src/Nitride.Handlebars/Configuration/IHandlebarsLoader.cs b/src/Nitride.Handlebars/Configuration/IHandlebarsLoader.cs new file mode 100644 index 0000000..eaf8383 --- /dev/null +++ b/src/Nitride.Handlebars/Configuration/IHandlebarsLoader.cs @@ -0,0 +1,15 @@ +using HandlebarsDotNet; + +namespace Nitride.Handlebars.Configuration; + +/// +/// Describes a dependency injected loader of templates or modules. +/// +public interface IHandlebarsLoader +{ + /// + /// Registers the given helper into the handlebars engine. + /// + /// The handlebars to register the helper into. + void Register(IHandlebars handlebars); +} diff --git a/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs b/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs index d6c4a2c..0813532 100644 --- a/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs +++ b/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs @@ -1,11 +1,39 @@ +using System; +using System.Collections.Generic; + using Autofac; +using Nitride.Handlebars.Configuration; + namespace Nitride.Handlebars; public static class NitrideHandlebarsBuilderExtensions { public static NitrideBuilder UseHandlebars(this NitrideBuilder builder) { - return builder.ConfigureContainer(x => x.RegisterModule()); + return builder + .ConfigureContainer(x => x.RegisterModule()); + } + + public static NitrideBuilder UseHandlebars( + this NitrideBuilder builder, + Func> configure) + { + builder.UseHandlebars(); + + builder.ConfigureContainer( + c => + { + IEnumerable loaders = configure(c); + + foreach (IHandlebarsLoader loader in loaders) + { + c.RegisterInstance(loader) + .As() + .SingleInstance(); + } + }); + + return builder; } } diff --git a/src/Nitride.Handlebars/NitrideHandlebarsModule.cs b/src/Nitride.Handlebars/NitrideHandlebarsModule.cs index 1004f25..c84ff35 100644 --- a/src/Nitride.Handlebars/NitrideHandlebarsModule.cs +++ b/src/Nitride.Handlebars/NitrideHandlebarsModule.cs @@ -1,5 +1,11 @@ +using System.Collections.Generic; + using Autofac; +using HandlebarsDotNet; + +using Nitride.Handlebars.Configuration; + namespace Nitride.Handlebars; public class NitrideHandlebarsModule : Module @@ -14,10 +20,20 @@ public class NitrideHandlebarsModule : Module .AsSelf() .SingleInstance(); - builder.RegisterGeneric(typeof(RenderContentTemplate<>)) - .As(typeof(RenderContentTemplate<>)); + builder.Register( + (context) => + { + IHandlebars handlebars = HandlebarsDotNet.Handlebars.Create(); + IEnumerable helpers = context.Resolve>(); - builder.RegisterGeneric(typeof(ApplyStyleTemplate<>)) - .As(typeof(ApplyStyleTemplate<>)); + foreach (IHandlebarsLoader helper in helpers) + { + helper.Register(handlebars); + } + + return handlebars; + }) + .As() + .SingleInstance(); } } diff --git a/src/Nitride.Handlebars/RenderContentTemplate.cs b/src/Nitride.Handlebars/RenderContentTemplate.cs index 9bca3a4..8622833 100644 --- a/src/Nitride.Handlebars/RenderContentTemplate.cs +++ b/src/Nitride.Handlebars/RenderContentTemplate.cs @@ -16,18 +16,18 @@ namespace Nitride.Handlebars; /// applied against the content and metadata and then replaces the content /// of that entity. /// -public class RenderContentTemplate : OperationBase +[WithProperties] +public partial class RenderContentTemplate : OperationBase { private readonly HandlebarsTemplateCache cache; - // TODO: This does not use [WithProperties] because the source generator hasn't been taught how to do generics. + private readonly IValidator validator; - private readonly IValidator> validator; - - public RenderContentTemplate(HandlebarsTemplateCache cache) + public RenderContentTemplate( + IValidator validator, + HandlebarsTemplateCache cache) { - // TODO: Figure out why Autofac won't let us register IValidator of generic classes. - this.validator = new RenderContentTemplateValidator(); + this.validator = validator; this.cache = cache; } @@ -36,7 +36,7 @@ public class RenderContentTemplate : OperationBase /// entity. This allows for the website to customize what information is /// being passed to the template. /// - public Func? CreateModelCallback { get; set; } + public Func? CreateModelCallback { get; set; } /// public override IEnumerable Run(IEnumerable input) @@ -46,36 +46,18 @@ public class RenderContentTemplate : OperationBase return input.SelectEntity(this.Apply); } - /// - /// Sets the callback for the template and returns the operation to - /// chain operations. - /// - /// The callback to set. - /// The ApplyContentHandlebarsTemplate to chain requests. - public RenderContentTemplate WithCreateModelCallback(Func? callback) - { - this.CreateModelCallback = callback; - - return this; - } - private Entity Apply( Entity entity, HasHandlebarsTemplate _, ITextContent content) { - // Create the model using the callback. - TModel model = this.CreateModelCallback!(entity); - - // Create a template from the contents. string text = content.GetText(); HandlebarsTemplate template = this.cache.GetLiteralTemplate(text); - - // Render the template and create a new entity with the updated - // text. + object model = this.CreateModelCallback!(entity); string result = template(model!); - return entity.Remove() - .SetTextContent(new StringTextContent(result)); + return entity + .Remove() + .SetTextContent(result); } } diff --git a/src/Nitride.Handlebars/RenderContentTemplateValidator.cs b/src/Nitride.Handlebars/RenderContentTemplateValidator.cs index c9fbe8f..9e5eb3b 100644 --- a/src/Nitride.Handlebars/RenderContentTemplateValidator.cs +++ b/src/Nitride.Handlebars/RenderContentTemplateValidator.cs @@ -2,7 +2,7 @@ using FluentValidation; namespace Nitride.Handlebars; -public class RenderContentTemplateValidator : AbstractValidator> +public class RenderContentTemplateValidator : AbstractValidator { public RenderContentTemplateValidator() { diff --git a/src/Nitride/NitrideBuilder.cs b/src/Nitride/NitrideBuilder.cs index 0719356..d184ee3 100644 --- a/src/Nitride/NitrideBuilder.cs +++ b/src/Nitride/NitrideBuilder.cs @@ -92,7 +92,8 @@ public class NitrideBuilder "Application name must be set, such as with the NitrideBuilder.WithApplicationName() to properly run."); } - return await ToolBoxBuilder.Create(this.nitrideModule.ApplicationName, this.arguments) + return await ToolBoxBuilder + .Create(this.nitrideModule.ApplicationName, this.arguments) .ConfigureContainer(this.ConfigureContainer) .Build() .RunAsync();