From e60e4e52fc45b4466174757d91f7500454f5e553 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Mar 2016 18:00:14 +0100 Subject: [PATCH] U4-7708 Using a RTE Macro Partial Form (SurfaceController) & Doctype Hijack Controller causes problem with submissions --- ...tialViewMacroViewContextFilterAttribute.cs | 47 +++++++++++++++++++ .../Mvc/UmbracoViewPageOfTModel.cs | 7 ++- src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/WebBootManager.cs | 8 ++++ 4 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs diff --git a/src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs new file mode 100644 index 0000000000..afd40330ea --- /dev/null +++ b/src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs @@ -0,0 +1,47 @@ +using System.IO; +using System.Web.Mvc; + +namespace Umbraco.Web.Mvc +{ + /// + /// 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 + /// + /// + /// 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. + /// + internal class EnsurePartialViewMacroViewContextFilterAttribute : ActionFilterAttribute + { + public override void OnActionExecuting(ActionExecutingContext filterContext) + { + //ignore anything that is not IRenderController + if ((filterContext.Controller is IRenderController) == false) + return; + + var viewCtx = new ViewContext( + filterContext.Controller.ControllerContext, + new DummyView(), + filterContext.Controller.ViewData, filterContext.Controller.TempData, + new StringWriter()); + + //set the special data token + filterContext.RequestContext.RouteData.DataTokens[Constants.DataTokenCurrentViewContext] = viewCtx; + } + + private class DummyView : IView + { + public void Render(ViewContext viewContext, TextWriter writer) + { + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index 861b872275..0c45c24c4a 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -120,10 +120,9 @@ namespace Umbraco.Web.Mvc base.InitializePage(); if (ViewContext.IsChildAction == false) { - if (ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext) == false) - { - ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, ViewContext); - } + //always ensure the special data token is set - this is used purely for partial view macros that contain forms + // and mostly just when rendered within the RTE + ViewContext.RouteData.DataTokens[Constants.DataTokenCurrentViewContext] = ViewContext; } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cb09ef97fd..65fc475c0b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -343,6 +343,7 @@ + diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 935d3934a6..93928d0867 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -198,6 +198,9 @@ namespace Umbraco.Web //Wrap viewengines in the profiling engine WrapViewEngines(ViewEngines.Engines); + //add global filters + ConfigureGlobalFilters(); + //set routes CreateRoutes(); @@ -224,6 +227,11 @@ namespace Umbraco.Web return this; } + internal static void ConfigureGlobalFilters() + { + GlobalFilters.Filters.Add(new EnsurePartialViewMacroViewContextFilterAttribute()); + } + internal static void WrapViewEngines(IList viewEngines) { if (viewEngines == null || viewEngines.Count == 0) return;