Reimplementing EnsurePartialViewMacroViewContextFilterAttribute and other required classes
This commit is contained in:
12
src/Umbraco.Web.Common/Constants/Constants.cs
Normal file
12
src/Umbraco.Web.Common/Constants/Constants.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Web.Common.Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// constants
|
||||
/// </summary>
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string ViewLocation = "~/Views";
|
||||
|
||||
internal const string DataTokenCurrentViewContext = "umbraco-current-view-context";
|
||||
}
|
||||
}
|
||||
9
src/Umbraco.Web.Common/Controllers/RenderController.cs
Normal file
9
src/Umbraco.Web.Common/Controllers/RenderController.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Common.Controllers
|
||||
{
|
||||
public abstract class RenderController : Controller
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Umbraco.Web.Common.Controllers;
|
||||
|
||||
namespace Umbraco.Web.Common.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a special filter which is required for the RTE to be able to render Partial View Macros that
|
||||
/// contain forms when the RTE value is resolved outside of an MVC view being rendered
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The entire way that we support partial view macros that contain forms isn't really great, these forms
|
||||
/// need to be executed as ChildActions so that the ModelState,ViewData,TempData get merged into that action
|
||||
/// so the form can show errors, viewdata, etc...
|
||||
/// Under normal circumstances, macros will be rendered after a ViewContext is created but in some cases
|
||||
/// developers will resolve the RTE value in the controller, in this case the Form won't be rendered correctly
|
||||
/// with merged ModelState from the controller because the special DataToken hasn't been set yet (which is
|
||||
/// normally done in the UmbracoViewPageOfModel when a real ViewContext is available.
|
||||
/// So we need to detect if the currently rendering controller is IRenderController and if so we'll ensure that
|
||||
/// this DataToken exists before the action executes in case the developer resolves an RTE value that contains
|
||||
/// a partial view macro form.
|
||||
/// </remarks>
|
||||
internal class EnsurePartialViewMacroViewContextFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the custom ViewContext datatoken is set before the RenderController action is invoked,
|
||||
/// this ensures that any calls to GetPropertyValue with regards to RTE or Grid editors can still
|
||||
/// render any PartialViewMacro with a form and maintain ModelState
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (!(context.Controller is Controller controller)) return;
|
||||
|
||||
//ignore anything that is not IRenderController
|
||||
if (!(controller is RenderController)) return;
|
||||
|
||||
SetViewContext(context, controller);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the custom ViewContext datatoken is set after the RenderController action is invoked,
|
||||
/// this ensures that any custom ModelState that may have been added in the RenderController itself is
|
||||
/// passed onwards in case it is required when rendering a PartialViewMacro with a form
|
||||
/// </summary>
|
||||
/// <param name="context">The filter context.</param>
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
if (!(context.Controller is Controller controller)) return;
|
||||
|
||||
//ignore anything that is not IRenderController
|
||||
if (!(controller is RenderController)) return;
|
||||
|
||||
SetViewContext(context, controller);
|
||||
}
|
||||
|
||||
private void SetViewContext(ActionContext context, Controller controller)
|
||||
{
|
||||
var viewCtx = new ViewContext(
|
||||
context,
|
||||
new DummyView(),
|
||||
controller.ViewData,
|
||||
controller.TempData,
|
||||
new StringWriter(),
|
||||
new HtmlHelperOptions());
|
||||
|
||||
//set the special data token
|
||||
context.RouteData.DataTokens[Constants.Constants.DataTokenCurrentViewContext] = viewCtx;
|
||||
}
|
||||
|
||||
private class DummyView : IView
|
||||
{
|
||||
public Task RenderAsync(ViewContext context)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user