diff --git a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs
index c867a0823d..cc7cbbca19 100644
--- a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs
+++ b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs
@@ -26,7 +26,7 @@ namespace Umbraco.Web.Mvc
/// The DataToken value to set for the 'umbraco' key, this defaults to 'backoffice'
///
///
- 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;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs b/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs
index e6697103ad..214532b6e4 100644
--- a/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs
+++ b/src/Umbraco.Web/Mvc/IFilteredControllerFactory.cs
@@ -5,6 +5,7 @@ using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
+
public interface IFilteredControllerFactory : IControllerFactory
{
///
@@ -15,12 +16,5 @@ namespace Umbraco.Web.Mvc
///
bool CanHandle(RequestContext request);
- ///
- /// Returns the controller type for the controller name otherwise null if not found
- ///
- ///
- ///
- ///
- Type GetControllerType(RequestContext requestContext, string controllerName);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs
index 1f4d4b0a5f..4a58fb7251 100644
--- a/src/Umbraco.Web/Mvc/MasterControllerFactory.cs
+++ b/src/Umbraco.Web/Mvc/MasterControllerFactory.cs
@@ -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);
}
///
diff --git a/src/Umbraco.Web/Mvc/PluginControllerArea.cs b/src/Umbraco.Web/Mvc/PluginControllerArea.cs
index 591d53480a..42652eafd4 100644
--- a/src/Umbraco.Web/Mvc/PluginControllerArea.cs
+++ b/src/Umbraco.Web/Mvc/PluginControllerArea.cs
@@ -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();
}
}
}
diff --git a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs
index 770c57c3bb..9482815a03 100644
--- a/src/Umbraco.Web/Mvc/RenderControllerFactory.cs
+++ b/src/Umbraco.Web/Mvc/RenderControllerFactory.cs
@@ -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
{
- ///
+ ///
/// 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.
///
///
- public class RenderControllerFactory : IFilteredControllerFactory
+ public class RenderControllerFactory : UmbracoControllerFactory
{
- private readonly OverridenDefaultControllerFactory _innerFactory = new OverridenDefaultControllerFactory();
-
- ///
- /// Initializes a new instance of the class.
- ///
- public RenderControllerFactory()
- {
-
- }
-
-
+
///
/// Determines whether this instance can handle the specified request.
///
/// The request.
/// true if this instance can handle the specified request; otherwise, false.
///
- 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());
}
- ///
- /// Returns the controller type for the controller name otherwise null if not found
- ///
- ///
- ///
- ///
- public Type GetControllerType(RequestContext requestContext, string controllerName)
- {
- return _innerFactory.GetControllerType(requestContext, controllerName);
- }
-
- ///
- /// Creates the specified controller by using the specified request context.
- ///
- ///
- /// The controller.
- ///
- /// The request context.The name of the controller.
- 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);
- }
-
- ///
- /// Gets the controller's session behavior.
- ///
- ///
- /// The controller's session behavior.
- ///
- /// The request context.The name of the controller whose session behavior you want to get.
- public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
- {
- return ((IControllerFactory)_innerFactory).GetControllerSessionBehavior(requestContext, controllerName);
- }
-
- ///
- /// Releases the specified controller.
- ///
- /// The controller.
- public void ReleaseController(IController controller)
- {
- _innerFactory.ReleaseController(controller);
- }
-
- ///
- /// By default, only exposes which throws an exception
- /// if the controller is not found. Since we want to try creating a controller, and then fall back to if one isn't found,
- /// this nested class changes the visibility of 's internal methods in order to not have to rely on a try-catch.
- ///
- ///
- 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);
- }
- }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
index a0c943745c..680d94ff79 100644
--- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
+++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs
@@ -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()
+ .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()
+ .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()
- .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;
diff --git a/src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs b/src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs
new file mode 100644
index 0000000000..8e56cd715f
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/SurfaceRouteHandler.cs
@@ -0,0 +1,16 @@
+using System.Web;
+using System.Web.Routing;
+
+namespace Umbraco.Web.Mvc
+{
+ ///
+ /// Assigned to all SurfaceController's so that it returns our custom SurfaceMvcHandler to use for rendering
+ ///
+ internal class SurfaceRouteHandler : IRouteHandler
+ {
+ public IHttpHandler GetHttpHandler(RequestContext requestContext)
+ {
+ return new UmbracoMvcHandler(requestContext);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs b/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs
new file mode 100644
index 0000000000..a8f3817b6c
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/UmbracoControllerFactory.cs
@@ -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
+{
+ ///
+ /// Abstract filtered controller factory used for all Umbraco controller factory implementations
+ ///
+ 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);
+ }
+
+ ///
+ /// Creates the specified controller by using the specified request context.
+ ///
+ ///
+ /// The controller.
+ ///
+ /// The request context.The name of the controller.
+ 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);
+ }
+
+ ///
+ /// Gets the controller's session behavior.
+ ///
+ ///
+ /// The controller's session behavior.
+ ///
+ /// The request context.The name of the controller whose session behavior you want to get.
+ public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
+ {
+ return ((IControllerFactory)_innerFactory).GetControllerSessionBehavior(requestContext, controllerName);
+ }
+
+ ///
+ /// Releases the specified controller.
+ ///
+ /// The controller.
+ public void ReleaseController(IController controller)
+ {
+ _innerFactory.ReleaseController(controller);
+ }
+
+ ///
+ /// By default, only exposes which throws an exception
+ /// if the controller is not found. Since we want to try creating a controller, and then fall back to if one isn't found,
+ /// this nested class changes the visibility of 's internal methods in order to not have to rely on a try-catch.
+ ///
+ ///
+ 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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index ef55c9055f..22238d67e7 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -300,9 +300,11 @@
+
+
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs
index 9e130e8fe9..6e68259fb2 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebBootManager.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)