Managed mode page will render with layout

This commit is contained in:
2025-12-11 22:25:40 +08:00
parent 922afc2239
commit 8181938aaf
6 changed files with 132 additions and 13 deletions

View File

@@ -0,0 +1,8 @@
@model DynamicPage
@{
Layout = "_LayoutContained";
}
<div class="dynamic-content">
@Html.Raw(Model.Html)
</div>

View File

@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace DysonNetwork.Zone.Pages.Dynamic;
public class DynamicPage : PageModel
{
public string Html { get; set; } = "";
}

View File

@@ -1,5 +1,6 @@
@using DysonNetwork.Zone.Publication
@using DysonNetwork.Shared.Models
@using Microsoft.IdentityModel.Tokens
@{
Layout = "_Layout";
var site = Context.Items[PublicationSiteMiddleware.SiteContextKey] as SnPublicationSite;
@@ -8,13 +9,26 @@
<div class="navbar backdrop-blur-md bg-white/1 shadow-xl px-5">
<div class="flex-1">
<a class="btn btn-ghost text-xl" asp-page="/Index">@siteDisplayName</a>
<a class="btn btn-ghost text-xl" href="/">@siteDisplayName</a>
</div>
<div class="flex-none">
<ul class="menu menu-horizontal px-1">
<li><a asp-page="/Posts">Posts</a></li>
<li><a asp-page="/About">About</a></li>
</ul>
@if (site?.Config.NavItems is null || site.Config.NavItems.IsNullOrEmpty())
{
@*Use preset navs*@
<ul class="menu menu-horizontal px-1">
<li><a href="/posts">Posts</a></li>
<li><a href="/about">About</a></li>
</ul>
}
else
{
<ul class="menu menu-horizontal px-1">
@foreach (var item in site.Config.NavItems)
{
<li><a href="@item.Href">@item.Label</a></li>
}
</ul>
}
</div>
</div>
@@ -30,6 +44,11 @@
@section Head
{
@await RenderSectionAsync("Head", required: false)
@if (site?.Config.StyleOverride is not null)
{
<style>@(site.Config.StyleOverride)</style>
}
<style>
.navbar {

View File

@@ -1,5 +1,13 @@
using System.Text.Json;
using DysonNetwork.Shared.Models;
using DysonNetwork.Zone.Pages.Dynamic;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
@@ -8,8 +16,14 @@ namespace DysonNetwork.Zone.Publication;
public class PublicationSiteMiddleware(RequestDelegate next)
{
public const string SiteContextKey = "PubSite";
public async Task InvokeAsync(HttpContext context, AppDatabase db, PublicationSiteManager psm)
public async Task InvokeAsync(
HttpContext context,
AppDatabase db,
PublicationSiteManager psm,
IRazorViewEngine viewEngine,
ITempDataProvider tempData
)
{
var siteNameValue = context.Request.Headers["X-SiteName"].ToString();
var currentPath = context.Request.Path.Value ?? "";
@@ -21,7 +35,8 @@ public class PublicationSiteMiddleware(RequestDelegate next)
}
var site = await db.PublicationSites
.FirstOrDefaultAsync(s => EF.Functions.ILike(s.Slug, siteNameValue));
.FirstOrDefaultAsync(s => EF.Functions.ILike(s.Slug, siteNameValue)
);
if (site == null)
{
await next(context);
@@ -29,7 +44,7 @@ public class PublicationSiteMiddleware(RequestDelegate next)
}
context.Items[SiteContextKey] = site;
var page = await db.PublicationPages
.FirstOrDefaultAsync(p => p.SiteId == site.Id && p.Path == currentPath);
if (page != null)
@@ -38,13 +53,31 @@ public class PublicationSiteMiddleware(RequestDelegate next)
{
case PublicationPageType.HtmlPage
when page.Config.TryGetValue("html", out var html) && html is JsonElement content:
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(content.ToString());
if (site.Mode == PublicationSiteMode.FullyManaged)
{
context.Items["PublicationHtmlContent"] = content.ToString();
var layoutedHtml = await RenderViewAsync(
context,
viewEngine,
tempData,
"/Pages/Dynamic/DynamicPage.cshtml",
new DynamicPage { Html = content.ToString() }
);
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(layoutedHtml);
}
else
{
context.Response.ContentType = "text/html";
await context.Response.WriteAsync(content.ToString());
}
return;
case PublicationPageType.Redirect
when page.Config.TryGetValue("target", out var tgt) && tgt is JsonElement redirectUrl:
context.Response.Redirect(redirectUrl.ToString());
return;
default:
throw new ArgumentOutOfRangeException();
}
}
@@ -85,4 +118,51 @@ public class PublicationSiteMiddleware(RequestDelegate next)
await next(context);
}
}
private async Task<string> RenderViewAsync(
HttpContext context,
IRazorViewEngine engine,
ITempDataProvider tempDataProvider,
string viewPath,
object model)
{
var endpointFeature = context.Features.Get<IEndpointFeature>();
var endpoint = endpointFeature?.Endpoint;
var routeData = context.GetRouteData();
var actionContext = new ActionContext(
context,
routeData,
new ActionDescriptor()
);
await using var sw = new StringWriter();
var viewResult = engine.GetView(null, viewPath, true);
if (!viewResult.Success)
throw new FileNotFoundException($"View '{viewPath}' not found.");
var viewData = new ViewDataDictionary(
new EmptyModelMetadataProvider(),
new ModelStateDictionary())
{
Model = model
};
var tempData = new TempDataDictionary(context, tempDataProvider);
var viewContext = new ViewContext(
actionContext,
viewResult.View,
viewData,
tempData,
sw,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return sw.ToString();
}
}