From 84ae5cb0c8e0ec297a645f32b39695c3ea7003a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Wed, 24 Jul 2024 17:49:56 +0200 Subject: [PATCH] Make ResourcesTagHelper extensible (#16329) Co-authored-by: Hisham Bin Ateya --- .../Liquid/ResourcesTag.cs | 62 ++++------------- .../Services/ResourcesTagHelperProcessor.cs | 53 +++++++++++++++ .../OrchardCore.Resources/Startup.cs | 3 + .../IResourcesTagHelperProcessor.cs | 14 ++++ .../ResourceTagType.cs | 44 +++++++++++++ .../ResourcesTagHelperProcessorContext.cs | 14 ++++ .../TagHelpers/ResourcesTagHelper.cs | 66 +++++-------------- src/docs/releases/2.0.0.md | 4 ++ 8 files changed, 159 insertions(+), 101 deletions(-) create mode 100644 src/OrchardCore.Modules/OrchardCore.Resources/Services/ResourcesTagHelperProcessor.cs create mode 100644 src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/IResourcesTagHelperProcessor.cs create mode 100644 src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourceTagType.cs create mode 100644 src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourcesTagHelperProcessorContext.cs diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs index e88787defae..88770c2ed9c 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Liquid/ResourcesTag.cs @@ -16,69 +16,31 @@ public class ResourcesTag public static async ValueTask WriteToAsync(IReadOnlyList argumentsList, TextWriter writer, TextEncoder _, TemplateContext context) { var services = ((LiquidTemplateContext)context).Services; - var resourceManager = services.GetRequiredService(); + var processors = services.GetRequiredService>(); - var type = ResourceType.Footer; + var processorContext = new ResourcesTagHelperProcessorContext(ResourceTagType.Footer, writer); foreach (var argument in argumentsList) { switch (argument.Name) { -#pragma warning disable CA1806 // Do not ignore method results - case "type": Enum.TryParse((await argument.Expression.EvaluateAsync(context)).ToStringValue(), out type); break; -#pragma warning restore CA1806 // Do not ignore method results + case "type": + var typeString = (await argument.Expression.EvaluateAsync(context)).ToStringValue(); + if (Enum.TryParse(typeString, out var type)) + { + processorContext = processorContext with { Type = type }; + } + + break; } } - switch (type) + foreach (var processor in processors) { - case ResourceType.Meta: - resourceManager.RenderMeta(writer); - break; - - case ResourceType.HeadLink: - resourceManager.RenderHeadLink(writer); - break; - - case ResourceType.Stylesheet: - resourceManager.RenderStylesheet(writer); - break; - - case ResourceType.HeadScript: - resourceManager.RenderHeadScript(writer); - break; - - case ResourceType.FootScript: - resourceManager.RenderFootScript(writer); - break; - - case ResourceType.Header: - resourceManager.RenderMeta(writer); - resourceManager.RenderHeadLink(writer); - resourceManager.RenderStylesheet(writer); - resourceManager.RenderHeadScript(writer); - break; - - case ResourceType.Footer: - resourceManager.RenderFootScript(writer); - break; - - default: - break; + await processor.ProcessAsync(processorContext); } return Completion.Normal; } - - public enum ResourceType - { - Meta, - HeadLink, - Stylesheet, - HeadScript, - FootScript, - Header, - Footer - } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Services/ResourcesTagHelperProcessor.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Services/ResourcesTagHelperProcessor.cs new file mode 100644 index 00000000000..8ed49d0dd3a --- /dev/null +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Services/ResourcesTagHelperProcessor.cs @@ -0,0 +1,53 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using OrchardCore.ResourceManagement; + +namespace OrchardCore.Resources.Services; + +public class ResourcesTagHelperProcessor : IResourcesTagHelperProcessor +{ + private readonly IResourceManager _resourceManager; + private readonly ILogger _logger; + + public ResourcesTagHelperProcessor(IResourceManager resourceManager, ILogger logger) + { + _resourceManager = resourceManager; + _logger = logger; + } + + public Task ProcessAsync(ResourcesTagHelperProcessorContext context) + { + switch (context.Type) + { + case ResourceTagType.Meta: + _resourceManager.RenderMeta(context.Writer); + break; + case ResourceTagType.HeadLink: + _resourceManager.RenderHeadLink(context.Writer); + break; + case ResourceTagType.Stylesheet: + _resourceManager.RenderStylesheet(context.Writer); + break; + case ResourceTagType.HeadScript: + _resourceManager.RenderHeadScript(context.Writer); + break; + case ResourceTagType.FootScript: + _resourceManager.RenderFootScript(context.Writer); + break; + case ResourceTagType.Header: + _resourceManager.RenderMeta(context.Writer); + _resourceManager.RenderHeadLink(context.Writer); + _resourceManager.RenderStylesheet(context.Writer); + _resourceManager.RenderHeadScript(context.Writer); + break; + case ResourceTagType.Footer: + _resourceManager.RenderFootScript(context.Writer); + break; + default: + _logger.LogWarning("Unknown {TypeName} value \"{Value}\".", nameof(ResourceTagType), context.Type); + break; + } + + return Task.CompletedTask; + } +} diff --git a/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs b/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs index 1c8ccc57391..cb6cdb4e6d4 100644 --- a/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.Resources/Startup.cs @@ -5,6 +5,7 @@ using OrchardCore.Modules; using OrchardCore.ResourceManagement; using OrchardCore.Resources.Liquid; +using OrchardCore.Resources.Services; namespace OrchardCore.Resources { @@ -34,6 +35,8 @@ public override void ConfigureServices(IServiceCollection serviceCollection) var resourceConfiguration = _shellConfiguration.GetSection("OrchardCore_Resources"); serviceCollection.Configure(resourceConfiguration); + + serviceCollection.AddScoped(); } } } diff --git a/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/IResourcesTagHelperProcessor.cs b/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/IResourcesTagHelperProcessor.cs new file mode 100644 index 00000000000..e1dbc78baad --- /dev/null +++ b/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/IResourcesTagHelperProcessor.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; + +namespace OrchardCore.ResourceManagement; + +/// +/// Processes resources in the <resources /> tag helper. +/// +public interface IResourcesTagHelperProcessor +{ + /// + /// Invoked when rendering registered resources. + /// + Task ProcessAsync(ResourcesTagHelperProcessorContext context); +} diff --git a/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourceTagType.cs b/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourceTagType.cs new file mode 100644 index 00000000000..64b0cd3ad99 --- /dev/null +++ b/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourceTagType.cs @@ -0,0 +1,44 @@ +namespace OrchardCore.ResourceManagement; + +/// +/// The possible type values of the <resources type="..." /> Razor tag helper and the +/// {% resources type: "..." %} liquid tag, indicating the kinds of resources to be rendered. The value should be +/// chosen based on the tag's location in the document. +/// +public enum ResourceTagType +{ + /// + /// Resources that should be rendered along with . + /// + Meta, + + /// + /// Resources that should be rendered along with . + /// + HeadLink, + + /// + /// Resources that should be rendered along with . + /// + Stylesheet, + + /// + /// Resources that should be rendered along with . + /// + HeadScript, + + /// + /// Resources that should be rendered along with . + /// + FootScript, + + /// + /// Resources that should be rendered inside the /html/head element. + /// + Header, + + /// + /// Resources that should be rendered inside the /html/head element, near the end of the document. + /// + Footer +} diff --git a/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourcesTagHelperProcessorContext.cs b/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourcesTagHelperProcessorContext.cs new file mode 100644 index 00000000000..5cbf130164b --- /dev/null +++ b/src/OrchardCore/OrchardCore.ResourceManagement.Abstractions/ResourcesTagHelperProcessorContext.cs @@ -0,0 +1,14 @@ +using System.IO; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace OrchardCore.ResourceManagement; + +/// +/// The context passed to , to render the +/// <resources /> Razor tag helper and the {% resources %} liquid tag. +/// +/// The value indicating which types of resources to render. +/// The object that writes the rendered content into the HTML output. +public record ResourcesTagHelperProcessorContext( + ResourceTagType Type, + TextWriter Writer); diff --git a/src/OrchardCore/OrchardCore.ResourceManagement/TagHelpers/ResourcesTagHelper.cs b/src/OrchardCore/OrchardCore.ResourceManagement/TagHelpers/ResourcesTagHelper.cs index e95cdb49b2a..bccd406e402 100644 --- a/src/OrchardCore/OrchardCore.ResourceManagement/TagHelpers/ResourcesTagHelper.cs +++ b/src/OrchardCore/OrchardCore.ResourceManagement/TagHelpers/ResourcesTagHelper.cs @@ -1,81 +1,45 @@ using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Cysharp.Text; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.Logging; namespace OrchardCore.ResourceManagement.TagHelpers { - public enum ResourceType - { - Meta, - HeadLink, - Stylesheet, - HeadScript, - FootScript, - Header, - Footer - } - [HtmlTargetElement("resources", Attributes = nameof(Type))] public class ResourcesTagHelper : TagHelper { - public ResourceType Type { get; set; } - private readonly IResourceManager _resourceManager; private readonly ILogger _logger; + private readonly IEnumerable _processors; public ResourcesTagHelper( IResourceManager resourceManager, - ILogger logger) + ILogger logger, + IEnumerable processors) { _resourceManager = resourceManager; _logger = logger; + _processors = processors; } - public override void Process(TagHelperContext tagHelperContext, TagHelperOutput output) + public ResourceTagType Type { get; set; } + + public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { try { - using var sw = new ZStringWriter(); - - switch (Type) - { - case ResourceType.Meta: - _resourceManager.RenderMeta(sw); - break; + await using var writer = new ZStringWriter(); - case ResourceType.HeadLink: - _resourceManager.RenderHeadLink(sw); - break; + var processorContext = new ResourcesTagHelperProcessorContext(Type, writer); - case ResourceType.Stylesheet: - _resourceManager.RenderStylesheet(sw); - break; - - case ResourceType.HeadScript: - _resourceManager.RenderHeadScript(sw); - break; - - case ResourceType.FootScript: - _resourceManager.RenderFootScript(sw); - break; - - case ResourceType.Header: - _resourceManager.RenderMeta(sw); - _resourceManager.RenderHeadLink(sw); - _resourceManager.RenderStylesheet(sw); - _resourceManager.RenderHeadScript(sw); - break; - - case ResourceType.Footer: - _resourceManager.RenderFootScript(sw); - break; - - default: - break; + foreach (var processor in _processors) + { + await processor.ProcessAsync(processorContext); } - output.Content.AppendHtml(sw.ToString()); + output.Content.AppendHtml(writer.ToString()); } catch (Exception ex) { diff --git a/src/docs/releases/2.0.0.md b/src/docs/releases/2.0.0.md index 18a5b5b9377..e1f40a05d54 100644 --- a/src/docs/releases/2.0.0.md +++ b/src/docs/releases/2.0.0.md @@ -279,6 +279,10 @@ You may have to adjust your GraphQL queries in that case. Additionally, the `GetAliases` method in the `IIndexAliasProvider` interface is now asynchronous and has been renamed to `GetAliasesAsync`. Implementations of this interface should be modified by updating the method signature and ensure they handle asynchronous operations correctly. +### Resource Management + +Previously the `` Razor tag helper and the `{% resources type: "..." %}` Liquid tag were only capable of handling a hard-coded set of resource definition types (`script`, `stylesheet`, etc). Now both can be extended with `IResourcesTagHelperProcessor` to run custom rendering logic. To make this possible, the `OrchardCore.ResourceManagement.TagHelpers.ResourceType` and `OrchardCore.Resources.Liquid.ResourcesTag.ResourceType` enums have been replaced with a common `OrchardCore.ResourceManagement.ResourceTagType`. It was renamed to avoid confusion with `ResourceDefinition.Type`. This change is breaking in code, but it does not affect the uses of the Razor tag helper or the Liquid tag in templates. + ## Change Logs ### Azure AI Search Module