Skip to content

Commit

Permalink
Add Navbar shape for Liquid (#15532)
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeAlhayek authored Mar 21, 2024
1 parent bf0afdb commit 980f322
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 40 deletions.
39 changes: 39 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.Admin/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using Fluid;
using Fluid.Values;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
Expand All @@ -9,10 +11,13 @@
using OrchardCore.Admin.Controllers;
using OrchardCore.Admin.Drivers;
using OrchardCore.Admin.Models;
using OrchardCore.DisplayManagement;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.DisplayManagement.Theming;
using OrchardCore.Environment.Shell.Configuration;
using OrchardCore.Environment.Shell.Scope;
using OrchardCore.Liquid;
using OrchardCore.Modules;
using OrchardCore.Mvc.Core.Utilities;
using OrchardCore.Mvc.Routing;
Expand Down Expand Up @@ -93,4 +98,38 @@ public override void ConfigureServices(IServiceCollection services)
services.AddSiteSettingsPropertyDeploymentStep<AdminSettings, DeploymentStartup>(S => S["Admin settings"], S => S["Exports the admin settings."]);
}
}

[RequireFeatures("OrchardCore.Liquid")]
public class LiquidStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.Configure<TemplateOptions>(o =>
{
o.Scope.SetValue(nameof(Navbar), new FunctionValue(async (args, ctx) =>
{
if (ctx is LiquidTemplateContext context)
{
var displayManager = context.Services.GetRequiredService<IDisplayManager<Navbar>>();
var updateModelAccessor = context.Services.GetRequiredService<IUpdateModelAccessor>();

var shape = await displayManager.BuildDisplayAsync(updateModelAccessor.ModelUpdater);

return FluidValue.Create(shape, ctx.Options);
}

return NilValue.Instance;
}));

o.MemberAccessStrategy.Register<Navbar, FluidValue>((navbar, name, context) =>
{
return name switch
{
nameof(Navbar.Properties) => new ObjectValue(navbar.Properties),
_ => NilValue.Instance
};
});
});
}
}
}
16 changes: 15 additions & 1 deletion src/OrchardCore.Modules/OrchardCore.Admin/Views/Navbar.cshtml
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
@using OrchardCore.Admin.Models
@using OrchardCore.DisplayManagement
@using OrchardCore.DisplayManagement.ModelBinding

@inject IDisplayManager<Navbar> DisplayManager
@inject IUpdateModelAccessor UpdateModelAccessor

@if (Model.Content == null)
{
return;
dynamic shape = await DisplayAsync(await DisplayManager.BuildDisplayAsync(UpdateModelAccessor.ModelUpdater, (string)Model.Metadata.DisplayType));

if (shape.Content == null)
{
return;
}

Model.Content = shape.Content;
}

<ul class="navbar-nav user-top-navbar">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,14 @@
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using OrchardCore.Admin.Models;
using OrchardCore.ContentLocalization.ViewModels;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Localization;

namespace OrchardCore.ContentLocalization.Drivers;

public class ContentCulturePickerNavbarDisplayDriver : DisplayDriver<Navbar>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILocalizationService _localizationService;

public ContentCulturePickerNavbarDisplayDriver(
IHttpContextAccessor httpContextAccessor,
ILocalizationService localizationService)
{
_httpContextAccessor = httpContextAccessor;
_localizationService = localizationService;
}

public override async Task<IDisplayResult> DisplayAsync(Navbar model, BuildDisplayContext context)
public override IDisplayResult Display(Navbar model)
{
var supportedCultures = (await _localizationService.GetSupportedCulturesAsync()).Select(c => CultureInfo.GetCultureInfo(c));

return Initialize<CulturePickerViewModel>("ContentCulturePicker", model =>
{
model.SupportedCultures = supportedCultures;
model.CurrentCulture = _httpContextAccessor
.HttpContext
.Features
.Get<IRequestCultureFeature>()?.RequestCulture?.Culture ?? CultureInfo.CurrentUICulture;

}).RenderWhen(() => Task.FromResult(supportedCultures.Count() > 1))
.Location("Detail", "Content:5");
return View("ContentCulturePicker", model)
.Location("Detail", "Content:5");
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
@using Microsoft.AspNetCore.Localization
@using OrchardCore.Localization
@using System.Globalization

@inject ILocalizationService LocalizationService
@{
var supportedCultures = (await LocalizationService.GetSupportedCulturesAsync()).Select(c => CultureInfo.GetCultureInfo(c));

if (supportedCultures.Count() < 2)
{
return;
}

var currentCulture = Context.Request.HttpContext.Features
.Get<IRequestCultureFeature>()?.RequestCulture?.Culture ?? CultureInfo.CurrentUICulture;
}

<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="oc-culture-picker" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@Model.CurrentCulture.NativeName</a>
<a class="nav-link dropdown-toggle" href="#" id="oc-culture-picker" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@currentCulture</a>
<div class="dropdown-menu" aria-labelledby="oc-culture-picker">
@foreach (var culture in Model.SupportedCultures)
@foreach (var culture in supportedCultures)
{
if (culture.Name != Model.CurrentCulture.Name)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
@inject IUpdateModelAccessor UpdateModelAccessor

<li class="nav-item">
@await DisplayAsync(await DisplayManager.BuildDisplayAsync<UserMenu>(UpdateModelAccessor.ModelUpdater, (string)Model.Metadata.DisplayType))
@await DisplayAsync(await DisplayManager.BuildDisplayAsync(UpdateModelAccessor.ModelUpdater, (string)Model.Metadata.DisplayType))
</li>
2 changes: 1 addition & 1 deletion src/OrchardCore.Themes/TheAdmin/Views/Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

// Branding and Navbar are pre-rendered to allow resource injection.
var brandingHtml = await DisplayAsync(await New.AdminBranding());
var navbar = await DisplayAsync(await DisplayManager.BuildDisplayAsync<Navbar>(UpdateModelAccessor.ModelUpdater, "DetailAdmin"));
var navbar = await DisplayAsync(await DisplayManager.BuildDisplayAsync(UpdateModelAccessor.ModelUpdater, "DetailAdmin"));
}
<!DOCTYPE html>
<html lang="@Orchard.CultureName()" dir="@Orchard.CultureDir()" data-bs-theme="@await ThemeTogglerService.CurrentTheme()" data-tenant="@ThemeTogglerService.CurrentTenant">
Expand Down
2 changes: 1 addition & 1 deletion src/OrchardCore.Themes/TheTheme/Views/Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@{
var adminSettings = Site.As<AdminSettings>();
// Navbar is pre-rendered to allow resource injection.
var navbar = await DisplayAsync(await DisplayManager.BuildDisplayAsync<Navbar>(UpdateModelAccessor.ModelUpdater));
var navbar = await DisplayAsync(await DisplayManager.BuildDisplayAsync(UpdateModelAccessor.ModelUpdater));
}
<!DOCTYPE html>
<html lang="@Orchard.CultureName()" dir="@Orchard.CultureDir()" data-bs-theme="@await ThemeTogglerService.CurrentTheme()" data-tenant="@ThemeTogglerService.CurrentTenant">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
@inject IDisplayManager<UserMenu> DisplayManager
@inject IUpdateModelAccessor UpdateModelAccessor

@await DisplayAsync(await DisplayManager.BuildDisplayAsync<UserMenu>(UpdateModelAccessor.ModelUpdater, (string)Model.Metadata.DisplayType))
@await DisplayAsync(await DisplayManager.BuildDisplayAsync(UpdateModelAccessor.ModelUpdater, (string)Model.Metadata.DisplayType))
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace OrchardCore.DisplayManagement.Liquid
public class LiquidViewParser : FluidParser
{
public LiquidViewParser(IOptions<LiquidViewOptions> liquidViewOptions)
: base(new FluidParserOptions() { AllowFunctions = true })
{
RegisterEmptyTag("render_body", RenderBodyTag.WriteToAsync);
RegisterParserTag("render_section", ArgumentsList, RenderSectionTag.WriteToAsync);
Expand Down
30 changes: 29 additions & 1 deletion src/docs/reference/modules/Admin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,35 @@ Here are samples using logo and favicon from media module.

## Navbar Shape

The navigation bar shape is available in two display types `Detail` for the frontend and `DetailAdmin` for the backend admin. If you wish to add a menu item to the navigation bar, simply create a display driver for `Navbar`.
The navigation bar shape is available in two display types `Detail` for the frontend and `DetailAdmin` for the backend admin. The `Navbar` shape is composed and used `TheAdmin` and `TheTheme` themes. If you wish to compose and use the `Navbar` shape in other themes, you may create it using two steps


=== "Liquid"

``` liquid
// Construct the shape at the beginning of the layout.liquid file to enable navbar items to potentially contribute to the resources output as necessary.
{% assign navbar = Navbar() | shape_render %}

// Subsequently in the layout.liquid file, invoke the shape at the location where you want to display it.
{{ navbar }}
```

=== "Razor"

``` html
@inject IDisplayManager<Navbar> DisplayManager
@inject IUpdateModelAccessor UpdateModelAccessor
@{
// Construct the shape at the beginning of the layout.cshtml file to enable navbar items to potentially contribute to the resources output as necessary.
var navbar = await DisplayAsync(await DisplayManager.BuildDisplayAsync(UpdateModelAccessor.ModelUpdater, "Detail"));
}

// Subsequently in the layout.cshtml file, invoke the shape at the location where you want to display it.
@navbar
```


If you wish to add a menu item to the navbar, simply create a display driver for `Navbar`.

As an illustration, we inject the Visit Site link into `DetailAdmin` display type using a display driver as outlined below:

Expand Down
16 changes: 15 additions & 1 deletion src/docs/releases/1.9.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,21 @@ public class PersonController : Controller

In this example, (if the admin prefix remains the default "Admin") you can reach the Index action at `~/Admin/Person` (or by the route name `Person`), because its own action-level attribute took precedence. You can reach Create at `~/Admin/Person/Create` (route name `PersonCreate`) and Edit for the person whose identifier string is "john-doe" at `~/Admin/Person/john-doe` (route name `PersonEdit`).

## Users Module
### Users Module

Added a new User Localization feature that allows to be able to configure the culture per user from the admin UI.

### Navbar

Added a new `Navbar()` function to Liquid to allow building the `Navbar` shape using Liquid. Here are the steps needed to add the `Navbar` shape into your custom Liquid shape:

1. Construct the shape at the beginning of the `layout.liquid` file to enable navbar items to potentially contribute to the resources output as necessary.

```
{% assign navbar = Navbar() | shape_render %}
```
2. Subsequently in the `layout.liquid` file, invoke the shape at the location where you want to display it.

```
{{ navbar }}
```

0 comments on commit 980f322

Please sign in to comment.