Skip to content

Commit

Permalink
Fix menu widget and add wrapper classes.
Browse files Browse the repository at this point in the history
  • Loading branch information
sarahelsaig committed Jun 21, 2023
1 parent a0b5c56 commit cbf0693
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,132 @@
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;

public class MenuWidgetLiquidFilter : ILiquidFilter
{
private readonly ILiquidContentDisplayService _liquidContentDisplayService;
private readonly Lazy<IUrlHelper> _urlHelperLazy;
private readonly IStringLocalizer<MenuWidgetLiquidFilter> T;

public MenuWidgetLiquidFilter(ILiquidContentDisplayService liquidContentDisplayService) =>
public MenuWidgetLiquidFilter(
IActionContextAccessor actionContextAccessor,
ILiquidContentDisplayService liquidContentDisplayService,
IStringLocalizer<MenuWidgetLiquidFilter> stringLocalizer,
IUrlHelperFactory urlHelperFactory)
{
_liquidContentDisplayService = liquidContentDisplayService;

_urlHelperLazy = new Lazy<IUrlHelper>(() =>
urlHelperFactory.GetUrlHelper(actionContextAccessor.ActionContext!));

T = stringLocalizer;
}

public ValueTask<FluidValue> 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<IEnumerable<MenuItem>>(input!.ToStringValue()),
FluidValues.String => JsonConvert.DeserializeObject<IList<MenuItem>>(
input!.ToStringValue(),
serializerSettings),
FluidValues.Object => input!.ToObjectValue() switch
{
IEnumerable<MenuItem> enumerable => enumerable,
IEnumerable<MenuItem> enumerable => enumerable.AsList(),
MenuItem single => new[] { single },
JArray jArray => jArray.ToObject<IEnumerable<MenuItem>>(),
JObject jObject => new[] { jObject.ToObject<MenuItem>() },
JArray jArray => jArray.ToObject<IList<MenuItem>>(serializer),
JObject jObject => new[] { jObject.ToObject<MenuItem>(serializer) },
_ => null,
},
_ => null,
};

UpdateMenuItems(menuItems, localNav);

return _liquidContentDisplayService.DisplayNewAsync<MenuWidgetViewModel>(
WidgetTypes.MenuWidget,
model =>
{
model.NoWrapper = noWrapper;
model.MenuItems = menuItems ?? Enumerable.Empty<MenuItem>();
model.MenuItems = menuItems ?? Array.Empty<MenuItem>();
model.HtmlClasses = classes;
});
}

private void UpdateMenuItems(IEnumerable<MenuItem> 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<LocalizedString>
{
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<string>() is { } text ? T[text] : null;
case JsonToken.StartObject:
var data = new Dictionary<string, string>(
JToken.ReadFrom(reader).ToObject<Dictionary<string, string>>(),
StringComparer.OrdinalIgnoreCase);
return new LocalizedString(data[nameof(LocalizedString.Name)], data[nameof(LocalizedString.Value)]);
default:
throw new InvalidOperationException("Unable to parse JSON!");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,23 @@ namespace Lombiq.HelpfulExtensions.Extensions.Widgets.ViewModels;
public class MenuWidgetViewModel
{
public bool NoWrapper { get; set; }

public IEnumerable<MenuItem> MenuItems { get; set; }

public MenuWidgetViewModel(bool noWrapper = false, IEnumerable<MenuItem> menuItems = null)
public string HtmlClasses { get; set; } = string.Empty;

public MenuWidgetViewModel()
: this(noWrapper: false, menuItems: Enumerable.Empty<MenuItem>())
{
}

public MenuWidgetViewModel(bool noWrapper, IEnumerable<MenuItem> menuItems)
{
NoWrapper = noWrapper;
MenuItems = menuItems ?? Enumerable.Empty<MenuItem>();
}

public MenuWidgetViewModel(dynamic model)
: this((model.NoWrapper as bool?) == true, model.MenuItems as IEnumerable<MenuItem>)
{
}
: this((model.NoWrapper as bool?) == true, model.MenuItems as IEnumerable<MenuItem>) =>
HtmlClasses = model.HtmlClasses as string ?? string.Empty;
}
4 changes: 2 additions & 2 deletions Lombiq.HelpfulExtensions/Views/MenuWidget.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@

@if (!viewModel.NoWrapper)
{
@:<nav class="navbar navbar-expand-md">
@:<nav class="navbar navbar-expand-md @viewModel.HtmlClasses">
}
<div class="container-fluid">
<div class="container-fluid @(viewModel.NoWrapper ? viewModel.HtmlClasses : string.Empty)">
<button class="navbar-toggler @togglerElementName"
type="button"
data-bs-toggle="collapse"
Expand Down

0 comments on commit cbf0693

Please sign in to comment.