Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Navbar shape for Liquid #15532

Merged
merged 7 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }}
```
Loading