Merge remote-tracking branch 'origin/6.1.6' into 6.2.0
Conflicts: src/Umbraco.Web/Mvc/UmbracoPageResult.cs
This commit is contained in:
@@ -11,12 +11,7 @@ namespace Umbraco.Web.Mvc
|
||||
{
|
||||
public string ControllerName { get; set; }
|
||||
public string ActionName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Controller instance found for routing to
|
||||
/// </summary>
|
||||
public ControllerBase Controller { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Controller type found for routing to
|
||||
/// </summary>
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Mvc handler class to intercept creation of controller and store it for later use.
|
||||
/// This means we create two instances of the same controller to support some features later on.
|
||||
/// MVC handler to facilitate the TemplateRenderer. This handler can execute an MVC request and return it as a string.
|
||||
///
|
||||
/// The alternate option for this is to completely rewrite all MvcHandler methods.
|
||||
/// Original:
|
||||
///
|
||||
/// This is currently needed for the 'return CurrentUmbracoPage()' surface controller functionality
|
||||
/// This handler also used to intercept creation of controllers and store it for later use.
|
||||
/// This was needed for the 'return CurrentUmbracoPage()' surface controller functionality
|
||||
/// because it needs to send data back to the page controller.
|
||||
///
|
||||
/// The creation of this controller has been moved to the UmbracoPageResult class which will create a controller when needed.
|
||||
/// </summary>
|
||||
internal class UmbracoMvcHandler : MvcHandler
|
||||
{
|
||||
@@ -25,46 +20,13 @@ namespace Umbraco.Web.Mvc
|
||||
: base(requestContext)
|
||||
{
|
||||
}
|
||||
|
||||
private void StoreControllerInRouteDefinition()
|
||||
{
|
||||
var routeDef = (RouteDefinition)RequestContext.RouteData.DataTokens["umbraco-route-def"];
|
||||
|
||||
if (routeDef == null) return;
|
||||
|
||||
// Get the factory and controller and create a new instance of the controller
|
||||
var factory = ControllerBuilder.Current.GetControllerFactory();
|
||||
var controller = factory.CreateController(RequestContext, routeDef.ControllerName) as ControllerBase;
|
||||
|
||||
// Store the controller
|
||||
routeDef.Controller = controller;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is used internally purely to render an Umbraco MVC template to string and shouldn't be used for anything else.
|
||||
/// </summary>
|
||||
internal void ExecuteUmbracoRequest()
|
||||
{
|
||||
StoreControllerInRouteDefinition();
|
||||
base.ProcessRequest(RequestContext.HttpContext);
|
||||
}
|
||||
|
||||
protected override void ProcessRequest(HttpContextBase httpContext)
|
||||
{
|
||||
StoreControllerInRouteDefinition();
|
||||
|
||||
// Let MVC do its magic and continue the request
|
||||
base.ProcessRequest(httpContext);
|
||||
}
|
||||
|
||||
protected override IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback,
|
||||
object state)
|
||||
{
|
||||
StoreControllerInRouteDefinition();
|
||||
|
||||
return base.BeginProcessRequest(httpContext, callback, state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
@@ -11,54 +12,100 @@ namespace Umbraco.Web.Mvc
|
||||
{
|
||||
public override void ExecuteResult(ControllerContext context)
|
||||
{
|
||||
ResetRouteData(context.RouteData);
|
||||
|
||||
//since we could be returning the current page from a surface controller posted values in which the routing values are changed, we
|
||||
//need to revert these values back to nothing in order for the normal page to render again.
|
||||
context.RouteData.DataTokens["area"] = null;
|
||||
context.RouteData.DataTokens["Namespaces"] = null;
|
||||
|
||||
//validate that the current page execution is not being handled by the normal umbraco routing system
|
||||
if (!context.RouteData.DataTokens.ContainsKey("umbraco-route-def"))
|
||||
{
|
||||
throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name + " in the context of an Http POST when using a SurfaceController form");
|
||||
}
|
||||
ValidateRouteData(context.RouteData);
|
||||
|
||||
var routeDef = (RouteDefinition)context.RouteData.DataTokens["umbraco-route-def"];
|
||||
var factory = ControllerBuilder.Current.GetControllerFactory();
|
||||
|
||||
var targetController = ((Controller)routeDef.Controller);
|
||||
var sourceController = ((Controller) context.Controller);
|
||||
|
||||
//ensure the original template is reset
|
||||
context.RouteData.Values["action"] = routeDef.ActionName;
|
||||
|
||||
//ensure ModelState is copied across
|
||||
routeDef.Controller.ViewData.ModelState.Merge(sourceController.ViewData.ModelState);
|
||||
ControllerBase controller = null;
|
||||
|
||||
//ensure TempData and ViewData is copied across
|
||||
foreach (var d in sourceController.ViewData)
|
||||
routeDef.Controller.ViewData[d.Key] = d.Value;
|
||||
try
|
||||
{
|
||||
controller = CreateController(context, factory, routeDef);
|
||||
|
||||
controller.ViewData.ModelState.Merge(context.Controller.ViewData.ModelState);
|
||||
|
||||
CopyControllerData(context, controller);
|
||||
|
||||
//We cannot simply merge the temp data because during controller execution it will attempt to 'load' temp data
|
||||
// but since it has not been saved, there will be nothing to load and it will revert to nothing, so the trick is
|
||||
// to Save the state of the temp data first then it will automatically be picked up.
|
||||
// http://issues.umbraco.org/issue/U4-1339
|
||||
targetController.TempDataProvider = sourceController.TempDataProvider;
|
||||
targetController.TempData = sourceController.TempData;
|
||||
targetController.TempData.Save(sourceController.ControllerContext, sourceController.TempDataProvider);
|
||||
|
||||
using (DisposableTimer.TraceDuration<UmbracoPageResult>("Executing Umbraco RouteDefinition controller", "Finished"))
|
||||
{
|
||||
try
|
||||
{
|
||||
((IController)targetController).Execute(context.RequestContext);
|
||||
}
|
||||
finally
|
||||
{
|
||||
routeDef.Controller.DisposeIfDisposable();
|
||||
}
|
||||
}
|
||||
|
||||
ExecuteControllerAction(context, controller);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupController(controller, factory);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the controller action
|
||||
/// </summary>
|
||||
private static void ExecuteControllerAction(ControllerContext context, IController controller)
|
||||
{
|
||||
using (DisposableTimer.TraceDuration<UmbracoPageResult>("Executing Umbraco RouteDefinition controller", "Finished"))
|
||||
{
|
||||
controller.Execute(context.RequestContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Since we could be returning the current page from a surface controller posted values in which the routing values are changed, we
|
||||
/// need to revert these values back to nothing in order for the normal page to render again.
|
||||
/// </summary>
|
||||
private static void ResetRouteData(RouteData routeData)
|
||||
{
|
||||
routeData.DataTokens["area"] = null;
|
||||
routeData.DataTokens["Namespaces"] = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate that the current page execution is not being handled by the normal umbraco routing system
|
||||
/// </summary>
|
||||
private static void ValidateRouteData(RouteData routeData)
|
||||
{
|
||||
if (!routeData.DataTokens.ContainsKey("umbraco-route-def"))
|
||||
{
|
||||
throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name +
|
||||
" in the context of an Http POST when using a SurfaceController form");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure TempData and ViewData is copied across
|
||||
/// </summary>
|
||||
private static void CopyControllerData(ControllerContext context, ControllerBase controller)
|
||||
{
|
||||
foreach (var d in context.Controller.ViewData)
|
||||
controller.ViewData[d.Key] = d.Value;
|
||||
|
||||
controller.TempData = context.Controller.TempData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a controller using the controller factory
|
||||
/// </summary>
|
||||
private static ControllerBase CreateController(ControllerContext context, IControllerFactory factory, RouteDefinition routeDef)
|
||||
{
|
||||
var controller = factory.CreateController(context.RequestContext, routeDef.ControllerName) as ControllerBase;
|
||||
|
||||
if (controller == null)
|
||||
throw new InvalidOperationException("Could not create controller with name " + routeDef.ControllerName + ".");
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up the controller by releasing it using the controller factory, and by disposing it.
|
||||
/// </summary>
|
||||
private static void CleanupController(IController controller, IControllerFactory factory)
|
||||
{
|
||||
if (controller != null)
|
||||
factory.ReleaseController(controller);
|
||||
|
||||
if (controller != null)
|
||||
controller.DisposeIfDisposable();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user