diff --git a/Lombiq.HelpfulExtensions/Extensions/Widgets/Liquid/MenuWidgetLiquidFilter.cs b/Lombiq.HelpfulExtensions/Extensions/Widgets/Liquid/MenuWidgetLiquidFilter.cs index 3f79d340..2cc0ac4a 100644 --- a/Lombiq.HelpfulExtensions/Extensions/Widgets/Liquid/MenuWidgetLiquidFilter.cs +++ b/Lombiq.HelpfulExtensions/Extensions/Widgets/Liquid/MenuWidgetLiquidFilter.cs @@ -2,12 +2,17 @@ using Fluid.Values; using Lombiq.HelpfulExtensions.Extensions.Widgets.ViewModels; using Lombiq.HelpfulLibraries.OrchardCore.Liquid; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.Extensions.Localization; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OrchardCore.Liquid; using OrchardCore.Navigation; +using System; using System.Collections.Generic; -using System.Linq; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace Lombiq.HelpfulExtensions.Extensions.Widgets.Liquid; @@ -15,35 +20,114 @@ namespace Lombiq.HelpfulExtensions.Extensions.Widgets.Liquid; public class MenuWidgetLiquidFilter : ILiquidFilter { private readonly ILiquidContentDisplayService _liquidContentDisplayService; + private readonly Lazy _urlHelperLazy; + private readonly IStringLocalizer T; - public MenuWidgetLiquidFilter(ILiquidContentDisplayService liquidContentDisplayService) => + public MenuWidgetLiquidFilter( + IActionContextAccessor actionContextAccessor, + ILiquidContentDisplayService liquidContentDisplayService, + IStringLocalizer stringLocalizer, + IUrlHelperFactory urlHelperFactory) + { _liquidContentDisplayService = liquidContentDisplayService; + _urlHelperLazy = new Lazy(() => + urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext!)); + + T = stringLocalizer; + } + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, LiquidTemplateContext context) { - bool noWrapper; + bool noWrapper, localNav; + string classes; noWrapper = arguments[nameof(noWrapper)].ToBooleanValue(); + localNav = arguments[nameof(localNav)].ToBooleanValue(); + classes = arguments[nameof(classes)].ToStringValue(); + + var converter = new LocalizedStringJsonConverter(T); + var serializer = new JsonSerializer(); + serializer.Converters.Add(converter); + var serializerSettings = new JsonSerializerSettings(); + serializerSettings.Converters.Add(converter); var menuItems = input?.Type switch { - FluidValues.String => JsonConvert.DeserializeObject>(input!.ToStringValue()), + FluidValues.String => JsonConvert.DeserializeObject>( + input!.ToStringValue(), + serializerSettings), FluidValues.Object => input!.ToObjectValue() switch { - IEnumerable enumerable => enumerable, + IEnumerable enumerable => enumerable.AsList(), MenuItem single => new[] { single }, - JArray jArray => jArray.ToObject>(), - JObject jObject => new[] { jObject.ToObject() }, + JArray jArray => jArray.ToObject>(serializer), + JObject jObject => new[] { jObject.ToObject(serializer) }, _ => null, }, _ => null, }; + UpdateMenuItems(menuItems, localNav); + return _liquidContentDisplayService.DisplayNewAsync( WidgetTypes.MenuWidget, model => { model.NoWrapper = noWrapper; - model.MenuItems = menuItems ?? Enumerable.Empty(); + model.MenuItems = menuItems ?? Array.Empty(); + model.HtmlClasses = classes; }); } + + private void UpdateMenuItems(IEnumerable menuItems, bool localNav) + { + if (menuItems == null) return; + + foreach (var item in menuItems) + { + if (!string.IsNullOrEmpty(item.Url)) + { + var finalUrl = _urlHelperLazy.Value.Content(item.Url); + item.Url = finalUrl; + item.Href = finalUrl; + } + + item.LocalNav = localNav || item.LocalNav; + + UpdateMenuItems(item.Items, localNav); + } + } + + public class LocalizedStringJsonConverter : JsonConverter + { + private readonly IStringLocalizer T; + + public LocalizedStringJsonConverter(IStringLocalizer stringLocalizer) => + T = stringLocalizer; + + public override void WriteJson(JsonWriter writer, LocalizedString value, JsonSerializer serializer) => + writer.WriteValue(value?.Value); + + [SuppressMessage("Style", "IDE0010:Add missing cases", Justification = "We don't want to handle other token types.")] + public override LocalizedString ReadJson( + JsonReader reader, + Type objectType, + LocalizedString existingValue, + bool hasExistingValue, + JsonSerializer serializer) + { + switch (reader.TokenType) + { + case JsonToken.String: + return JToken.ReadFrom(reader).ToObject() is { } text ? T[text] : null; + case JsonToken.StartObject: + var data = new Dictionary( + JToken.ReadFrom(reader).ToObject>(), + StringComparer.OrdinalIgnoreCase); + return new LocalizedString(data[nameof(LocalizedString.Name)], data[nameof(LocalizedString.Value)]); + default: + throw new InvalidOperationException("Unable to parse JSON!"); + } + } + } } diff --git a/Lombiq.HelpfulExtensions/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs b/Lombiq.HelpfulExtensions/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs index 842bcd39..48e7cdef 100644 --- a/Lombiq.HelpfulExtensions/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs +++ b/Lombiq.HelpfulExtensions/Extensions/Widgets/ViewModels/MenuWidgetViewModel.cs @@ -7,16 +7,23 @@ namespace Lombiq.HelpfulExtensions.Extensions.Widgets.ViewModels; public class MenuWidgetViewModel { public bool NoWrapper { get; set; } + public IEnumerable MenuItems { get; set; } - public MenuWidgetViewModel(bool noWrapper = false, IEnumerable menuItems = null) + public string HtmlClasses { get; set; } = string.Empty; + + public MenuWidgetViewModel() + : this(noWrapper: false, menuItems: Enumerable.Empty()) + { + } + + public MenuWidgetViewModel(bool noWrapper, IEnumerable menuItems) { NoWrapper = noWrapper; MenuItems = menuItems ?? Enumerable.Empty(); } public MenuWidgetViewModel(dynamic model) - : this((model.NoWrapper as bool?) == true, model.MenuItems as IEnumerable) - { - } + : this((model.NoWrapper as bool?) == true, model.MenuItems as IEnumerable) => + HtmlClasses = model.HtmlClasses as string ?? string.Empty; } diff --git a/Lombiq.HelpfulExtensions/Views/MenuWidget.cshtml b/Lombiq.HelpfulExtensions/Views/MenuWidget.cshtml index 1088679d..68d66eaf 100644 --- a/Lombiq.HelpfulExtensions/Views/MenuWidget.cshtml +++ b/Lombiq.HelpfulExtensions/Views/MenuWidget.cshtml @@ -34,9 +34,9 @@ @if (!viewModel.NoWrapper) { - @: