Fixes up pull request so that there is no breaking changes. We now type check for a new base class called
UmbracoControllerFactory to get the controller type. Adds SurfaceRouteHandler to ensure that UmbracoMvcHandler is used for all SurfaceControllers, and updates all routes accordingly. Streamlines the RenderRouteHandler to then just look up surface routes in the route table for posted values. This now ensures that UmbracoMvcHandler is used even with plugin surface controllers.
This commit is contained in:
@@ -26,7 +26,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <param name="umbracoTokenValue">The DataToken value to set for the 'umbraco' key, this defaults to 'backoffice' </param>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
internal static void RouteControllerPlugin(this AreaRegistration area, string controllerName, Type controllerType, RouteCollection routes,
|
||||
internal static Route RouteControllerPlugin(this AreaRegistration area, string controllerName, Type controllerType, RouteCollection routes,
|
||||
string controllerSuffixName, string defaultAction, object defaultId,
|
||||
string umbracoTokenValue = "backoffice")
|
||||
{
|
||||
@@ -72,6 +72,7 @@ namespace Umbraco.Web.Mvc
|
||||
controllerPluginRoute.DataTokens.Add("area", area.AreaName);
|
||||
controllerPluginRoute.DataTokens.Add("umbraco", umbracoTokenValue); //ensure the umbraco token is set
|
||||
|
||||
return controllerPluginRoute;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
|
||||
public interface IFilteredControllerFactory : IControllerFactory
|
||||
{
|
||||
/// <summary>
|
||||
@@ -15,12 +16,5 @@ namespace Umbraco.Web.Mvc
|
||||
/// <remarks></remarks>
|
||||
bool CanHandle(RequestContext request);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the controller type for the controller name otherwise null if not found
|
||||
/// </summary>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <param name="controllerName"></param>
|
||||
/// <returns></returns>
|
||||
Type GetControllerType(RequestContext requestContext, string controllerName);
|
||||
}
|
||||
}
|
||||
@@ -57,9 +57,23 @@ namespace Umbraco.Web.Mvc
|
||||
internal Type GetControllerTypeInternal(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext));
|
||||
return factory != null
|
||||
? factory.GetControllerType(requestContext, controllerName)
|
||||
: base.GetControllerType(requestContext, controllerName);
|
||||
if (factory != null)
|
||||
{
|
||||
//check to see if the factory is of type UmbracoControllerFactory which exposes the GetControllerType method so we don't have to create
|
||||
// an instance of the controller to figure out what it is. This is a work around for not having a breaking change for:
|
||||
// http://issues.umbraco.org/issue/U4-1726
|
||||
|
||||
var umbFactory = factory as UmbracoControllerFactory;
|
||||
if (umbFactory != null)
|
||||
{
|
||||
return umbFactory.GetControllerType(requestContext, controllerName);
|
||||
}
|
||||
//we have no choice but to instantiate the controller
|
||||
var instance = factory.CreateController(requestContext, controllerName);
|
||||
return instance.GetType();
|
||||
}
|
||||
|
||||
return base.GetControllerType(requestContext, controllerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -67,7 +67,9 @@ namespace Umbraco.Web.Mvc
|
||||
{
|
||||
foreach (var s in surfaceControllers)
|
||||
{
|
||||
this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "Surface", "Index", UrlParameter.Optional, "surface");
|
||||
var route = this.RouteControllerPlugin(s.ControllerName, s.ControllerType, routes, "Surface", "Index", UrlParameter.Optional, "surface");
|
||||
//set the route handler to our SurfaceRouteHandler
|
||||
route.RouteHandler = new SurfaceRouteHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,109 +1,26 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Web.SessionState;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// A controller factory for the render pipeline of Umbraco. This controller factory tries to create a controller with the supplied
|
||||
/// name, and falls back to UmbracoController if none was found.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
public class RenderControllerFactory : IFilteredControllerFactory
|
||||
public class RenderControllerFactory : UmbracoControllerFactory
|
||||
{
|
||||
private readonly OverridenDefaultControllerFactory _innerFactory = new OverridenDefaultControllerFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
||||
/// </summary>
|
||||
public RenderControllerFactory()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance can handle the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns><c>true</c> if this instance can handle the specified request; otherwise, <c>false</c>.</returns>
|
||||
/// <remarks></remarks>
|
||||
public virtual bool CanHandle(RequestContext request)
|
||||
public override bool CanHandle(RequestContext request)
|
||||
{
|
||||
var dataToken = request.RouteData.DataTokens["area"];
|
||||
return dataToken == null || string.IsNullOrWhiteSpace(dataToken.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the controller type for the controller name otherwise null if not found
|
||||
/// </summary>
|
||||
/// <param name="requestContext"></param>
|
||||
/// <param name="controllerName"></param>
|
||||
/// <returns></returns>
|
||||
public Type GetControllerType(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
return _innerFactory.GetControllerType(requestContext, controllerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the specified controller by using the specified request context.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The controller.
|
||||
/// </returns>
|
||||
/// <param name="requestContext">The request context.</param><param name="controllerName">The name of the controller.</param>
|
||||
public virtual IController CreateController(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
Type controllerType = GetControllerType(requestContext, controllerName) ??
|
||||
_innerFactory.GetControllerType(requestContext, ControllerExtensions.GetControllerName(typeof(RenderMvcController)));
|
||||
|
||||
return _innerFactory.GetControllerInstance(requestContext, controllerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the controller's session behavior.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The controller's session behavior.
|
||||
/// </returns>
|
||||
/// <param name="requestContext">The request context.</param><param name="controllerName">The name of the controller whose session behavior you want to get.</param>
|
||||
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
return ((IControllerFactory)_innerFactory).GetControllerSessionBehavior(requestContext, controllerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the specified controller.
|
||||
/// </summary>
|
||||
/// <param name="controller">The controller.</param>
|
||||
public void ReleaseController(IController controller)
|
||||
{
|
||||
_innerFactory.ReleaseController(controller);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// By default, <see cref="DefaultControllerFactory"/> only exposes <see cref="IControllerFactory.CreateController"/> which throws an exception
|
||||
/// if the controller is not found. Since we want to try creating a controller, and then fall back to <see cref="RenderMvcController"/> if one isn't found,
|
||||
/// this nested class changes the visibility of <see cref="DefaultControllerFactory"/>'s internal methods in order to not have to rely on a try-catch.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
internal class OverridenDefaultControllerFactory : DefaultControllerFactory
|
||||
{
|
||||
public new IController GetControllerInstance(RequestContext requestContext, Type controllerType)
|
||||
{
|
||||
return base.GetControllerInstance(requestContext, controllerType);
|
||||
}
|
||||
|
||||
public new Type GetControllerType(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
if (controllerName.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return base.GetControllerType(requestContext, controllerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,34 +185,44 @@ namespace Umbraco.Web.Mvc
|
||||
requestContext.RouteData.Values["action"] = postedInfo.ActionName;
|
||||
requestContext.RouteData.DataTokens["area"] = postedInfo.Area;
|
||||
|
||||
IHttpHandler handler = new UmbracoMvcHandler(requestContext);
|
||||
IHttpHandler handler;
|
||||
|
||||
//get the route from the defined routes
|
||||
using (RouteTable.Routes.GetReadLock())
|
||||
{
|
||||
Route surfaceRoute;
|
||||
if (postedInfo.Area == standardArea)
|
||||
{
|
||||
//find the controller in the route table without an area
|
||||
surfaceRoute = RouteTable.Routes.OfType<Route>()
|
||||
.SingleOrDefault(x => x.Defaults != null &&
|
||||
x.Defaults.ContainsKey("controller") &&
|
||||
x.Defaults["controller"].ToString() == postedInfo.ControllerName &&
|
||||
!x.DataTokens.ContainsKey("area"));
|
||||
}
|
||||
else
|
||||
{
|
||||
//find the controller in the route table with the specified area
|
||||
surfaceRoute = RouteTable.Routes.OfType<Route>()
|
||||
.SingleOrDefault(x => x.Defaults != null &&
|
||||
x.Defaults.ContainsKey("controller") &&
|
||||
x.Defaults["controller"].ToString() == postedInfo.ControllerName &&
|
||||
x.DataTokens.ContainsKey("area") &&
|
||||
x.DataTokens["area"].ToString() == postedInfo.Area);
|
||||
}
|
||||
|
||||
//ensure the controllerType is set if found, meaning it is a plugin, not locally declared
|
||||
if (postedInfo.Area != standardArea)
|
||||
{
|
||||
//requestContext.RouteData.Values["controllerType"] = postedInfo.ControllerType;
|
||||
//find the other data tokens for this route and merge... things like Namespace will be included here
|
||||
using (RouteTable.Routes.GetReadLock())
|
||||
{
|
||||
var surfaceRoute = RouteTable.Routes.OfType<Route>()
|
||||
.SingleOrDefault(x => x.Defaults != null &&
|
||||
x.Defaults.ContainsKey("controller") &&
|
||||
x.Defaults["controller"].ToString() == postedInfo.ControllerName &&
|
||||
x.DataTokens.ContainsKey("area") &&
|
||||
x.DataTokens["area"].ToString() == postedInfo.Area);
|
||||
if (surfaceRoute == null)
|
||||
throw new InvalidOperationException("Could not find a Surface controller route in the RouteTable for controller name " + postedInfo.ControllerName + " and within the area of " + postedInfo.Area);
|
||||
//set the 'Namespaces' token so the controller factory knows where to look to construct it
|
||||
if (surfaceRoute.DataTokens.ContainsKey("Namespaces"))
|
||||
{
|
||||
requestContext.RouteData.DataTokens["Namespaces"] = surfaceRoute.DataTokens["Namespaces"];
|
||||
}
|
||||
handler = surfaceRoute.RouteHandler.GetHttpHandler(requestContext);
|
||||
}
|
||||
if (surfaceRoute == null)
|
||||
throw new InvalidOperationException("Could not find a Surface controller route in the RouteTable for controller name " + postedInfo.ControllerName);
|
||||
//set the 'Namespaces' token so the controller factory knows where to look to construct it
|
||||
if (surfaceRoute.DataTokens.ContainsKey("Namespaces"))
|
||||
{
|
||||
requestContext.RouteData.DataTokens["Namespaces"] = surfaceRoute.DataTokens["Namespaces"];
|
||||
}
|
||||
handler = surfaceRoute.RouteHandler.GetHttpHandler(requestContext);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//store the original URL this came in on
|
||||
//store the original URL this came in on
|
||||
requestContext.RouteData.DataTokens["umbraco-item-url"] = requestContext.HttpContext.Request.Url.AbsolutePath;
|
||||
//store the original route definition
|
||||
requestContext.RouteData.DataTokens["umbraco-route-def"] = routeDefinition;
|
||||
|
||||
16
src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs
Normal file
16
src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Assigned to all SurfaceController's so that it returns our custom SurfaceMvcHandler to use for rendering
|
||||
/// </summary>
|
||||
internal class SurfaceRouteHandler : IRouteHandler
|
||||
{
|
||||
public IHttpHandler GetHttpHandler(RequestContext requestContext)
|
||||
{
|
||||
return new UmbracoMvcHandler(requestContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs
Normal file
82
src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Web.SessionState;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract filtered controller factory used for all Umbraco controller factory implementations
|
||||
/// </summary>
|
||||
public abstract class UmbracoControllerFactory : IFilteredControllerFactory
|
||||
{
|
||||
private readonly OverridenDefaultControllerFactory _innerFactory = new OverridenDefaultControllerFactory();
|
||||
|
||||
public abstract bool CanHandle(RequestContext request);
|
||||
|
||||
public virtual Type GetControllerType(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
return _innerFactory.GetControllerType(requestContext, controllerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the specified controller by using the specified request context.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The controller.
|
||||
/// </returns>
|
||||
/// <param name="requestContext">The request context.</param><param name="controllerName">The name of the controller.</param>
|
||||
public virtual IController CreateController(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
Type controllerType = GetControllerType(requestContext, controllerName) ??
|
||||
_innerFactory.GetControllerType(requestContext, ControllerExtensions.GetControllerName(typeof(RenderMvcController)));
|
||||
|
||||
return _innerFactory.GetControllerInstance(requestContext, controllerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the controller's session behavior.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The controller's session behavior.
|
||||
/// </returns>
|
||||
/// <param name="requestContext">The request context.</param><param name="controllerName">The name of the controller whose session behavior you want to get.</param>
|
||||
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
return ((IControllerFactory)_innerFactory).GetControllerSessionBehavior(requestContext, controllerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the specified controller.
|
||||
/// </summary>
|
||||
/// <param name="controller">The controller.</param>
|
||||
public void ReleaseController(IController controller)
|
||||
{
|
||||
_innerFactory.ReleaseController(controller);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// By default, <see cref="DefaultControllerFactory"/> only exposes <see cref="IControllerFactory.CreateController"/> which throws an exception
|
||||
/// if the controller is not found. Since we want to try creating a controller, and then fall back to <see cref="RenderMvcController"/> if one isn't found,
|
||||
/// this nested class changes the visibility of <see cref="DefaultControllerFactory"/>'s internal methods in order to not have to rely on a try-catch.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
internal class OverridenDefaultControllerFactory : DefaultControllerFactory
|
||||
{
|
||||
public new IController GetControllerInstance(RequestContext requestContext, Type controllerType)
|
||||
{
|
||||
return base.GetControllerInstance(requestContext, controllerType);
|
||||
}
|
||||
|
||||
public new Type GetControllerType(RequestContext requestContext, string controllerName)
|
||||
{
|
||||
if (controllerName.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return base.GetControllerType(requestContext, controllerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,9 +300,11 @@
|
||||
<Compile Include="DefaultPublishedMediaStore.cs" />
|
||||
<Compile Include="Dictionary\UmbracoCultureDictionary.cs" />
|
||||
<Compile Include="Dictionary\UmbracoCultureDictionaryFactory.cs" />
|
||||
<Compile Include="Mvc\SurfaceRouteHandler.cs" />
|
||||
<Compile Include="Mvc\UmbracoAuthorizeAttribute.cs" />
|
||||
<Compile Include="Mvc\UmbracoAuthorizedController.cs" />
|
||||
<Compile Include="Mvc\UmbracoController.cs" />
|
||||
<Compile Include="Mvc\UmbracoControllerFactory.cs" />
|
||||
<Compile Include="Mvc\UmbracoMvcHandler.cs" />
|
||||
<Compile Include="Mvc\UmbracoViewPage.cs" />
|
||||
<Compile Include="PublishedContentExtensions.cs" />
|
||||
|
||||
@@ -153,7 +153,9 @@ namespace Umbraco.Web
|
||||
umbracoPath + "/Surface/" + meta.ControllerName + "/{action}/{id}",//url to match
|
||||
new { controller = meta.ControllerName, action = "Index", id = UrlParameter.Optional },
|
||||
new[] { meta.ControllerNamespace }); //only match this namespace
|
||||
route.DataTokens.Add("umbraco", "surface"); //ensure the umbraco token is set
|
||||
route.DataTokens.Add("umbraco", "surface"); //ensure the umbraco token is set
|
||||
//make it use our custom/special SurfaceMvcHandler
|
||||
route.RouteHandler = new SurfaceRouteHandler();
|
||||
}
|
||||
|
||||
//need to get the plugin controllers that are unique to each area (group by)
|
||||
|
||||
Reference in New Issue
Block a user