From b76ce526071dbe51a80bd0d73d0964ba41852ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Sat, 27 Jan 2024 13:43:25 +0100 Subject: [PATCH 01/29] Add JsonHelperExtensions with DataAttribute. --- .../Extensions/JsonHelperExtensions.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs b/Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs new file mode 100644 index 00000000..5c75963a --- /dev/null +++ b/Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Razor.TagHelpers; +using System.IO; +using System.Web; + +namespace Microsoft.AspNetCore.Mvc.Rendering; + +public static class JsonHelperExtensions +{ + /// + /// Returns a full HTML element attribute withe the given prefixed with data- and the + /// value appropriately encoded. + /// + public static IHtmlContent DataAttribute(this IJsonHelper helper, string name, object value) + { + using var stringWriter = new StringWriter(); + helper.Serialize(value).WriteTo(stringWriter, NullHtmlEncoder.Default); + + return new HtmlString($"data-{name}=\"{HttpUtility.HtmlAttributeEncode(stringWriter.ToString())}\""); + } +} From b97d71f7ba29d283bf7c407ad804136d219b3820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Thu, 8 Feb 2024 06:07:37 +0100 Subject: [PATCH 02/29] Add script-module extensions. --- .../ResourceManagement/ResourceManagerExtensions.cs | 7 +++++++ .../ResourceManagement/ResourceTypes.cs | 6 ++++++ 2 files changed, 13 insertions(+) create mode 100644 Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceTypes.cs diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 9d9168fe..5f709d98 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -1,3 +1,4 @@ +using Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; using Microsoft.AspNetCore.Html; using OrchardCore.ResourceManagement.TagHelpers; using System; @@ -45,6 +46,12 @@ public static HtmlString RenderAndTransformHeader( return new HtmlString(headerHtml); } + public static ResourceDefinition DefineScriptAsModule(this ResourceManifest manifest, string name) => + manifest.DefineResource(ResourceTypes.ScriptModule, name); + + public static RequireSettings RegisterScriptAsModule(this IResourceManager resourceManager, string name) => + resourceManager.RegisterResource(ResourceTypes.ScriptModule, name); + private static RequireSettings SetVersionIfAny(RequireSettings requireSettings, string version) { if (!string.IsNullOrEmpty(version)) requireSettings.UseVersion(version); diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceTypes.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceTypes.cs new file mode 100644 index 00000000..e1fdd0b4 --- /dev/null +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceTypes.cs @@ -0,0 +1,6 @@ +namespace Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; + +public static class ResourceTypes +{ + public const string ScriptModule = "script-module"; +} From a8212f7fce85f82d85d8bc2ef73b3cdba6209e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Tue, 13 Feb 2024 16:55:12 +0100 Subject: [PATCH 03/29] Add docs. --- .../ResourceManagement/ResourceManagerExtensions.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 5f709d98..43c6da24 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -46,10 +46,19 @@ public static HtmlString RenderAndTransformHeader( return new HtmlString(headerHtml); } - public static ResourceDefinition DefineScriptAsModule(this ResourceManifest manifest, string name) => + /// + /// Adds a script-module" resource to the manifest. All of these resources are mapped using so they can be imported by module type scripts using the import ... from + /// statement. + /// + public static ResourceDefinition DefineScriptModule(this ResourceManifest manifest, string name) => manifest.DefineResource(ResourceTypes.ScriptModule, name); - public static RequireSettings RegisterScriptAsModule(this IResourceManager resourceManager, string name) => + /// + /// Registers a script-module" resource to be used on the current page. These can be rendered using as <script src="..." type="module"> elements. + /// + public static RequireSettings RegisterScriptModule(this IResourceManager resourceManager, string name) => resourceManager.RegisterResource(ResourceTypes.ScriptModule, name); private static RequireSettings SetVersionIfAny(RequireSettings requireSettings, string version) From 1aa3c144021015f2eb32d408ad8b4d082c441ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Tue, 13 Feb 2024 16:55:43 +0100 Subject: [PATCH 04/29] Add methods for rendering ES modules. --- .../ResourceManagerExtensions.cs | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 43c6da24..a6d844d6 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -1,8 +1,14 @@ using Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using OrchardCore.ResourceManagement.TagHelpers; using System; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Text.Json; namespace OrchardCore.ResourceManagement; @@ -61,6 +67,105 @@ public static ResourceDefinition DefineScriptModule(this ResourceManifest manife public static RequireSettings RegisterScriptModule(this IResourceManager resourceManager, string name) => resourceManager.RegisterResource(ResourceTypes.ScriptModule, name); + /// + /// Turns the required script-module" resources into <script src="..." type="module"> elements. + /// + public static IEnumerable GetRequiredScriptModuleTags( + this IResourceManager resourceManager, + string applicationPath = null, + Func filter = null) + { + var contexts = resourceManager.GetRequiredResources(ResourceTypes.ScriptModule); + if (filter != null) contexts = contexts.Where(filter); + + return contexts.Select(context => new TagBuilder("script") + { + TagRenderMode = TagRenderMode.SelfClosing, + Attributes = + { + ["type"] = "module", + ["src"] = context.Resource.GetResourceUrl( + context.FileVersionProvider, + context.Settings.DebugMode, + context.Settings.CdnMode, + applicationPath), + }, + }); + } + + /// + /// Turns the required script-module" resource with the into a + /// <script src="..." type="module"> element. + /// + public static TagBuilder GetRequiredScriptModuleTag( + this IResourceManager resourceManager, + string applicationPath, + string resourceName) => + resourceManager + .GetRequiredScriptModuleTags( + applicationPath, + context => context.Resource.Name.EqualsOrdinalIgnoreCase(resourceName)) + .FirstOrDefault(); + + /// + /// Returns a <script type="importmap"> element that maps all the registered module resources by + /// resource name to their respective URLs so you can import these resources in your module type scripts using + /// import someModule from 'resourceName' instead of using the full resource URL. This way import will work + /// regardless of your CDN configuration. + /// + public static IHtmlContent GetScriptModuleMap( + this ResourceManagementOptions resources, + IFileVersionProvider fileVersionProvider) + { + var imports = resources + .ResourceManifests + .SelectMany(manifest => manifest.GetResources(ResourceTypes.ScriptModule).Values) + .SelectMany(list => list) + .ToDictionary( + resource => resource.Name, + resource => resource.GetResourceUrl( + fileVersionProvider, + resources.DebugMode, + resources.UseCdn, + resources.ContentBasePath)); + + var tagBuilder = new TagBuilder("script") + { + TagRenderMode = TagRenderMode.Normal, + Attributes = { ["type"] = "importmap" }, + }; + + tagBuilder.InnerHtml.AppendHtml(JsonSerializer.Serialize(new { imports })); + return tagBuilder; + } + + private static string GetResourceUrl( + this ResourceDefinition definition, + IFileVersionProvider fileVersionProvider, + bool isDebug, + bool isCdn, + PathString basePath) + { + static string Coalesce(params string[] strings) => strings.Find(str => !string.IsNullOrEmpty(str)); + + var url = (isDebug, isCdn) switch + { + (true, true) => Coalesce(definition.UrlCdnDebug, definition.UrlDebug, definition.UrlCdn, definition.Url), + (true, false) => Coalesce(definition.UrlDebug, definition.Url, definition.UrlCdnDebug, definition.UrlCdn), + (false, true) => Coalesce(definition.UrlCdn, definition.Url, definition.UrlCdnDebug, definition.UrlDebug), + (false, false) => Coalesce(definition.Url, definition.UrlDebug, definition.UrlCdn, definition.UrlCdnDebug), + }; + + if (string.IsNullOrEmpty(url)) return url; + + if (url.StartsWith("~/", StringComparison.Ordinal)) + { + url = basePath + url[1..]; + } + + return fileVersionProvider.AddFileVersionToPath(basePath, url); + } + private static RequireSettings SetVersionIfAny(RequireSettings requireSettings, string version) { if (!string.IsNullOrEmpty(version)) requireSettings.UseVersion(version); From 3a05d7af30748732ec5ac793c55ae78b6dca2e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Tue, 13 Feb 2024 21:09:17 +0100 Subject: [PATCH 05/29] More documentation. --- .../ResourceManagerExtensions.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index a6d844d6..e096cb94 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -70,9 +70,16 @@ public static RequireSettings RegisterScriptModule(this IResourceManager resourc /// /// Turns the required script-module" resources into <script src="..." type="module"> elements. /// + /// + /// The path that's used to resolve ~ in the resource URLs. Typically should be used.. + /// + /// + /// If not it's used to select which required resources should be considered. + /// public static IEnumerable GetRequiredScriptModuleTags( this IResourceManager resourceManager, - string applicationPath = null, + string basePath = null, Func filter = null) { var contexts = resourceManager.GetRequiredResources(ResourceTypes.ScriptModule); @@ -88,7 +95,7 @@ public static IEnumerable GetRequiredScriptModuleTags( context.FileVersionProvider, context.Settings.DebugMode, context.Settings.CdnMode, - applicationPath), + basePath), }, }); } @@ -97,14 +104,19 @@ public static IEnumerable GetRequiredScriptModuleTags( /// Turns the required script-module" resource with the into a /// <script src="..." type="module"> element. /// + /// + /// The path that's used to resolve ~ in the resource URLs. Typically should be used.. + /// + /// The expected value of . public static TagBuilder GetRequiredScriptModuleTag( this IResourceManager resourceManager, - string applicationPath, + string basePath, string resourceName) => resourceManager .GetRequiredScriptModuleTags( - applicationPath, - context => context.Resource.Name.EqualsOrdinalIgnoreCase(resourceName)) + basePath, + context => context.Resource.Name == resourceName) .FirstOrDefault(); /// From d3939fc4eda734e59c9284de22a531320ebe0ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Tue, 13 Feb 2024 22:05:02 +0100 Subject: [PATCH 06/29] Bug fix. --- .../ResourceManagement/ResourceManagerExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index e096cb94..304ebef4 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -87,7 +87,7 @@ public static IEnumerable GetRequiredScriptModuleTags( return contexts.Select(context => new TagBuilder("script") { - TagRenderMode = TagRenderMode.SelfClosing, + TagRenderMode = TagRenderMode.Normal, Attributes = { ["type"] = "module", @@ -172,7 +172,7 @@ private static string GetResourceUrl( if (url.StartsWith("~/", StringComparison.Ordinal)) { - url = basePath + url[1..]; + url = basePath.Value?.TrimEnd('/') + url[1..]; } return fileVersionProvider.AddFileVersionToPath(basePath, url); From 69019b88c7a47ff13c766c161ca13126c5b1160e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Wed, 14 Feb 2024 19:28:56 +0100 Subject: [PATCH 07/29] Add GetScriptModuleImportMap to IOrchardHelper --- .../ResourceManagerExtensions.cs | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 304ebef4..f341cec6 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -1,8 +1,11 @@ +using AngleSharp.Common; using Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using OrchardCore.ResourceManagement.TagHelpers; using System; using System.Collections.Generic; @@ -119,27 +122,23 @@ public static TagBuilder GetRequiredScriptModuleTag( context => context.Resource.Name == resourceName) .FirstOrDefault(); - /// - /// Returns a <script type="importmap"> element that maps all the registered module resources by - /// resource name to their respective URLs so you can import these resources in your module type scripts using - /// import someModule from 'resourceName' instead of using the full resource URL. This way import will work - /// regardless of your CDN configuration. - /// - public static IHtmlContent GetScriptModuleMap( - this ResourceManagementOptions resources, + + /// + public static IHtmlContent GetScriptModuleImportMap( + this ResourceManagementOptions resourceOptions, + IEnumerable resourceManifests, IFileVersionProvider fileVersionProvider) { - var imports = resources - .ResourceManifests + var imports = (resourceManifests ?? resourceOptions.ResourceManifests) .SelectMany(manifest => manifest.GetResources(ResourceTypes.ScriptModule).Values) .SelectMany(list => list) .ToDictionary( resource => resource.Name, resource => resource.GetResourceUrl( fileVersionProvider, - resources.DebugMode, - resources.UseCdn, - resources.ContentBasePath)); + resourceOptions.DebugMode, + resourceOptions.UseCdn, + resourceOptions.ContentBasePath)); var tagBuilder = new TagBuilder("script") { @@ -151,6 +150,24 @@ public static IHtmlContent GetScriptModuleMap( return tagBuilder; } + /// + /// Returns a <script type="importmap"> element that maps all the registered module resources by + /// resource name to their respective URLs so you can import these resources in your module type scripts using + /// import someModule from 'resourceName' instead of using the full resource URL. This way import will work + /// regardless of your CDN configuration. + /// + public static IHtmlContent GetScriptModuleImportMap(this IOrchardHelper helper) + { + var serviceProvider = helper.HttpContext.RequestServices; + var resourceOptions = serviceProvider.GetRequiredService>().Value; + var resourceManager = serviceProvider.GetRequiredService(); + var fileVersionProvider = serviceProvider.GetRequiredService(); + + return resourceOptions.GetScriptModuleImportMap( + resourceOptions.ResourceManifests.Concat(resourceManager.InlineManifest), + fileVersionProvider); + } + private static string GetResourceUrl( this ResourceDefinition definition, IFileVersionProvider fileVersionProvider, From 7c160d187f7f49429ae4d494cbaf678a0c536fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Fri, 16 Feb 2024 15:32:22 +0100 Subject: [PATCH 08/29] Retain declared attributes. --- .../ResourceManagerExtensions.cs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index f341cec6..8154318d 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -88,18 +88,24 @@ public static IEnumerable GetRequiredScriptModuleTags( var contexts = resourceManager.GetRequiredResources(ResourceTypes.ScriptModule); if (filter != null) contexts = contexts.Where(filter); - return contexts.Select(context => new TagBuilder("script") + return contexts.Select(context => { - TagRenderMode = TagRenderMode.Normal, - Attributes = + var builder = new TagBuilder("script") { - ["type"] = "module", - ["src"] = context.Resource.GetResourceUrl( - context.FileVersionProvider, - context.Settings.DebugMode, - context.Settings.CdnMode, - basePath), - }, + TagRenderMode = TagRenderMode.Normal, + Attributes = + { + ["type"] = "module", + ["src"] = context.Resource.GetResourceUrl( + context.FileVersionProvider, + context.Settings.DebugMode, + context.Settings.CdnMode, + basePath), + }, + }; + builder.MergeAttributes(context.Resource.Attributes, replaceExisting: true); + + return builder; }); } From d97411a5bd4f4256ad193e63fc9ee244768cb543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Fri, 16 Feb 2024 19:11:16 +0100 Subject: [PATCH 09/29] Module display through filter. --- .../ResourceManagerExtensions.cs | 18 ----- .../ScriptModuleResourceFilter.cs | 78 +++++++++++++++++++ 2 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 8154318d..3dbeb63b 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -156,24 +156,6 @@ public static IHtmlContent GetScriptModuleImportMap( return tagBuilder; } - /// - /// Returns a <script type="importmap"> element that maps all the registered module resources by - /// resource name to their respective URLs so you can import these resources in your module type scripts using - /// import someModule from 'resourceName' instead of using the full resource URL. This way import will work - /// regardless of your CDN configuration. - /// - public static IHtmlContent GetScriptModuleImportMap(this IOrchardHelper helper) - { - var serviceProvider = helper.HttpContext.RequestServices; - var resourceOptions = serviceProvider.GetRequiredService>().Value; - var resourceManager = serviceProvider.GetRequiredService(); - var fileVersionProvider = serviceProvider.GetRequiredService(); - - return resourceOptions.GetScriptModuleImportMap( - resourceOptions.ResourceManifests.Concat(resourceManager.InlineManifest), - fileVersionProvider); - } - private static string GetResourceUrl( this ResourceDefinition definition, IFileVersionProvider fileVersionProvider, diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs new file mode 100644 index 00000000..79a395ee --- /dev/null +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -0,0 +1,78 @@ +using AngleSharp.Common; +using Lombiq.HelpfulLibraries.OrchardCore.Contents; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.Options; +using OrchardCore.DisplayManagement.Descriptors; +using OrchardCore.DisplayManagement.Layout; +using OrchardCore.DisplayManagement.Shapes; +using OrchardCore.DisplayManagement.Theming; +using OrchardCore.ResourceManagement; +using System.Linq; +using System.Threading.Tasks; + +namespace Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; + +public record ScriptModuleResourceFilter( + IFileVersionProvider FileVersionProvider, + ILayoutAccessor LayoutAccessor, + IResourceManager ResourceManager, + IOptions ResourceManagerOptions, + IShapeTableManager ShapeTableManager, + IThemeManager ThemeManager) : IAsyncResultFilter +{ + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + var theme = await ThemeManager.GetThemeAsync(); + var shapeTable = ShapeTableManager.GetShapeTable(theme.Id); + var shape = new Shape + { + Metadata = + { + Type = nameof(ScriptModuleResourceFilter), + }, + }; + + var shapeDescriptor = new ShapeDescriptor + { + ShapeType = nameof(ScriptModuleResourceFilter), + Bindings = + { + [nameof(ScriptModuleResourceFilter)] = new ShapeBinding + { + BindingName = nameof(ScriptModuleResourceFilter), + BindingAsync = _ => Task.FromResult(Display()), + }, + }, + }; + + shapeTable.Descriptors[shapeDescriptor.ShapeType] = shapeDescriptor; + foreach (var binding in shapeDescriptor.Bindings) + { + shapeTable.Bindings[binding.Key] = binding.Value; + } + + await LayoutAccessor.AddShapeToZoneAsync("Content", shape, "After"); + await next(); + } + + private IHtmlContent Display() + { + var options = ResourceManagerOptions.Value; + var scriptElements = ResourceManager + .GetRequiredScriptModuleTags(options.ContentBasePath) + .ToList(); + + if (!scriptElements.Any()) return null; + + var importMap = options.GetScriptModuleImportMap( + options.ResourceManifests.Concat(ResourceManager.InlineManifest), + FileVersionProvider); + + var content = new HtmlContentBuilder(capacity: scriptElements.Count + 1).AppendHtml(importMap); + foreach (var script in scriptElements) content.AppendHtml(script); + + return content; + } +} From ac541ba4ae059ba4cb0218b64dcb3941fc41ff92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Fri, 16 Feb 2024 19:55:29 +0100 Subject: [PATCH 10/29] Add CreateAdHocShape. --- .../ScriptModuleResourceFilter.cs | 41 ++--------- .../Shapes/ShapeExtensions.cs | 68 +++++++++++++++++++ 2 files changed, 74 insertions(+), 35 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 79a395ee..75f4dc31 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -4,10 +4,8 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.Extensions.Options; -using OrchardCore.DisplayManagement.Descriptors; +using OrchardCore.DisplayManagement.Implementation; using OrchardCore.DisplayManagement.Layout; -using OrchardCore.DisplayManagement.Shapes; -using OrchardCore.DisplayManagement.Theming; using OrchardCore.ResourceManagement; using System.Linq; using System.Threading.Tasks; @@ -18,46 +16,19 @@ public record ScriptModuleResourceFilter( IFileVersionProvider FileVersionProvider, ILayoutAccessor LayoutAccessor, IResourceManager ResourceManager, - IOptions ResourceManagerOptions, - IShapeTableManager ShapeTableManager, - IThemeManager ThemeManager) : IAsyncResultFilter + IOptions ResourceManagerOptions) : IAsyncResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { - var theme = await ThemeManager.GetThemeAsync(); - var shapeTable = ShapeTableManager.GetShapeTable(theme.Id); - var shape = new Shape - { - Metadata = - { - Type = nameof(ScriptModuleResourceFilter), - }, - }; - - var shapeDescriptor = new ShapeDescriptor - { - ShapeType = nameof(ScriptModuleResourceFilter), - Bindings = - { - [nameof(ScriptModuleResourceFilter)] = new ShapeBinding - { - BindingName = nameof(ScriptModuleResourceFilter), - BindingAsync = _ => Task.FromResult(Display()), - }, - }, - }; - - shapeTable.Descriptors[shapeDescriptor.ShapeType] = shapeDescriptor; - foreach (var binding in shapeDescriptor.Bindings) - { - shapeTable.Bindings[binding.Key] = binding.Value; - } + var shape = await context.HttpContext.RequestServices.CreateAdHocShapeForCurrentThemeAsync( + nameof(ScriptModuleResourceFilter), + _ => Task.FromResult(DisplayScriptModuleResources())); await LayoutAccessor.AddShapeToZoneAsync("Content", shape, "After"); await next(); } - private IHtmlContent Display() + private IHtmlContent DisplayScriptModuleResources() { var options = ResourceManagerOptions.Value; var scriptElements = ResourceManager diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs index 3b60a7d5..ce9a2b1c 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs @@ -1,6 +1,13 @@ +using Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; +using Microsoft.AspNetCore.Html; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.DisplayManagement.Descriptors; +using OrchardCore.DisplayManagement.Shapes; +using OrchardCore.DisplayManagement.Theming; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace OrchardCore.DisplayManagement.Implementation; @@ -48,4 +55,65 @@ public static T As(this IShape shape) return result; } + + /// + /// Creates a new shape with the given . If the type is not yet in the , then a new descriptor is added with binding that uses . + /// If is null or empty, a new random unique name is generated. + /// + public static IShape CreateAdHocShape(this ShapeTable shapeTable, string type, Func> displayAsync) + { + if (string.IsNullOrEmpty(type)) type = $"AdHocShape_{Guid.NewGuid():D}"; + + var shape = new Shape + { + Metadata = + { + Type = type, + }, + }; + + if (shapeTable.Descriptors.ContainsKey(type)) return shape; + + var shapeDescriptor = new ShapeDescriptor + { + ShapeType = type, + Bindings = + { + [type] = new ShapeBinding + { + BindingName = type, + BindingAsync = displayAsync, + }, + }, + }; + + shapeTable.Descriptors[shapeDescriptor.ShapeType] = shapeDescriptor; + foreach (var binding in shapeDescriptor.Bindings) + { + shapeTable.Bindings[binding.Key] = binding.Value; + } + + return shape; + } + + /// + /// Creates a new shape with the given in the shape table of the current front-end theme. If + /// the type is not yet in the theme's , then a new descriptor is added with binding that + /// uses . If is null or empty, a new random unique name is + /// generated. + /// + public static async Task CreateAdHocShapeForCurrentThemeAsync( + this IServiceProvider provider, + string type, + Func> displayAsync) + { + var themeManager = provider.GetRequiredService(); + var shapeTableManager = provider.GetRequiredService(); + + var theme = await themeManager.GetThemeAsync(); + var shapeTable = shapeTableManager.GetShapeTable(theme.Id); + + return shapeTable.CreateAdHocShape(type, displayAsync); + } } From 8b30c3aeba6ec5a614c52b21fbe2c1869e759b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Fri, 16 Feb 2024 19:55:35 +0100 Subject: [PATCH 11/29] Add docs. --- .../Docs/ResourceManagement.md | 7 +++++++ Lombiq.HelpfulLibraries.OrchardCore/Docs/Shapes.md | 2 +- .../ResourceManagement/ScriptModuleResourceFilter.cs | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md b/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md index 3ad7a763..4a9b2f61 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md +++ b/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md @@ -30,6 +30,13 @@ public class ResourceFilters : IResourceFilterProvider } ``` +## Javascript Module Support + +The `ScriptModuleResourceFilter` makes it possible to register JS modules in a way that they can be imported by name, so no bundling or importing by URL is necessary. Once you've added it to your service collection (`services.AddAsyncResultFilter();`) you can register modules with the `ResourceManifest.DefineScriptModule(resourceName)` extension method and require them using the `IResourceManager.RegisterScriptModule(resourceName)` etension method. + +You don't even have to register dependencies, because thanks to the [importmap script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) generated by the `ScriptModuleResourceFilter`, you can import any resource using tge `import * from 'resourceName'` syntax. You can see an example in Lombiq.VueJs where Vue 3 support is added ahead of Orchard Core by [importing Vue 3 as a JS module](https://github.com/Lombiq/Orchard-Vue.js/blob/dev/Lombiq.VueJs/Assets/Scripts/vue-component-app.mjs#L1C4-L1C4). + + ## Extensions - `ApplicationBuilderExtensions`: Shortcut extensions for application setup, such as `UseResourceFilters()` (see above). diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Docs/Shapes.md b/Lombiq.HelpfulLibraries.OrchardCore/Docs/Shapes.md index d1ddf2f6..a3d37e49 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Docs/Shapes.md +++ b/Lombiq.HelpfulLibraries.OrchardCore/Docs/Shapes.md @@ -4,7 +4,7 @@ - `LayoutExtensions`: Adds features for adding shapes to the layout via `ILayoutAccessor`. - `ServiceCollectionExtensions`: Allows adding `ShapeRenderer` to the service collection via `AddShapeRenderer()`. -- `ShapeExtensions`: Some shortcuts for managing shapes. +- `ShapeExtensions`: Some shortcuts for managing shapes and a pair of extensions for creating ad-hoc shapes and injecting them into the shape table. ## Shape rendering diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 75f4dc31..f9a505e8 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -12,6 +12,11 @@ namespace Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; +/// +/// A filter that looks for the required script-module" resources. If there were any, it injects the input map +/// (used for mapping module names to URLs) of all registered module resources and the script blocks of the currently +/// required resource. +/// public record ScriptModuleResourceFilter( IFileVersionProvider FileVersionProvider, ILayoutAccessor LayoutAccessor, From b60cb8b7fc1d30e6c3dd5f0b4238452eb487d62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Fri, 16 Feb 2024 20:13:44 +0100 Subject: [PATCH 12/29] Bug fix. --- .../ScriptModuleResourceFilter.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index f9a505e8..ccb1562e 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -3,10 +3,12 @@ using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OrchardCore.DisplayManagement.Implementation; using OrchardCore.DisplayManagement.Layout; using OrchardCore.ResourceManagement; +using System; using System.Linq; using System.Threading.Tasks; @@ -17,34 +19,36 @@ namespace Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; /// (used for mapping module names to URLs) of all registered module resources and the script blocks of the currently /// required resource. /// -public record ScriptModuleResourceFilter( - IFileVersionProvider FileVersionProvider, - ILayoutAccessor LayoutAccessor, - IResourceManager ResourceManager, - IOptions ResourceManagerOptions) : IAsyncResultFilter +public record ScriptModuleResourceFilter(ILayoutAccessor LayoutAccessor) : IAsyncResultFilter { public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { var shape = await context.HttpContext.RequestServices.CreateAdHocShapeForCurrentThemeAsync( nameof(ScriptModuleResourceFilter), - _ => Task.FromResult(DisplayScriptModuleResources())); + displayContext => Task.FromResult(DisplayScriptModuleResources(displayContext.ServiceProvider))); await LayoutAccessor.AddShapeToZoneAsync("Content", shape, "After"); await next(); } - private IHtmlContent DisplayScriptModuleResources() + // We can't safely inject resources from constructor here, as some get disposed by the time this display takes place. + private IHtmlContent DisplayScriptModuleResources(IServiceProvider serviceProvider) { - var options = ResourceManagerOptions.Value; - var scriptElements = ResourceManager + // Won't work correctly with injected resources, the scriptElements below will be empty. Possibly related to the + // IResourceManager.InlineManifest being different. + var resourceManager = serviceProvider.GetRequiredService(); + var options = serviceProvider.GetRequiredService>().Value; + var fileVersionProvider = serviceProvider.GetRequiredService(); + + var scriptElements = resourceManager .GetRequiredScriptModuleTags(options.ContentBasePath) .ToList(); if (!scriptElements.Any()) return null; var importMap = options.GetScriptModuleImportMap( - options.ResourceManifests.Concat(ResourceManager.InlineManifest), - FileVersionProvider); + options.ResourceManifests.Concat(resourceManager.InlineManifest), + fileVersionProvider); var content = new HtmlContentBuilder(capacity: scriptElements.Count + 1).AppendHtml(importMap); foreach (var script in scriptElements) content.AppendHtml(script); From 1a04e0189798f9e27c4667412a740d63e10a6bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Sat, 17 Feb 2024 21:03:27 +0100 Subject: [PATCH 13/29] GetScriptModuleImportMap shortcuts --- .../ResourceManagerExtensions.cs | 24 +++++++++++++++++-- .../ScriptModuleResourceFilter.cs | 11 ++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 3dbeb63b..e4aee09a 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -128,8 +128,12 @@ public static TagBuilder GetRequiredScriptModuleTag( context => context.Resource.Name == resourceName) .FirstOrDefault(); - - /// + /// + /// Returns a <script type="importmap"> element that maps all the registered module resources by + /// resource name to their respective URLs so you can import these resources in your module type scripts using + /// import someModule from 'resourceName' instead of using the full resource URL. This way import will work + /// regardless of your CDN configuration. + /// public static IHtmlContent GetScriptModuleImportMap( this ResourceManagementOptions resourceOptions, IEnumerable resourceManifests, @@ -156,6 +160,22 @@ public static IHtmlContent GetScriptModuleImportMap( return tagBuilder; } + /// + internal static IHtmlContent GetScriptModuleImportMap(this IServiceProvider serviceProvider) + { + var options = serviceProvider.GetRequiredService>().Value; + var resourceManager = serviceProvider.GetRequiredService(); + var fileVersionProvider = serviceProvider.GetRequiredService(); + + return options.GetScriptModuleImportMap( + options.ResourceManifests.Concat(resourceManager.InlineManifest), + fileVersionProvider); + } + + /// + public static IHtmlContent GetScriptModuleImportMap(this IOrchardHelper helper) => + helper.HttpContext.RequestServices.GetScriptModuleImportMap(); + private static string GetResourceUrl( this ResourceDefinition definition, IFileVersionProvider fileVersionProvider, diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index ccb1562e..87387402 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -38,18 +38,11 @@ private IHtmlContent DisplayScriptModuleResources(IServiceProvider serviceProvid // IResourceManager.InlineManifest being different. var resourceManager = serviceProvider.GetRequiredService(); var options = serviceProvider.GetRequiredService>().Value; - var fileVersionProvider = serviceProvider.GetRequiredService(); - - var scriptElements = resourceManager - .GetRequiredScriptModuleTags(options.ContentBasePath) - .ToList(); + var scriptElements = resourceManager.GetRequiredScriptModuleTags(options.ContentBasePath).ToList(); if (!scriptElements.Any()) return null; - var importMap = options.GetScriptModuleImportMap( - options.ResourceManifests.Concat(resourceManager.InlineManifest), - fileVersionProvider); - + var importMap = serviceProvider.GetScriptModuleImportMap(); var content = new HtmlContentBuilder(capacity: scriptElements.Count + 1).AppendHtml(importMap); foreach (var script in scriptElements) content.AppendHtml(script); From b7a7b16f8923243f25e7a0a84b8dee7d3b238e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Sun, 18 Feb 2024 17:31:53 +0100 Subject: [PATCH 14/29] Add MergeDirectiveValues. --- .../IContentSecurityPolicyProvider.cs | 21 ++++++++++++++++++- ...yAttributeContentSecurityPolicyProvider.cs | 7 ++++--- ...rceManagerContentSecurityPolicyProvider.cs | 4 +--- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs index 10c464ee..503eba69 100644 --- a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using static Lombiq.HelpfulLibraries.AspNetCore.Security.ContentSecurityPolicyDirectives; @@ -22,7 +24,11 @@ public interface IContentSecurityPolicyProvider /// Returns the first non-empty directive from the or or an empty /// string. /// - public static string GetDirective(IDictionary securityPolicies, params string[] names) + public static string GetDirective(IDictionary securityPolicies, params string[] names) => + GetDirective(securityPolicies, names.AsEnumerable()); + + /// + public static string GetDirective(IDictionary securityPolicies, IEnumerable names) { foreach (var name in names) { @@ -34,4 +40,17 @@ public static string GetDirective(IDictionary securityPolicies, return securityPolicies.GetMaybe(DefaultSrc) ?? string.Empty; } + + /// + /// Updates the directive (the first entry of the ) by merging its space + /// separated values with the values from . + /// + public static void MergeDirectiveValues( + IDictionary securityPolicies, + IEnumerable directiveNameChain, + params string[] otherValues) + { + var nameChain = directiveNameChain.AsList(); + securityPolicies[nameChain[0]] = GetDirective(securityPolicies, nameChain).MergeWordSets(otherValues); + } } diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Security/ContentSecurityPolicyAttributeContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.OrchardCore/Security/ContentSecurityPolicyAttributeContentSecurityPolicyProvider.cs index 7c8c3b59..ef6aa8b1 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Security/ContentSecurityPolicyAttributeContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Security/ContentSecurityPolicyAttributeContentSecurityPolicyProvider.cs @@ -65,9 +65,10 @@ public ValueTask UpdateAsync(IDictionary securityPolicies, HttpC { foreach (var attribute in actionDescriptor.MethodInfo.GetCustomAttributes()) { - securityPolicies[ScriptSrc] = IContentSecurityPolicyProvider - .GetDirective(securityPolicies, attribute.DirectiveNames) - .MergeWordSets(attribute.DirectiveValue); + IContentSecurityPolicyProvider.MergeDirectiveValues( + securityPolicies, + attribute.DirectiveNames, + attribute.DirectiveValue); } } diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs index cbae5b7b..40e5add5 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs @@ -32,9 +32,7 @@ public ValueTask UpdateAsync(IDictionary securityPolicies, HttpC if (resourceExists) { - securityPolicies[DirectiveName] = IContentSecurityPolicyProvider - .GetDirective(securityPolicies, DirectiveNameChain.ToArray()) - .MergeWordSets(DirectiveValue); + IContentSecurityPolicyProvider.MergeDirectiveValues(securityPolicies, DirectiveNameChain, DirectiveValue); } return ThenUpdateAsync(securityPolicies, context, resourceExists); From 253fd2d6de0acc911bcf8ac79d3140728ba0a7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Sun, 18 Feb 2024 18:04:46 +0100 Subject: [PATCH 15/29] Fix xmldoc. --- .../ResourceManagement/ResourceManagerExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index e4aee09a..4c9070c9 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -57,7 +57,7 @@ public static HtmlString RenderAndTransformHeader( /// /// Adds a script-module" resource to the manifest. All of these resources are mapped using so they can be imported by module type scripts using the import ... from + /// cref="GetScriptModuleImportMap"/> so they can be imported by module type scripts using the import ... from /// statement. /// public static ResourceDefinition DefineScriptModule(this ResourceManifest manifest, string name) => From 9276e32b76da17a7dc8d9b9c4225914c3d2d5359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Sun, 18 Feb 2024 18:18:05 +0100 Subject: [PATCH 16/29] Spelling. --- .../Docs/ResourceManagement.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md b/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md index 4a9b2f61..3ce34591 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md +++ b/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md @@ -32,9 +32,9 @@ public class ResourceFilters : IResourceFilterProvider ## Javascript Module Support -The `ScriptModuleResourceFilter` makes it possible to register JS modules in a way that they can be imported by name, so no bundling or importing by URL is necessary. Once you've added it to your service collection (`services.AddAsyncResultFilter();`) you can register modules with the `ResourceManifest.DefineScriptModule(resourceName)` extension method and require them using the `IResourceManager.RegisterScriptModule(resourceName)` etension method. +The `ScriptModuleResourceFilter` makes it possible to register JS modules in a way that they can be imported by name, so no bundling or importing by URL is necessary. Once you've added it to your service collection (`services.AddAsyncResultFilter();`) you can register modules with the `ResourceManifest.DefineScriptModule(resourceName)` extension method and require them using the `IResourceManager.RegisterScriptModule(resourceName)` extension method. -You don't even have to register dependencies, because thanks to the [importmap script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) generated by the `ScriptModuleResourceFilter`, you can import any resource using tge `import * from 'resourceName'` syntax. You can see an example in Lombiq.VueJs where Vue 3 support is added ahead of Orchard Core by [importing Vue 3 as a JS module](https://github.com/Lombiq/Orchard-Vue.js/blob/dev/Lombiq.VueJs/Assets/Scripts/vue-component-app.mjs#L1C4-L1C4). +You don't even have to register dependencies, because thanks to the [importmap script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) generated by the `ScriptModuleResourceFilter`, you can import any resource using the `import * from 'resourceName'` syntax. You can see an example in Lombiq.VueJs where Vue 3 support is added ahead of Orchard Core by [importing Vue 3 as a JS module](https://github.com/Lombiq/Orchard-Vue.js/blob/dev/Lombiq.VueJs/Assets/Scripts/vue-component-app.mjs#L1C4-L1C4). ## Extensions From 1bd1002fab0e206a50ed24d18293ab8fff45c3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Sun, 18 Feb 2024 19:12:20 +0100 Subject: [PATCH 17/29] Code cleanup. --- .../Docs/ResourceManagement.md | 1 - .../ResourceManagement/ResourceManagerExtensions.cs | 4 ++-- .../ResourceManagement/ScriptModuleResourceFilter.cs | 4 +--- .../Security/ResourceManagerContentSecurityPolicyProvider.cs | 1 - Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs | 1 - 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md b/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md index 3ce34591..ef6cf424 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md +++ b/Lombiq.HelpfulLibraries.OrchardCore/Docs/ResourceManagement.md @@ -36,7 +36,6 @@ The `ScriptModuleResourceFilter` makes it possible to register JS modules in a w You don't even have to register dependencies, because thanks to the [importmap script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) generated by the `ScriptModuleResourceFilter`, you can import any resource using the `import * from 'resourceName'` syntax. You can see an example in Lombiq.VueJs where Vue 3 support is added ahead of Orchard Core by [importing Vue 3 as a JS module](https://github.com/Lombiq/Orchard-Vue.js/blob/dev/Lombiq.VueJs/Assets/Scripts/vue-component-app.mjs#L1C4-L1C4). - ## Extensions - `ApplicationBuilderExtensions`: Shortcut extensions for application setup, such as `UseResourceFilters()` (see above). diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 4c9070c9..6f4a0a3a 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -57,8 +57,8 @@ public static HtmlString RenderAndTransformHeader( /// /// Adds a script-module" resource to the manifest. All of these resources are mapped using so they can be imported by module type scripts using the import ... from - /// statement. + /// cref="GetScriptModuleImportMap(IOrchardHelper)"/> so they can be imported by module type scripts using the + /// import ResourceName from 'resourceName' statement. /// public static ResourceDefinition DefineScriptModule(this ResourceManifest manifest, string name) => manifest.DefineResource(ResourceTypes.ScriptModule, name); diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 87387402..4ff067b8 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -1,8 +1,6 @@ -using AngleSharp.Common; using Lombiq.HelpfulLibraries.OrchardCore.Contents; using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using OrchardCore.DisplayManagement.Implementation; @@ -32,7 +30,7 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE } // We can't safely inject resources from constructor here, as some get disposed by the time this display takes place. - private IHtmlContent DisplayScriptModuleResources(IServiceProvider serviceProvider) + private static IHtmlContent DisplayScriptModuleResources(IServiceProvider serviceProvider) { // Won't work correctly with injected resources, the scriptElements below will be empty. Possibly related to the // IResourceManager.InlineManifest being different. diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs index 40e5add5..f9fe372d 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Security/ResourceManagerContentSecurityPolicyProvider.cs @@ -1,7 +1,6 @@ using Lombiq.HelpfulLibraries.AspNetCore.Security; using Microsoft.AspNetCore.Http; using OrchardCore.ResourceManagement; -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs index ce9a2b1c..affb8020 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs @@ -1,4 +1,3 @@ -using Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; using Microsoft.AspNetCore.Html; using Microsoft.Extensions.DependencyInjection; using OrchardCore.DisplayManagement.Descriptors; From 7f019cb7c8b87a636ed473098f59da47baba7bf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 00:27:43 +0100 Subject: [PATCH 18/29] Temporarily remove all ref comments. --- .../IContentSecurityPolicyProvider.cs | 1 - .../ResourceManagerExtensions.cs | 30 ------------------- .../Shapes/ShapeExtensions.cs | 6 ---- 3 files changed, 37 deletions(-) diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs index 503eba69..575261a8 100644 --- a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs @@ -27,7 +27,6 @@ public interface IContentSecurityPolicyProvider public static string GetDirective(IDictionary securityPolicies, params string[] names) => GetDirective(securityPolicies, names.AsEnumerable()); - /// public static string GetDirective(IDictionary securityPolicies, IEnumerable names) { foreach (var name in names) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 6f4a0a3a..659cfa06 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -55,31 +55,12 @@ public static HtmlString RenderAndTransformHeader( return new HtmlString(headerHtml); } - /// - /// Adds a script-module" resource to the manifest. All of these resources are mapped using so they can be imported by module type scripts using the - /// import ResourceName from 'resourceName' statement. - /// public static ResourceDefinition DefineScriptModule(this ResourceManifest manifest, string name) => manifest.DefineResource(ResourceTypes.ScriptModule, name); - /// - /// Registers a script-module" resource to be used on the current page. These can be rendered using as <script src="..." type="module"> elements. - /// public static RequireSettings RegisterScriptModule(this IResourceManager resourceManager, string name) => resourceManager.RegisterResource(ResourceTypes.ScriptModule, name); - /// - /// Turns the required script-module" resources into <script src="..." type="module"> elements. - /// - /// - /// The path that's used to resolve ~ in the resource URLs. Typically should be used.. - /// - /// - /// If not it's used to select which required resources should be considered. - /// public static IEnumerable GetRequiredScriptModuleTags( this IResourceManager resourceManager, string basePath = null, @@ -109,15 +90,6 @@ public static IEnumerable GetRequiredScriptModuleTags( }); } - /// - /// Turns the required script-module" resource with the into a - /// <script src="..." type="module"> element. - /// - /// - /// The path that's used to resolve ~ in the resource URLs. Typically should be used.. - /// - /// The expected value of . public static TagBuilder GetRequiredScriptModuleTag( this IResourceManager resourceManager, string basePath, @@ -160,7 +132,6 @@ public static IHtmlContent GetScriptModuleImportMap( return tagBuilder; } - /// internal static IHtmlContent GetScriptModuleImportMap(this IServiceProvider serviceProvider) { var options = serviceProvider.GetRequiredService>().Value; @@ -172,7 +143,6 @@ internal static IHtmlContent GetScriptModuleImportMap(this IServiceProvider serv fileVersionProvider); } - /// public static IHtmlContent GetScriptModuleImportMap(this IOrchardHelper helper) => helper.HttpContext.RequestServices.GetScriptModuleImportMap(); diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs index affb8020..17f80015 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs @@ -96,12 +96,6 @@ public static IShape CreateAdHocShape(this ShapeTable shapeTable, string type, F return shape; } - /// - /// Creates a new shape with the given in the shape table of the current front-end theme. If - /// the type is not yet in the theme's , then a new descriptor is added with binding that - /// uses . If is null or empty, a new random unique name is - /// generated. - /// public static async Task CreateAdHocShapeForCurrentThemeAsync( this IServiceProvider provider, string type, From 004cbb5728c5b01ae3f3213ddef3f3f269bf437d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 00:45:58 +0100 Subject: [PATCH 19/29] Fix --- .../Security/IContentSecurityPolicyProvider.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs index 575261a8..3cf9fb15 100644 --- a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs @@ -27,6 +27,10 @@ public interface IContentSecurityPolicyProvider public static string GetDirective(IDictionary securityPolicies, params string[] names) => GetDirective(securityPolicies, names.AsEnumerable()); + /// + /// Returns the first non-empty directive from the or or an empty + /// string. + /// public static string GetDirective(IDictionary securityPolicies, IEnumerable names) { foreach (var name in names) From ec048e7ebeb90deb8db9d0d8ece33648ea63c9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 01:30:40 +0100 Subject: [PATCH 20/29] dunno --- .../Security/IContentSecurityPolicyProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs index 3cf9fb15..0ed3bfe8 100644 --- a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs @@ -27,10 +27,9 @@ public interface IContentSecurityPolicyProvider public static string GetDirective(IDictionary securityPolicies, params string[] names) => GetDirective(securityPolicies, names.AsEnumerable()); - /// - /// Returns the first non-empty directive from the or or an empty - /// string. - /// + // Documenting this somehow causes the analyzer to crash: + // https://github.com/Lombiq/Open-Source-Orchard-Core-Extensions/actions/runs/7952542539/job/21707262816?pr=705 +#pragma warning disable SA1600 // Elements should be documented public static string GetDirective(IDictionary securityPolicies, IEnumerable names) { foreach (var name in names) @@ -43,6 +42,7 @@ public static string GetDirective(IDictionary securityPolicies, return securityPolicies.GetMaybe(DefaultSrc) ?? string.Empty; } +#pragma warning restore SA1600 // Elements should be documented /// /// Updates the directive (the first entry of the ) by merging its space From 52abebd12baa9f5ecf695eec53e58ab49a0c4162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 01:50:37 +0100 Subject: [PATCH 21/29] Revert "Temporarily remove all ref comments." --- .../IContentSecurityPolicyProvider.cs | 5 +--- .../ResourceManagerExtensions.cs | 30 +++++++++++++++++++ .../Shapes/ShapeExtensions.cs | 6 ++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs index 0ed3bfe8..503eba69 100644 --- a/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/IContentSecurityPolicyProvider.cs @@ -27,9 +27,7 @@ public interface IContentSecurityPolicyProvider public static string GetDirective(IDictionary securityPolicies, params string[] names) => GetDirective(securityPolicies, names.AsEnumerable()); - // Documenting this somehow causes the analyzer to crash: - // https://github.com/Lombiq/Open-Source-Orchard-Core-Extensions/actions/runs/7952542539/job/21707262816?pr=705 -#pragma warning disable SA1600 // Elements should be documented + /// public static string GetDirective(IDictionary securityPolicies, IEnumerable names) { foreach (var name in names) @@ -42,7 +40,6 @@ public static string GetDirective(IDictionary securityPolicies, return securityPolicies.GetMaybe(DefaultSrc) ?? string.Empty; } -#pragma warning restore SA1600 // Elements should be documented /// /// Updates the directive (the first entry of the ) by merging its space diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 659cfa06..6f4a0a3a 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -55,12 +55,31 @@ public static HtmlString RenderAndTransformHeader( return new HtmlString(headerHtml); } + /// + /// Adds a script-module" resource to the manifest. All of these resources are mapped using so they can be imported by module type scripts using the + /// import ResourceName from 'resourceName' statement. + /// public static ResourceDefinition DefineScriptModule(this ResourceManifest manifest, string name) => manifest.DefineResource(ResourceTypes.ScriptModule, name); + /// + /// Registers a script-module" resource to be used on the current page. These can be rendered using as <script src="..." type="module"> elements. + /// public static RequireSettings RegisterScriptModule(this IResourceManager resourceManager, string name) => resourceManager.RegisterResource(ResourceTypes.ScriptModule, name); + /// + /// Turns the required script-module" resources into <script src="..." type="module"> elements. + /// + /// + /// The path that's used to resolve ~ in the resource URLs. Typically should be used.. + /// + /// + /// If not it's used to select which required resources should be considered. + /// public static IEnumerable GetRequiredScriptModuleTags( this IResourceManager resourceManager, string basePath = null, @@ -90,6 +109,15 @@ public static IEnumerable GetRequiredScriptModuleTags( }); } + /// + /// Turns the required script-module" resource with the into a + /// <script src="..." type="module"> element. + /// + /// + /// The path that's used to resolve ~ in the resource URLs. Typically should be used.. + /// + /// The expected value of . public static TagBuilder GetRequiredScriptModuleTag( this IResourceManager resourceManager, string basePath, @@ -132,6 +160,7 @@ public static IHtmlContent GetScriptModuleImportMap( return tagBuilder; } + /// internal static IHtmlContent GetScriptModuleImportMap(this IServiceProvider serviceProvider) { var options = serviceProvider.GetRequiredService>().Value; @@ -143,6 +172,7 @@ internal static IHtmlContent GetScriptModuleImportMap(this IServiceProvider serv fileVersionProvider); } + /// public static IHtmlContent GetScriptModuleImportMap(this IOrchardHelper helper) => helper.HttpContext.RequestServices.GetScriptModuleImportMap(); diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs index 17f80015..affb8020 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs @@ -96,6 +96,12 @@ public static IShape CreateAdHocShape(this ShapeTable shapeTable, string type, F return shape; } + /// + /// Creates a new shape with the given in the shape table of the current front-end theme. If + /// the type is not yet in the theme's , then a new descriptor is added with binding that + /// uses . If is null or empty, a new random unique name is + /// generated. + /// public static async Task CreateAdHocShapeForCurrentThemeAsync( this IServiceProvider provider, string type, From 64854ec8cf9d773cde278d9e5e07ebe7a679daa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 01:53:56 +0100 Subject: [PATCH 22/29] Update Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../Extensions/JsonHelperExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs b/Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs index 5c75963a..52bb1dce 100644 --- a/Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs +++ b/Lombiq.HelpfulLibraries.AspNetCore/Extensions/JsonHelperExtensions.cs @@ -8,8 +8,8 @@ namespace Microsoft.AspNetCore.Mvc.Rendering; public static class JsonHelperExtensions { /// - /// Returns a full HTML element attribute withe the given prefixed with data- and the - /// value appropriately encoded. + /// Returns a full HTML element attribute with the given prefixed with data- and the + /// value appropriately encoded to prevent XSS attacks. /// public static IHtmlContent DataAttribute(this IJsonHelper helper, string name, object value) { From 142a768f1e6c5f43b43957b8b5296d1fc0c53f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 01:57:55 +0100 Subject: [PATCH 23/29] Elaborate comment as asked by coderabbit. --- .../ResourceManagement/ScriptModuleResourceFilter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 4ff067b8..73dee81b 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -29,7 +29,9 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE await next(); } - // We can't safely inject resources from constructor here, as some get disposed by the time this display takes place. + // We can't safely inject resources from the constructor because some resources may get disposed by the time this + // display action takes place, leading to potential access of disposed objects. Instead, the DisplayContext's + // service provider is used. private static IHtmlContent DisplayScriptModuleResources(IServiceProvider serviceProvider) { // Won't work correctly with injected resources, the scriptElements below will be empty. Possibly related to the From 8d2122cfcbae26d69405e2aca020244342575960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 21:20:51 +0100 Subject: [PATCH 24/29] Fix documentation. --- .../ResourceManagement/ScriptModuleResourceFilter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 73dee81b..f43dc5c3 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -13,7 +13,7 @@ namespace Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; /// -/// A filter that looks for the required script-module" resources. If there were any, it injects the input map +/// A filter that looks for the required script-module resources. If there were any, it injects the input map /// (used for mapping module names to URLs) of all registered module resources and the script blocks of the currently /// required resource. /// From c91b78d7b69db00645b471903d7e9f28c4b7491c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 22:09:58 +0100 Subject: [PATCH 25/29] Fix xmldoc for real this time. --- .../ResourceManagement/ResourceManagerExtensions.cs | 8 ++++---- .../ResourceManagement/ScriptModuleResourceFilter.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs index 6f4a0a3a..4c7dc3c4 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ResourceManagerExtensions.cs @@ -56,7 +56,7 @@ public static HtmlString RenderAndTransformHeader( } /// - /// Adds a script-module" resource to the manifest. All of these resources are mapped using script-module resource to the manifest. All of these resources are mapped using so they can be imported by module type scripts using the /// import ResourceName from 'resourceName' statement. /// @@ -64,14 +64,14 @@ public static ResourceDefinition DefineScriptModule(this ResourceManifest manife manifest.DefineResource(ResourceTypes.ScriptModule, name); /// - /// Registers a script-module" resource to be used on the current page. These can be rendered using script-module resource to be used on the current page. These can be rendered using as <script src="..." type="module"> elements. /// public static RequireSettings RegisterScriptModule(this IResourceManager resourceManager, string name) => resourceManager.RegisterResource(ResourceTypes.ScriptModule, name); /// - /// Turns the required script-module" resources into <script src="..." type="module"> elements. + /// Turns the required script-module resources into <script src="..." type="module"> elements. /// /// /// The path that's used to resolve ~ in the resource URLs. Typically GetRequiredScriptModuleTags( } /// - /// Turns the required script-module" resource with the into a + /// Turns the required script-module resource with the into a /// <script src="..." type="module"> element. /// /// diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index f43dc5c3..30f5ed71 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -13,7 +13,7 @@ namespace Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; /// -/// A filter that looks for the required script-module resources. If there were any, it injects the input map +/// A filter that looks for the required "script-module" resources. If there were any, it injects the input map /// (used for mapping module names to URLs) of all registered module resources and the script blocks of the currently /// required resource. /// From a985a8fc4497b9e69feaad0a367589f8f6a8c844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Mon, 19 Feb 2024 22:15:43 +0100 Subject: [PATCH 26/29] Note about weird analyzer behavior. --- .../ResourceManagement/ScriptModuleResourceFilter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 30f5ed71..3ebc0ee5 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -12,6 +12,10 @@ namespace Lombiq.HelpfulLibraries.OrchardCore.ResourceManagement; +// Don't replace the "script-module" there with script-module as that will cause the DOC105UseParamref analyzer +// to throw NullReferenceException. The same doesn't seem to happen in other files, for example the +// ResourceManagerExtensions.cs in this directory. + /// /// A filter that looks for the required "script-module" resources. If there were any, it injects the input map /// (used for mapping module names to URLs) of all registered module resources and the script blocks of the currently From 8cbc5afb3af52aa3c31ae49f05d92aa617eeb33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Tue, 20 Feb 2024 13:36:48 +0100 Subject: [PATCH 27/29] Permit connecting to fastly.jsdelivr.net --- .../Security/CdnContentSecurityPolicyProvider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs b/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs index 0f38bebe..0d432082 100644 --- a/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs +++ b/Lombiq.HelpfulLibraries.AspNetCore/Security/CdnContentSecurityPolicyProvider.cs @@ -23,6 +23,7 @@ public class CdnContentSecurityPolicyProvider : IContentSecurityPolicyProvider new Uri("https://fonts.googleapis.com/css"), new Uri("https://fonts.gstatic.com/"), new Uri("https://cdn.jsdelivr.net/npm"), + new Uri("https://fastly.jsdelivr.net/npm"), }); /// @@ -31,6 +32,7 @@ public class CdnContentSecurityPolicyProvider : IContentSecurityPolicyProvider public static ConcurrentBag PermittedScriptSources { get; } = new(new[] { new Uri("https://cdn.jsdelivr.net/npm"), + new Uri("https://fastly.jsdelivr.net/npm"), }); /// From 6c6556437c5b7302df4317b76360d68019ab347f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Tue, 20 Feb 2024 15:31:19 +0100 Subject: [PATCH 28/29] Fix new analyzer warning. --- .../ResourceManagement/ScriptModuleResourceFilter.cs | 2 +- Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 3ebc0ee5..65ebfd25 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -44,7 +44,7 @@ private static IHtmlContent DisplayScriptModuleResources(IServiceProvider servic var options = serviceProvider.GetRequiredService>().Value; var scriptElements = resourceManager.GetRequiredScriptModuleTags(options.ContentBasePath).ToList(); - if (!scriptElements.Any()) return null; + if (scriptElements.Count == 0) return null; var importMap = serviceProvider.GetScriptModuleImportMap(); var content = new HtmlContentBuilder(capacity: scriptElements.Count + 1).AppendHtml(importMap); diff --git a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs index affb8020..66b904b2 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/Shapes/ShapeExtensions.cs @@ -111,7 +111,7 @@ public static async Task CreateAdHocShapeForCurrentThemeAsync( var shapeTableManager = provider.GetRequiredService(); var theme = await themeManager.GetThemeAsync(); - var shapeTable = shapeTableManager.GetShapeTable(theme.Id); + var shapeTable = await shapeTableManager.GetShapeTableAsync(theme.Id); return shapeTable.CreateAdHocShape(type, displayAsync); } From 55e4588e9c0cae321ccd9eed29bd99628118a505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1ra=20El-Saig?= Date: Wed, 21 Feb 2024 14:44:00 +0100 Subject: [PATCH 29/29] Update Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Krisztián Németh --- .../ResourceManagement/ScriptModuleResourceFilter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs index 3ebc0ee5..b4947d66 100644 --- a/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs +++ b/Lombiq.HelpfulLibraries.OrchardCore/ResourceManagement/ScriptModuleResourceFilter.cs @@ -33,7 +33,7 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE await next(); } - // We can't safely inject resources from the constructor because some resources may get disposed by the time this + // We can't safely inject resources from the constructor because some resources may get disposed by the time this // display action takes place, leading to potential access of disposed objects. Instead, the DisplayContext's // service provider is used. private static IHtmlContent DisplayScriptModuleResources(IServiceProvider serviceProvider)