Merge branch temp8 into temp8-di

This commit is contained in:
Stephan
2018-07-06 17:36:33 +02:00
1777 changed files with 275222 additions and 275689 deletions

View File

@@ -1,132 +1,132 @@
using System;
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
internal static class AreaRegistrationExtensions
{
/// <summary>
/// Creates a custom individual route for the specified controller plugin. Individual routes
/// are required by controller plugins to map to a unique URL based on ID.
/// </summary>
/// <param name="globalSettings"></param>
/// <param name="controllerName"></param>
/// <param name="controllerType"></param>
/// <param name="routes">An existing route collection</param>
/// <param name="controllerSuffixName">
/// The suffix name that the controller name must end in before the "Controller" string for example:
/// ContentTreeController has a controllerSuffixName of "Tree", this is used for route constraints.
/// </param>
/// <param name="defaultAction"></param>
/// <param name="defaultId"></param>
/// <param name="area"></param>
/// <param name="umbracoTokenValue">The DataToken value to set for the 'umbraco' key, this defaults to 'backoffice' </param>
/// <param name="routeTokens">By default this value is just {action}/{id} but can be modified for things like web api routes</param>
/// <param name="isMvc">Default is true for MVC, otherwise false for WebAPI</param>
/// <param name="areaPathPrefix">
/// If specified will add this string to the path between the umbraco path and the area path name, for example:
/// /umbraco/CUSTOMPATHPREFIX/areaname
/// if not specified, will just route like:
/// /umbraco/areaname
/// </param>
/// <remarks>
/// </remarks>
internal static Route RouteControllerPlugin(this AreaRegistration area,
IGlobalSettings globalSettings,
string controllerName, Type controllerType, RouteCollection routes,
string controllerSuffixName, string defaultAction, object defaultId,
string umbracoTokenValue = "backoffice",
string routeTokens = "{action}/{id}",
bool isMvc = true,
string areaPathPrefix = "")
{
if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName));
if (controllerSuffixName == null) throw new ArgumentNullException(nameof(controllerSuffixName));
if (controllerType == null) throw new ArgumentNullException(nameof(controllerType));
if (routes == null) throw new ArgumentNullException(nameof(routes));
if (defaultId == null) throw new ArgumentNullException(nameof(defaultId));
var umbracoArea = globalSettings.GetUmbracoMvcArea();
//routes are explicitly named with controller names and IDs
var url = umbracoArea + "/" +
(areaPathPrefix.IsNullOrWhiteSpace() ? "" : areaPathPrefix + "/") +
area.AreaName + "/" + controllerName + "/" + routeTokens;
Route controllerPluginRoute;
//var meta = PluginController.GetMetadata(controllerType);
if (isMvc)
{
//create a new route with custom name, specified url, and the namespace of the controller plugin
controllerPluginRoute = routes.MapRoute(
//name
string.Format("umbraco-{0}-{1}", area.AreaName, controllerName),
//url format
url,
//set the namespace of the controller to match
new[] {controllerType.Namespace});
//set defaults
controllerPluginRoute.Defaults = new RouteValueDictionary(
new Dictionary<string, object>
{
{"controller", controllerName},
{"action", defaultAction},
{"id", defaultId}
});
}
else
{
controllerPluginRoute = routes.MapHttpRoute(
//name
string.Format("umbraco-{0}-{1}-{2}", "api", area.AreaName, controllerName),
//url format
url,
new {controller = controllerName, id = defaultId});
//web api routes don't set the data tokens object
if (controllerPluginRoute.DataTokens == null)
{
controllerPluginRoute.DataTokens = new RouteValueDictionary();
}
//look in this namespace to create the controller
controllerPluginRoute.DataTokens.Add("Namespaces", new[] {controllerType.Namespace});
//Special case! Check if the controller type implements IRequiresSessionState and if so use our
//custom webapi session handler
if (typeof(IRequiresSessionState).IsAssignableFrom(controllerType))
{
controllerPluginRoute.RouteHandler = new SessionHttpControllerRouteHandler();
}
}
//Don't look anywhere else except this namespace!
controllerPluginRoute.DataTokens.Add("UseNamespaceFallback", false);
//constraints: only match controllers ending with 'controllerSuffixName' and only match this controller's ID for this route
if (controllerSuffixName.IsNullOrWhiteSpace() == false)
{
controllerPluginRoute.Constraints = new RouteValueDictionary(
new Dictionary<string, object>
{
{"controller", @"(\w+)" + controllerSuffixName}
});
}
//match this area
controllerPluginRoute.DataTokens.Add("area", area.AreaName);
controllerPluginRoute.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, umbracoTokenValue); //ensure the umbraco token is set
return controllerPluginRoute;
}
}
}
using System;
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
internal static class AreaRegistrationExtensions
{
/// <summary>
/// Creates a custom individual route for the specified controller plugin. Individual routes
/// are required by controller plugins to map to a unique URL based on ID.
/// </summary>
/// <param name="globalSettings"></param>
/// <param name="controllerName"></param>
/// <param name="controllerType"></param>
/// <param name="routes">An existing route collection</param>
/// <param name="controllerSuffixName">
/// The suffix name that the controller name must end in before the "Controller" string for example:
/// ContentTreeController has a controllerSuffixName of "Tree", this is used for route constraints.
/// </param>
/// <param name="defaultAction"></param>
/// <param name="defaultId"></param>
/// <param name="area"></param>
/// <param name="umbracoTokenValue">The DataToken value to set for the 'umbraco' key, this defaults to 'backoffice' </param>
/// <param name="routeTokens">By default this value is just {action}/{id} but can be modified for things like web api routes</param>
/// <param name="isMvc">Default is true for MVC, otherwise false for WebAPI</param>
/// <param name="areaPathPrefix">
/// If specified will add this string to the path between the umbraco path and the area path name, for example:
/// /umbraco/CUSTOMPATHPREFIX/areaname
/// if not specified, will just route like:
/// /umbraco/areaname
/// </param>
/// <remarks>
/// </remarks>
internal static Route RouteControllerPlugin(this AreaRegistration area,
IGlobalSettings globalSettings,
string controllerName, Type controllerType, RouteCollection routes,
string controllerSuffixName, string defaultAction, object defaultId,
string umbracoTokenValue = "backoffice",
string routeTokens = "{action}/{id}",
bool isMvc = true,
string areaPathPrefix = "")
{
if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName));
if (controllerSuffixName == null) throw new ArgumentNullException(nameof(controllerSuffixName));
if (controllerType == null) throw new ArgumentNullException(nameof(controllerType));
if (routes == null) throw new ArgumentNullException(nameof(routes));
if (defaultId == null) throw new ArgumentNullException(nameof(defaultId));
var umbracoArea = globalSettings.GetUmbracoMvcArea();
//routes are explicitly named with controller names and IDs
var url = umbracoArea + "/" +
(areaPathPrefix.IsNullOrWhiteSpace() ? "" : areaPathPrefix + "/") +
area.AreaName + "/" + controllerName + "/" + routeTokens;
Route controllerPluginRoute;
//var meta = PluginController.GetMetadata(controllerType);
if (isMvc)
{
//create a new route with custom name, specified url, and the namespace of the controller plugin
controllerPluginRoute = routes.MapRoute(
//name
string.Format("umbraco-{0}-{1}", area.AreaName, controllerName),
//url format
url,
//set the namespace of the controller to match
new[] {controllerType.Namespace});
//set defaults
controllerPluginRoute.Defaults = new RouteValueDictionary(
new Dictionary<string, object>
{
{"controller", controllerName},
{"action", defaultAction},
{"id", defaultId}
});
}
else
{
controllerPluginRoute = routes.MapHttpRoute(
//name
string.Format("umbraco-{0}-{1}-{2}", "api", area.AreaName, controllerName),
//url format
url,
new {controller = controllerName, id = defaultId});
//web api routes don't set the data tokens object
if (controllerPluginRoute.DataTokens == null)
{
controllerPluginRoute.DataTokens = new RouteValueDictionary();
}
//look in this namespace to create the controller
controllerPluginRoute.DataTokens.Add("Namespaces", new[] {controllerType.Namespace});
//Special case! Check if the controller type implements IRequiresSessionState and if so use our
//custom webapi session handler
if (typeof(IRequiresSessionState).IsAssignableFrom(controllerType))
{
controllerPluginRoute.RouteHandler = new SessionHttpControllerRouteHandler();
}
}
//Don't look anywhere else except this namespace!
controllerPluginRoute.DataTokens.Add("UseNamespaceFallback", false);
//constraints: only match controllers ending with 'controllerSuffixName' and only match this controller's ID for this route
if (controllerSuffixName.IsNullOrWhiteSpace() == false)
{
controllerPluginRoute.Constraints = new RouteValueDictionary(
new Dictionary<string, object>
{
{"controller", @"(\w+)" + controllerSuffixName}
});
}
//match this area
controllerPluginRoute.DataTokens.Add("area", area.AreaName);
controllerPluginRoute.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, umbracoTokenValue); //ensure the umbraco token is set
return controllerPluginRoute;
}
}
}

View File

@@ -1,59 +1,59 @@
using System.Web.Mvc;
using Umbraco.Core.Configuration;
using Umbraco.Web.Editors;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// An area registration for back office components
/// </summary>
internal class BackOfficeArea : AreaRegistration
{
private readonly IGlobalSettings _globalSettings;
public BackOfficeArea(IGlobalSettings globalSettings)
{
_globalSettings = globalSettings;
}
/// <summary>
/// Create the routes for the area
/// </summary>
/// <param name="context"></param>
/// <remarks>
/// By using the context to register the routes it means that the area is already applied to them all
/// and that the namespaces searched for the controllers are ONLY the ones specified.
/// </remarks>
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Umbraco_preview",
_globalSettings.GetUmbracoMvcArea() + "/preview/{action}/{editor}",
new {controller = "Preview", action = "Index", editor = UrlParameter.Optional},
new[] { "Umbraco.Web.Editors" });
context.MapRoute(
"Umbraco_back_office",
_globalSettings.GetUmbracoMvcArea() + "/{action}/{id}",
new {controller = "BackOffice", action = "Default", id = UrlParameter.Optional},
//limit the action/id to only allow characters - this is so this route doesn't hog all other
// routes like: /umbraco/channels/word.aspx, etc...
new
{
action = @"[a-zA-Z]*",
id = @"[a-zA-Z]*"
},
new[] {typeof (BackOfficeController).Namespace});
//Create the REST/web/script service routes
context.MapRoute(
"Umbraco_web_services",
_globalSettings.GetUmbracoMvcArea() + "/RestServices/{controller}/{action}/{id}",
new {controller = "SaveFileController", action = "Index", id = UrlParameter.Optional},
//look in this namespace for controllers
new[] {"Umbraco.Web.WebServices"});
}
public override string AreaName => _globalSettings.GetUmbracoMvcArea();
}
}
using System.Web.Mvc;
using Umbraco.Core.Configuration;
using Umbraco.Web.Editors;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// An area registration for back office components
/// </summary>
internal class BackOfficeArea : AreaRegistration
{
private readonly IGlobalSettings _globalSettings;
public BackOfficeArea(IGlobalSettings globalSettings)
{
_globalSettings = globalSettings;
}
/// <summary>
/// Create the routes for the area
/// </summary>
/// <param name="context"></param>
/// <remarks>
/// By using the context to register the routes it means that the area is already applied to them all
/// and that the namespaces searched for the controllers are ONLY the ones specified.
/// </remarks>
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Umbraco_preview",
_globalSettings.GetUmbracoMvcArea() + "/preview/{action}/{editor}",
new {controller = "Preview", action = "Index", editor = UrlParameter.Optional},
new[] { "Umbraco.Web.Editors" });
context.MapRoute(
"Umbraco_back_office",
_globalSettings.GetUmbracoMvcArea() + "/{action}/{id}",
new {controller = "BackOffice", action = "Default", id = UrlParameter.Optional},
//limit the action/id to only allow characters - this is so this route doesn't hog all other
// routes like: /umbraco/channels/word.aspx, etc...
new
{
action = @"[a-zA-Z]*",
id = @"[a-zA-Z]*"
},
new[] {typeof (BackOfficeController).Namespace});
//Create the REST/web/script service routes
context.MapRoute(
"Umbraco_web_services",
_globalSettings.GetUmbracoMvcArea() + "/RestServices/{controller}/{action}/{id}",
new {controller = "SaveFileController", action = "Index", id = UrlParameter.Optional},
//look in this namespace for controllers
new[] {"Umbraco.Web.WebServices"});
}
public override string AreaName => _globalSettings.GetUmbracoMvcArea();
}
}

View File

@@ -1,12 +1,12 @@
namespace Umbraco.Web.Mvc
{
/// <summary>
/// constants
/// </summary>
internal static class Constants
{
internal const string ViewLocation = "~/Views";
internal const string DataTokenCurrentViewContext = "umbraco-current-view-context";
}
}
namespace Umbraco.Web.Mvc
{
/// <summary>
/// constants
/// </summary>
internal static class Constants
{
internal const string ViewLocation = "~/Views";
internal const string DataTokenCurrentViewContext = "umbraco-current-view-context";
}
}

View File

@@ -1,206 +1,206 @@
using System;
using System.IO;
using System.Threading;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
internal static class ControllerExtensions
{
/// <summary>
/// Return the controller name from the controller type
/// </summary>
/// <param name="controllerType"></param>
/// <returns></returns>
internal static string GetControllerName(Type controllerType)
{
if (!controllerType.Name.EndsWith("Controller"))
{
throw new InvalidOperationException("The controller type " + controllerType + " does not follow conventions, MVC Controller class names must be suffixed with the term 'Controller'");
}
return controllerType.Name.Substring(0, controllerType.Name.LastIndexOf("Controller"));
}
/// <summary>
/// Return the controller name from the controller instance
/// </summary>
/// <param name="controllerInstance"></param>
/// <returns></returns>
internal static string GetControllerName(this IController controllerInstance)
{
return GetControllerName(controllerInstance.GetType());
}
/// <summary>
/// Return the controller name from the controller type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <remarks></remarks>
internal static string GetControllerName<T>()
{
return GetControllerName(typeof(T));
}
/// <summary>
/// This is generally used for proxying to a ChildAction which requires a ViewContext to be setup
/// but since the View isn't actually rendered the IView object is null, however the rest of the
/// properties are filled in.
/// </summary>
/// <param name="controller"></param>
/// <returns></returns>
internal static ViewContext CreateEmptyViewContext(this ControllerBase controller)
{
return new ViewContext
{
Controller = controller,
HttpContext = controller.ControllerContext.HttpContext,
RequestContext = controller.ControllerContext.RequestContext,
RouteData = controller.ControllerContext.RouteData,
TempData = controller.TempData,
ViewData = controller.ViewData
};
}
/// <summary>
/// Returns the string output from a ViewResultBase object
/// </summary>
/// <param name="controller"></param>
/// <param name="viewResult"></param>
/// <returns></returns>
internal static string RenderViewResultAsString(this ControllerBase controller, ViewResultBase viewResult)
{
using (var sw = new StringWriter())
{
controller.EnsureViewObjectDataOnResult(viewResult);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, viewResult.ViewData, viewResult.TempData, sw);
viewResult.View.Render(viewContext, sw);
foreach (var v in viewResult.ViewEngineCollection)
{
v.ReleaseView(controller.ControllerContext, viewResult.View);
}
return sw.ToString().Trim();
}
}
/// <summary>
/// Renders the partial view to string.
/// </summary>
/// <param name="controller">The controller context.</param>
/// <param name="viewName">Name of the view.</param>
/// <param name="model">The model.</param>
/// <param name="isPartial">true if it is a Partial view, otherwise false for a normal view </param>
/// <returns></returns>
internal static string RenderViewToString(this ControllerBase controller, string viewName, object model, bool isPartial = false)
{
if (controller.ControllerContext == null)
throw new ArgumentException("The controller must have an assigned ControllerContext to execute this method.");
controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = isPartial == false
? ViewEngines.Engines.FindView(controller.ControllerContext, viewName, null)
: ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
if (viewResult.View == null)
throw new InvalidOperationException("No view could be found by name " + viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
/// <summary>
/// Renders the partial view to string.
/// </summary>
/// <param name="requestContext">The request context.</param>
/// <param name="viewData"></param>
/// <param name="tempData"></param>
/// <param name="viewName">Name of the view.</param>
/// <param name="model">The model.</param>
/// <param name="isPartial">true if it is a Partial view, otherwise false for a normal view </param>
/// <returns></returns>
internal static string RenderViewToString(
this RequestContext requestContext,
ViewDataDictionary viewData,
TempDataDictionary tempData,
string viewName, object model, bool isPartial = false)
{
if (requestContext == null) throw new ArgumentNullException("requestContext");
if (viewData == null) throw new ArgumentNullException("viewData");
if (tempData == null) throw new ArgumentNullException("tempData");
var routeData = requestContext.RouteData;
if (routeData.Values.ContainsKey("controller") == false)
routeData.Values.Add("controller", "Fake");
viewData.Model = model;
var controllerContext = new ControllerContext(
requestContext.HttpContext, routeData,
new FakeController
{
ViewData = viewData
});
using (var sw = new StringWriter())
{
var viewResult = isPartial == false
? ViewEngines.Engines.FindView(controllerContext, viewName, null)
: ViewEngines.Engines.FindPartialView(controllerContext, viewName);
if (viewResult.View == null)
throw new InvalidOperationException("No view could be found by name " + viewName);
var viewContext = new ViewContext(controllerContext, viewResult.View, viewData, tempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
private class FakeController : ControllerBase { protected override void ExecuteCore() { } }
/// <summary>
/// Normally in MVC the way that the View object gets assigned to the result is to Execute the ViewResult, this however
/// will write to the Response output stream which isn't what we want. Instead, this method will use the same logic inside
/// of MVC to assign the View object to the result but without executing it.
/// This is only relavent for view results of PartialViewResult or ViewResult.
/// </summary>
/// <param name="result"></param>
/// <param name="controller"></param>
private static void EnsureViewObjectDataOnResult(this ControllerBase controller, ViewResultBase result)
{
if (result.View != null) return;
if (string.IsNullOrEmpty(result.ViewName))
result.ViewName = controller.ControllerContext.RouteData.GetRequiredString("action");
if (result.View != null) return;
if (result is PartialViewResult)
{
var viewEngineResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, result.ViewName);
if (viewEngineResult.View == null)
{
throw new InvalidOperationException("Could not find the view " + result.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
}
result.View = viewEngineResult.View;
}
else if (result is ViewResult)
{
var vr = (ViewResult)result;
var viewEngineResult = ViewEngines.Engines.FindView(controller.ControllerContext, vr.ViewName, vr.MasterName);
if (viewEngineResult.View == null)
{
throw new InvalidOperationException("Could not find the view " + vr.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
}
result.View = viewEngineResult.View;
}
}
}
}
using System;
using System.IO;
using System.Threading;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
internal static class ControllerExtensions
{
/// <summary>
/// Return the controller name from the controller type
/// </summary>
/// <param name="controllerType"></param>
/// <returns></returns>
internal static string GetControllerName(Type controllerType)
{
if (!controllerType.Name.EndsWith("Controller"))
{
throw new InvalidOperationException("The controller type " + controllerType + " does not follow conventions, MVC Controller class names must be suffixed with the term 'Controller'");
}
return controllerType.Name.Substring(0, controllerType.Name.LastIndexOf("Controller"));
}
/// <summary>
/// Return the controller name from the controller instance
/// </summary>
/// <param name="controllerInstance"></param>
/// <returns></returns>
internal static string GetControllerName(this IController controllerInstance)
{
return GetControllerName(controllerInstance.GetType());
}
/// <summary>
/// Return the controller name from the controller type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// <remarks></remarks>
internal static string GetControllerName<T>()
{
return GetControllerName(typeof(T));
}
/// <summary>
/// This is generally used for proxying to a ChildAction which requires a ViewContext to be setup
/// but since the View isn't actually rendered the IView object is null, however the rest of the
/// properties are filled in.
/// </summary>
/// <param name="controller"></param>
/// <returns></returns>
internal static ViewContext CreateEmptyViewContext(this ControllerBase controller)
{
return new ViewContext
{
Controller = controller,
HttpContext = controller.ControllerContext.HttpContext,
RequestContext = controller.ControllerContext.RequestContext,
RouteData = controller.ControllerContext.RouteData,
TempData = controller.TempData,
ViewData = controller.ViewData
};
}
/// <summary>
/// Returns the string output from a ViewResultBase object
/// </summary>
/// <param name="controller"></param>
/// <param name="viewResult"></param>
/// <returns></returns>
internal static string RenderViewResultAsString(this ControllerBase controller, ViewResultBase viewResult)
{
using (var sw = new StringWriter())
{
controller.EnsureViewObjectDataOnResult(viewResult);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, viewResult.ViewData, viewResult.TempData, sw);
viewResult.View.Render(viewContext, sw);
foreach (var v in viewResult.ViewEngineCollection)
{
v.ReleaseView(controller.ControllerContext, viewResult.View);
}
return sw.ToString().Trim();
}
}
/// <summary>
/// Renders the partial view to string.
/// </summary>
/// <param name="controller">The controller context.</param>
/// <param name="viewName">Name of the view.</param>
/// <param name="model">The model.</param>
/// <param name="isPartial">true if it is a Partial view, otherwise false for a normal view </param>
/// <returns></returns>
internal static string RenderViewToString(this ControllerBase controller, string viewName, object model, bool isPartial = false)
{
if (controller.ControllerContext == null)
throw new ArgumentException("The controller must have an assigned ControllerContext to execute this method.");
controller.ViewData.Model = model;
using (var sw = new StringWriter())
{
var viewResult = isPartial == false
? ViewEngines.Engines.FindView(controller.ControllerContext, viewName, null)
: ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
if (viewResult.View == null)
throw new InvalidOperationException("No view could be found by name " + viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
/// <summary>
/// Renders the partial view to string.
/// </summary>
/// <param name="requestContext">The request context.</param>
/// <param name="viewData"></param>
/// <param name="tempData"></param>
/// <param name="viewName">Name of the view.</param>
/// <param name="model">The model.</param>
/// <param name="isPartial">true if it is a Partial view, otherwise false for a normal view </param>
/// <returns></returns>
internal static string RenderViewToString(
this RequestContext requestContext,
ViewDataDictionary viewData,
TempDataDictionary tempData,
string viewName, object model, bool isPartial = false)
{
if (requestContext == null) throw new ArgumentNullException("requestContext");
if (viewData == null) throw new ArgumentNullException("viewData");
if (tempData == null) throw new ArgumentNullException("tempData");
var routeData = requestContext.RouteData;
if (routeData.Values.ContainsKey("controller") == false)
routeData.Values.Add("controller", "Fake");
viewData.Model = model;
var controllerContext = new ControllerContext(
requestContext.HttpContext, routeData,
new FakeController
{
ViewData = viewData
});
using (var sw = new StringWriter())
{
var viewResult = isPartial == false
? ViewEngines.Engines.FindView(controllerContext, viewName, null)
: ViewEngines.Engines.FindPartialView(controllerContext, viewName);
if (viewResult.View == null)
throw new InvalidOperationException("No view could be found by name " + viewName);
var viewContext = new ViewContext(controllerContext, viewResult.View, viewData, tempData, sw);
viewResult.View.Render(viewContext, sw);
viewResult.ViewEngine.ReleaseView(controllerContext, viewResult.View);
return sw.GetStringBuilder().ToString();
}
}
private class FakeController : ControllerBase { protected override void ExecuteCore() { } }
/// <summary>
/// Normally in MVC the way that the View object gets assigned to the result is to Execute the ViewResult, this however
/// will write to the Response output stream which isn't what we want. Instead, this method will use the same logic inside
/// of MVC to assign the View object to the result but without executing it.
/// This is only relavent for view results of PartialViewResult or ViewResult.
/// </summary>
/// <param name="result"></param>
/// <param name="controller"></param>
private static void EnsureViewObjectDataOnResult(this ControllerBase controller, ViewResultBase result)
{
if (result.View != null) return;
if (string.IsNullOrEmpty(result.ViewName))
result.ViewName = controller.ControllerContext.RouteData.GetRequiredString("action");
if (result.View != null) return;
if (result is PartialViewResult)
{
var viewEngineResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, result.ViewName);
if (viewEngineResult.View == null)
{
throw new InvalidOperationException("Could not find the view " + result.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
}
result.View = viewEngineResult.View;
}
else if (result is ViewResult)
{
var vr = (ViewResult)result;
var viewEngineResult = ViewEngines.Engines.FindView(controller.ControllerContext, vr.ViewName, vr.MasterName);
if (viewEngineResult.View == null)
{
throw new InvalidOperationException("Could not find the view " + vr.ViewName + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
}
result.View = viewEngineResult.View;
}
}
}
}

View File

@@ -1,38 +1,38 @@
using System;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
internal static class ControllerFactoryExtensions
{
/// <summary>
/// Gets a controller type by the name
/// </summary>
/// <param name="factory"></param>
/// <param name="requestContext"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
/// <remarks>
/// This is related to issue: http://issues.umbraco.org/issue/U4-1726. We already have a method called GetControllerTypeInternal on our MasterControlelrFactory,
/// however, we cannot always guarantee that the usage of this will be a MasterControllerFactory like during unit tests. So we needed to create
/// this extension method to do the checks instead.
/// </remarks>
internal static Type GetControllerTypeInternal(this IControllerFactory factory, RequestContext requestContext, string controllerName)
{
var controllerFactory = factory as MasterControllerFactory;
if (controllerFactory != null)
{
return controllerFactory.GetControllerTypeInternal(requestContext, controllerName);
}
//we have no choice but to instantiate the controller
var instance = factory.CreateController(requestContext, controllerName);
if (instance != null)
{
return instance.GetType();
}
return null;
}
}
}
using System;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
internal static class ControllerFactoryExtensions
{
/// <summary>
/// Gets a controller type by the name
/// </summary>
/// <param name="factory"></param>
/// <param name="requestContext"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
/// <remarks>
/// This is related to issue: http://issues.umbraco.org/issue/U4-1726. We already have a method called GetControllerTypeInternal on our MasterControlelrFactory,
/// however, we cannot always guarantee that the usage of this will be a MasterControllerFactory like during unit tests. So we needed to create
/// this extension method to do the checks instead.
/// </remarks>
internal static Type GetControllerTypeInternal(this IControllerFactory factory, RequestContext requestContext, string controllerName)
{
var controllerFactory = factory as MasterControllerFactory;
if (controllerFactory != null)
{
return controllerFactory.GetControllerTypeInternal(requestContext, controllerName);
}
//we have no choice but to instantiate the controller
var instance = factory.CreateController(requestContext, controllerName);
if (instance != null)
{
return instance.GetType();
}
return null;
}
}
}

View File

@@ -1,212 +1,212 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.IO;
using System.Web;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
public class HtmlTagWrapper : IHtmlTagWrapper, IHtmlString
{
public HtmlTagWrapper Parent;
private readonly List<IHtmlTagWrapper> _children;
public IEnumerable<IHtmlTagWrapper> Children
{
get { return _children; }
}
private List<KeyValuePair<string, string>> _attributes;
public IEnumerable<KeyValuePair<string, string>> Attributes
{
get { return _attributes; }
}
public void ReflectAttributesFromAnonymousType(List<KeyValuePair<string, string>> newAttributes)
{
List<KeyValuePair<string, string>> mergedAttributes =
newAttributes
.Concat(Attributes)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value)
.Select(g => new KeyValuePair<string, string>(g.Key, string.Join(" ", g.ToArray())))
.ToList();
_attributes = mergedAttributes;
}
public void ReflectAttributesFromAnonymousType(object anonymousAttributes)
{
var newAttributes =
anonymousAttributes
.GetType()
.GetProperties()
.Where(prop => !string.IsNullOrWhiteSpace(string.Format("{0}", prop.GetValue(anonymousAttributes, null))))
.ToList()
.ConvertAll(prop =>
new KeyValuePair<string, string>(
prop.Name,
string.Format("{0}", prop.GetValue(anonymousAttributes, null))
)
);
ReflectAttributesFromAnonymousType(newAttributes);
}
public List<string> CssClasses;
public string Tag;
public bool Visible;
public HtmlTagWrapper(string tag)
{
this.Tag = tag;
this._children = new List<IHtmlTagWrapper>();
this.CssClasses = new List<string>();
this._attributes = new List<KeyValuePair<string, string>>();
this.Visible = true;
}
public HtmlString Write()
{
if ((Children.Any() || Attributes.Any() || CssClasses.Count > 0) && Visible)
{
using (MemoryStream ms = new MemoryStream())
{
using (TextWriter tw = new StreamWriter(ms))
{
HtmlTextWriter html = new HtmlTextWriter(tw);
this.WriteToHtmlTextWriter(html);
tw.Flush();
ms.Position = 0;
using (TextReader tr = new StreamReader(ms))
{
string result = tr.ReadToEnd();
return new HtmlString(result);
}
}
}
}
return new HtmlString(string.Empty);
}
public override string ToString()
{
return "Use @item.Write() to emit the HTML rather than @item";
}
public IHtmlString ToHtml()
{
return this.Write();
}
public void WriteToHtmlTextWriter(HtmlTextWriter html)
{
html.WriteBeginTag(Tag);
string cssClassNames = string.Join(" ", CssClasses.ToArray()).Trim();
foreach (var attribute in Attributes)
{
html.WriteAttribute(attribute.Key, attribute.Value);
}
if (!string.IsNullOrWhiteSpace(cssClassNames))
{
html.WriteAttribute("class", cssClassNames);
}
html.Write(HtmlTextWriter.TagRightChar);
foreach (var child in Children)
{
child.WriteToHtmlTextWriter(html);
}
html.WriteEndTag(Tag);
}
public HtmlTagWrapper AddClassName(string className)
{
className = className.Trim();
if (!this.CssClasses.Contains(className))
{
this.CssClasses.Add(className);
}
return this;
}
public HtmlTagWrapper RemoveClassName(string className)
{
className = className.Trim();
if (this.CssClasses.Contains(className))
{
this.CssClasses.Remove(className);
}
return this;
}
public bool HasClassName(string className)
{
className = className.Trim();
return (this.CssClasses.Contains(className));
}
public HtmlTagWrapper Attr(object newAttributes)
{
this.ReflectAttributesFromAnonymousType(newAttributes);
return this;
}
public HtmlTagWrapper Attr(string name, string value)
{
if (!string.IsNullOrWhiteSpace(value))
{
var newAttributes = new List<KeyValuePair<string, string>> {new KeyValuePair<string, string>(name, value)};
this.ReflectAttributesFromAnonymousType(newAttributes);
}
else
{
var existingKey = this._attributes.Find(item => item.Key == name);
_attributes.Remove(existingKey);
}
return this;
}
public HtmlTagWrapper AddChild(IHtmlTagWrapper newChild)
{
_children.Add(newChild);
return this;
}
public HtmlTagWrapper AddChildren(params IHtmlTagWrapper[] collection)
{
_children.AddRange(collection);
return this;
}
public HtmlTagWrapper AddChild(string text)
{
_children.Add(new HtmlTagWrapperTextNode(text));
return this;
}
public HtmlTagWrapper AddChildAt(int index, IHtmlTagWrapper newChild)
{
_children.Insert(index, newChild);
return this;
}
public HtmlTagWrapper AddChildAt(int index, string text)
{
_children.Insert(index, new HtmlTagWrapperTextNode(text));
return this;
}
public HtmlTagWrapper AddChildrenAt(int index, params IHtmlTagWrapper[] collection)
{
_children.InsertRange(index, collection);
return this;
}
public HtmlTagWrapper RemoveChildAt(int index)
{
return this;
}
public int CountChildren()
{
return this.Children.Count();
}
public HtmlTagWrapper ClearChildren()
{
return this;
}
public string ToHtmlString()
{
return this.Write().ToHtmlString();
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.IO;
using System.Web;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
public class HtmlTagWrapper : IHtmlTagWrapper, IHtmlString
{
public HtmlTagWrapper Parent;
private readonly List<IHtmlTagWrapper> _children;
public IEnumerable<IHtmlTagWrapper> Children
{
get { return _children; }
}
private List<KeyValuePair<string, string>> _attributes;
public IEnumerable<KeyValuePair<string, string>> Attributes
{
get { return _attributes; }
}
public void ReflectAttributesFromAnonymousType(List<KeyValuePair<string, string>> newAttributes)
{
List<KeyValuePair<string, string>> mergedAttributes =
newAttributes
.Concat(Attributes)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value)
.Select(g => new KeyValuePair<string, string>(g.Key, string.Join(" ", g.ToArray())))
.ToList();
_attributes = mergedAttributes;
}
public void ReflectAttributesFromAnonymousType(object anonymousAttributes)
{
var newAttributes =
anonymousAttributes
.GetType()
.GetProperties()
.Where(prop => !string.IsNullOrWhiteSpace(string.Format("{0}", prop.GetValue(anonymousAttributes, null))))
.ToList()
.ConvertAll(prop =>
new KeyValuePair<string, string>(
prop.Name,
string.Format("{0}", prop.GetValue(anonymousAttributes, null))
)
);
ReflectAttributesFromAnonymousType(newAttributes);
}
public List<string> CssClasses;
public string Tag;
public bool Visible;
public HtmlTagWrapper(string tag)
{
this.Tag = tag;
this._children = new List<IHtmlTagWrapper>();
this.CssClasses = new List<string>();
this._attributes = new List<KeyValuePair<string, string>>();
this.Visible = true;
}
public HtmlString Write()
{
if ((Children.Any() || Attributes.Any() || CssClasses.Count > 0) && Visible)
{
using (MemoryStream ms = new MemoryStream())
{
using (TextWriter tw = new StreamWriter(ms))
{
HtmlTextWriter html = new HtmlTextWriter(tw);
this.WriteToHtmlTextWriter(html);
tw.Flush();
ms.Position = 0;
using (TextReader tr = new StreamReader(ms))
{
string result = tr.ReadToEnd();
return new HtmlString(result);
}
}
}
}
return new HtmlString(string.Empty);
}
public override string ToString()
{
return "Use @item.Write() to emit the HTML rather than @item";
}
public IHtmlString ToHtml()
{
return this.Write();
}
public void WriteToHtmlTextWriter(HtmlTextWriter html)
{
html.WriteBeginTag(Tag);
string cssClassNames = string.Join(" ", CssClasses.ToArray()).Trim();
foreach (var attribute in Attributes)
{
html.WriteAttribute(attribute.Key, attribute.Value);
}
if (!string.IsNullOrWhiteSpace(cssClassNames))
{
html.WriteAttribute("class", cssClassNames);
}
html.Write(HtmlTextWriter.TagRightChar);
foreach (var child in Children)
{
child.WriteToHtmlTextWriter(html);
}
html.WriteEndTag(Tag);
}
public HtmlTagWrapper AddClassName(string className)
{
className = className.Trim();
if (!this.CssClasses.Contains(className))
{
this.CssClasses.Add(className);
}
return this;
}
public HtmlTagWrapper RemoveClassName(string className)
{
className = className.Trim();
if (this.CssClasses.Contains(className))
{
this.CssClasses.Remove(className);
}
return this;
}
public bool HasClassName(string className)
{
className = className.Trim();
return (this.CssClasses.Contains(className));
}
public HtmlTagWrapper Attr(object newAttributes)
{
this.ReflectAttributesFromAnonymousType(newAttributes);
return this;
}
public HtmlTagWrapper Attr(string name, string value)
{
if (!string.IsNullOrWhiteSpace(value))
{
var newAttributes = new List<KeyValuePair<string, string>> {new KeyValuePair<string, string>(name, value)};
this.ReflectAttributesFromAnonymousType(newAttributes);
}
else
{
var existingKey = this._attributes.Find(item => item.Key == name);
_attributes.Remove(existingKey);
}
return this;
}
public HtmlTagWrapper AddChild(IHtmlTagWrapper newChild)
{
_children.Add(newChild);
return this;
}
public HtmlTagWrapper AddChildren(params IHtmlTagWrapper[] collection)
{
_children.AddRange(collection);
return this;
}
public HtmlTagWrapper AddChild(string text)
{
_children.Add(new HtmlTagWrapperTextNode(text));
return this;
}
public HtmlTagWrapper AddChildAt(int index, IHtmlTagWrapper newChild)
{
_children.Insert(index, newChild);
return this;
}
public HtmlTagWrapper AddChildAt(int index, string text)
{
_children.Insert(index, new HtmlTagWrapperTextNode(text));
return this;
}
public HtmlTagWrapper AddChildrenAt(int index, params IHtmlTagWrapper[] collection)
{
_children.InsertRange(index, collection);
return this;
}
public HtmlTagWrapper RemoveChildAt(int index)
{
return this;
}
public int CountChildren()
{
return this.Children.Count();
}
public HtmlTagWrapper ClearChildren()
{
return this;
}
public string ToHtmlString()
{
return this.Write().ToHtmlString();
}
}
}

View File

@@ -1,18 +1,18 @@
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
public class HtmlTagWrapperTextNode : IHtmlTagWrapper
{
public string Content { get; private set; }
public HtmlTagWrapperTextNode(string content)
{
this.Content = content;
}
public void WriteToHtmlTextWriter(System.Web.UI.HtmlTextWriter html)
{
html.Write(Content);
}
}
}
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
public class HtmlTagWrapperTextNode : IHtmlTagWrapper
{
public string Content { get; private set; }
public HtmlTagWrapperTextNode(string content)
{
this.Content = content;
}
public void WriteToHtmlTextWriter(System.Web.UI.HtmlTextWriter html)
{
html.Write(Content);
}
}
}

View File

@@ -1,20 +1,20 @@
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
public interface IFilteredControllerFactory : IControllerFactory
{
/// <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>
bool CanHandle(RequestContext request);
}
}
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
public interface IFilteredControllerFactory : IControllerFactory
{
/// <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>
bool CanHandle(RequestContext request);
}
}

View File

@@ -1,9 +1,9 @@
using System.Web.UI;
namespace Umbraco.Web.Mvc
{
public interface IHtmlTagWrapper
{
void WriteToHtmlTextWriter(HtmlTextWriter html);
}
}
using System.Web.UI;
namespace Umbraco.Web.Mvc
{
public interface IHtmlTagWrapper
{
void WriteToHtmlTextWriter(HtmlTextWriter html);
}
}

View File

@@ -1,22 +1,22 @@
using System;
using System.Web.Http.Filters;
using System.Web.Mvc;
using System.Web.Routing;
using System.Windows.Forms;
using Umbraco.Web.Models;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// The interface that must be implemented for a controller to be designated to execute for route hijacking
/// </summary>
public interface IRenderMvcController : IRenderController
{
/// <summary>
/// The default action to render the front-end view
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
ActionResult Index(ContentModel model);
}
}
using System;
using System.Web.Http.Filters;
using System.Web.Mvc;
using System.Web.Routing;
using System.Windows.Forms;
using Umbraco.Web.Models;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// The interface that must be implemented for a controller to be designated to execute for route hijacking
/// </summary>
public interface IRenderMvcController : IRenderController
{
/// <summary>
/// The default action to render the front-end view
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
ActionResult Index(ContentModel model);
}
}

View File

@@ -1,113 +1,113 @@
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A controller factory which uses an internal list of <see cref="IFilteredControllerFactory"/> in order to invoke
/// different controller factories dependent upon their implementation of <see cref="IFilteredControllerFactory.CanHandle"/> for the current
/// request. Allows circumvention of MVC3's singly registered IControllerFactory.
/// </summary>
/// <remarks></remarks>
internal class MasterControllerFactory : DefaultControllerFactory
{
private readonly Func<FilteredControllerFactoryCollection> _factoriesAccessor;
private FilteredControllerFactoryCollection _factories;
/// <summary>
/// Initializes a new instance of the <see cref="MasterControllerFactory"/> with a factories accessor.
/// </summary>
/// <param name="factoriesAccessor">The factories accessor.</param>
public MasterControllerFactory(Func<FilteredControllerFactoryCollection> factoriesAccessor)
{
// note
// because the MasterControllerFactory needs to be ctored to be assigned to
// ControllerBuilder.Current when setting up Mvc and WebApi, it cannot be ctored by
// the IoC container - and yet we don't want that ctor to resolve the factories
// as that happen before everything is configured - so, passing a factories
// accessor func.
_factoriesAccessor = factoriesAccessor;
}
private IFilteredControllerFactory FactoryForRequest(RequestContext context)
{
if (_factories == null) _factories = _factoriesAccessor();
return _factories.FirstOrDefault(x => x.CanHandle(context));
}
/// <summary>
/// Creates the specified controller by using the specified request context.
/// </summary>
/// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
/// <param name="controllerName">The name of the controller.</param>
/// <returns>The controller.</returns>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext"/> parameter is null.</exception>
///
/// <exception cref="T:System.ArgumentException">The <paramref name="controllerName"/> parameter is null or empty.</exception>
/// <remarks></remarks>
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var factory = FactoryForRequest(requestContext);
return factory != null
? factory.CreateController(requestContext, controllerName)
: base.CreateController(requestContext, controllerName);
}
/// <summary>
/// Retrieves the controller type for the specified name and request context.
/// </summary>
///
/// <returns>
/// The controller type.
/// </returns>
/// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
/// <param name="controllerName">The name of the controller.</param>
internal Type GetControllerTypeInternal(RequestContext requestContext, string controllerName)
{
var factory = FactoryForRequest(requestContext);
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 GetControllerType(requestContext, controllerName);
}
/// <summary>
/// Releases the specified controller.
/// </summary>
/// <param name="icontroller">The controller to release.</param>
/// <remarks></remarks>
public override void ReleaseController(IController icontroller)
{
var released = false;
var controller = icontroller as Controller;
if (controller != null)
{
var requestContext = controller.ControllerContext.RequestContext;
var factory = FactoryForRequest(requestContext);
if (factory != null)
{
factory.ReleaseController(controller);
released = true;
}
}
if (released == false)
base.ReleaseController(icontroller);
}
}
}
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A controller factory which uses an internal list of <see cref="IFilteredControllerFactory"/> in order to invoke
/// different controller factories dependent upon their implementation of <see cref="IFilteredControllerFactory.CanHandle"/> for the current
/// request. Allows circumvention of MVC3's singly registered IControllerFactory.
/// </summary>
/// <remarks></remarks>
internal class MasterControllerFactory : DefaultControllerFactory
{
private readonly Func<FilteredControllerFactoryCollection> _factoriesAccessor;
private FilteredControllerFactoryCollection _factories;
/// <summary>
/// Initializes a new instance of the <see cref="MasterControllerFactory"/> with a factories accessor.
/// </summary>
/// <param name="factoriesAccessor">The factories accessor.</param>
public MasterControllerFactory(Func<FilteredControllerFactoryCollection> factoriesAccessor)
{
// note
// because the MasterControllerFactory needs to be ctored to be assigned to
// ControllerBuilder.Current when setting up Mvc and WebApi, it cannot be ctored by
// the IoC container - and yet we don't want that ctor to resolve the factories
// as that happen before everything is configured - so, passing a factories
// accessor func.
_factoriesAccessor = factoriesAccessor;
}
private IFilteredControllerFactory FactoryForRequest(RequestContext context)
{
if (_factories == null) _factories = _factoriesAccessor();
return _factories.FirstOrDefault(x => x.CanHandle(context));
}
/// <summary>
/// Creates the specified controller by using the specified request context.
/// </summary>
/// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
/// <param name="controllerName">The name of the controller.</param>
/// <returns>The controller.</returns>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext"/> parameter is null.</exception>
///
/// <exception cref="T:System.ArgumentException">The <paramref name="controllerName"/> parameter is null or empty.</exception>
/// <remarks></remarks>
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var factory = FactoryForRequest(requestContext);
return factory != null
? factory.CreateController(requestContext, controllerName)
: base.CreateController(requestContext, controllerName);
}
/// <summary>
/// Retrieves the controller type for the specified name and request context.
/// </summary>
///
/// <returns>
/// The controller type.
/// </returns>
/// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
/// <param name="controllerName">The name of the controller.</param>
internal Type GetControllerTypeInternal(RequestContext requestContext, string controllerName)
{
var factory = FactoryForRequest(requestContext);
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 GetControllerType(requestContext, controllerName);
}
/// <summary>
/// Releases the specified controller.
/// </summary>
/// <param name="icontroller">The controller to release.</param>
/// <remarks></remarks>
public override void ReleaseController(IController icontroller)
{
var released = false;
var controller = icontroller as Controller;
if (controller != null)
{
var requestContext = controller.ControllerContext.RequestContext;
var factory = FactoryForRequest(requestContext);
if (factory != null)
{
factory.ReleaseController(controller);
released = true;
}
}
if (released == false)
base.ReleaseController(icontroller);
}
}
}

View File

@@ -1,96 +1,96 @@
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using AuthorizeAttribute = System.Web.Mvc.AuthorizeAttribute;
using Umbraco.Core;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Attribute for attributing controller actions to restrict them
/// to just authenticated members, and optionally of a particular type and/or group
/// </summary>
public sealed class MemberAuthorizeAttribute : AuthorizeAttribute
{
// see note in HttpInstallAuthorizeAttribute
private readonly UmbracoContext _umbracoContext;
private UmbracoContext UmbracoContext => _umbracoContext ?? Current.UmbracoContext;
/// <summary>
/// THIS SHOULD BE ONLY USED FOR UNIT TESTS
/// </summary>
/// <param name="umbracoContext"></param>
public MemberAuthorizeAttribute(UmbracoContext umbracoContext)
{
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
_umbracoContext = umbracoContext;
}
public MemberAuthorizeAttribute()
{
}
/// <summary>
/// Flag for whether to allow all site visitors or just authenticated members
/// </summary>
/// <remarks>
/// This is the same as applying the [AllowAnonymous] attribute
/// </remarks>
[Obsolete("Use [AllowAnonymous] instead")]
public bool AllowAll { get; set; }
/// <summary>
/// Comma delimited list of allowed member types
/// </summary>
public string AllowType { get; set; }
/// <summary>
/// Comma delimited list of allowed member groups
/// </summary>
public string AllowGroup { get; set; }
/// <summary>
/// Comma delimited list of allowed members
/// </summary>
public string AllowMembers { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (AllowMembers.IsNullOrWhiteSpace())
AllowMembers = "";
if (AllowGroup.IsNullOrWhiteSpace())
AllowGroup = "";
if (AllowType.IsNullOrWhiteSpace())
AllowType = "";
var members = new List<int>();
foreach (var s in AllowMembers.Split(','))
{
int id;
if (int.TryParse(s, out id))
{
members.Add(id);
}
}
return UmbracoContext.Security.IsMemberAuthorized(AllowAll,
AllowType.Split(','),
AllowGroup.Split(','),
members);
}
/// <summary>
/// Override method to throw exception instead of returning a 401 result
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
throw new HttpException(403, "Resource restricted: either member is not logged on or is not of a permitted type or group.");
}
}
}
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using AuthorizeAttribute = System.Web.Mvc.AuthorizeAttribute;
using Umbraco.Core;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Attribute for attributing controller actions to restrict them
/// to just authenticated members, and optionally of a particular type and/or group
/// </summary>
public sealed class MemberAuthorizeAttribute : AuthorizeAttribute
{
// see note in HttpInstallAuthorizeAttribute
private readonly UmbracoContext _umbracoContext;
private UmbracoContext UmbracoContext => _umbracoContext ?? Current.UmbracoContext;
/// <summary>
/// THIS SHOULD BE ONLY USED FOR UNIT TESTS
/// </summary>
/// <param name="umbracoContext"></param>
public MemberAuthorizeAttribute(UmbracoContext umbracoContext)
{
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
_umbracoContext = umbracoContext;
}
public MemberAuthorizeAttribute()
{
}
/// <summary>
/// Flag for whether to allow all site visitors or just authenticated members
/// </summary>
/// <remarks>
/// This is the same as applying the [AllowAnonymous] attribute
/// </remarks>
[Obsolete("Use [AllowAnonymous] instead")]
public bool AllowAll { get; set; }
/// <summary>
/// Comma delimited list of allowed member types
/// </summary>
public string AllowType { get; set; }
/// <summary>
/// Comma delimited list of allowed member groups
/// </summary>
public string AllowGroup { get; set; }
/// <summary>
/// Comma delimited list of allowed members
/// </summary>
public string AllowMembers { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (AllowMembers.IsNullOrWhiteSpace())
AllowMembers = "";
if (AllowGroup.IsNullOrWhiteSpace())
AllowGroup = "";
if (AllowType.IsNullOrWhiteSpace())
AllowType = "";
var members = new List<int>();
foreach (var s in AllowMembers.Split(','))
{
int id;
if (int.TryParse(s, out id))
{
members.Add(id);
}
}
return UmbracoContext.Security.IsMemberAuthorized(AllowAll,
AllowType.Split(','),
AllowGroup.Split(','),
members);
}
/// <summary>
/// Override method to throw exception instead of returning a 401 result
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
throw new HttpException(403, "Resource restricted: either member is not logged on or is not of a permitted type or group.");
}
}
}

View File

@@ -1,42 +1,42 @@
using System.Linq;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// When a ChildAction is executing and we want the ModelState from the Parent context to be merged in
/// to help with validation, this filter can be used.
/// </summary>
/// <remarks>
/// By default, this filter will only merge when an Http POST is detected but this can be modified in the ctor
/// </remarks>
public class MergeModelStateToChildActionAttribute : ActionFilterAttribute
{
private readonly string[] _verb;
public MergeModelStateToChildActionAttribute()
: this(HttpVerbs.Post)
{
}
public MergeModelStateToChildActionAttribute(params HttpVerbs[] verb)
{
_verb = verb.Select(x => x.ToString().ToUpper()).ToArray();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//check if the verb matches, if so merge the ModelState before the action is executed.
if (_verb.Contains(filterContext.HttpContext.Request.HttpMethod))
{
if (filterContext.Controller.ControllerContext.IsChildAction)
{
filterContext.Controller.ViewData.ModelState.Merge(
filterContext.Controller.ControllerContext.ParentActionViewContext.ViewData.ModelState);
}
}
base.OnActionExecuting(filterContext);
}
}
}
using System.Linq;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// When a ChildAction is executing and we want the ModelState from the Parent context to be merged in
/// to help with validation, this filter can be used.
/// </summary>
/// <remarks>
/// By default, this filter will only merge when an Http POST is detected but this can be modified in the ctor
/// </remarks>
public class MergeModelStateToChildActionAttribute : ActionFilterAttribute
{
private readonly string[] _verb;
public MergeModelStateToChildActionAttribute()
: this(HttpVerbs.Post)
{
}
public MergeModelStateToChildActionAttribute(params HttpVerbs[] verb)
{
_verb = verb.Select(x => x.ToString().ToUpper()).ToArray();
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//check if the verb matches, if so merge the ModelState before the action is executed.
if (_verb.Contains(filterContext.HttpContext.Request.HttpMethod))
{
if (filterContext.Controller.ControllerContext.IsChildAction)
{
filterContext.Controller.ViewData.ModelState.Merge(
filterContext.Controller.ControllerContext.ParentActionViewContext.ViewData.ModelState);
}
}
base.OnActionExecuting(filterContext);
}
}
}

View File

@@ -1,56 +1,56 @@
using System.Linq;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// This attribute can be used for when child actions execute and will automatically merge in the viewdata from the parent context to the
/// child action result.
/// </summary>
/// <remarks>
/// This will retain any custom viewdata put into the child viewdata if the same key persists in the parent context's view data. You can always still
/// access the parent's view data normally.
/// This just simplifies working with ChildActions and view data.
///
/// NOTE: This does not mean that the parent context's view data will be merged before the action executes, if you need access to the parent context's view
/// data during controller execution you can access it normally.
///
/// NOTE: This recursively merges in all ParentActionViewContext ancestry in case there's child actions inside of child actions.
/// </remarks>
public class MergeParentContextViewDataAttribute : ActionFilterAttribute
{
/// <summary>
/// Merge in the parent context's view data if this is a child action when the result is being executed
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.IsChildAction)
{
MergeCurrentParent(filterContext.Controller, filterContext.ParentActionViewContext);
}
base.OnResultExecuting(filterContext);
}
/// <summary>
/// Recursively merges in each parent view context into the target
/// </summary>
/// <param name="target"></param>
/// <param name="currentParent"></param>
private static void MergeCurrentParent(ControllerBase target, ViewContext currentParent)
{
if (currentParent != null && currentParent.ViewData != null && currentParent.ViewData.Any())
{
target.ViewData.MergeViewDataFrom(currentParent.ViewData);
//Recurse!
if (currentParent.IsChildAction)
{
MergeCurrentParent(target, currentParent.ParentActionViewContext);
}
}
}
}
}
using System.Linq;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// This attribute can be used for when child actions execute and will automatically merge in the viewdata from the parent context to the
/// child action result.
/// </summary>
/// <remarks>
/// This will retain any custom viewdata put into the child viewdata if the same key persists in the parent context's view data. You can always still
/// access the parent's view data normally.
/// This just simplifies working with ChildActions and view data.
///
/// NOTE: This does not mean that the parent context's view data will be merged before the action executes, if you need access to the parent context's view
/// data during controller execution you can access it normally.
///
/// NOTE: This recursively merges in all ParentActionViewContext ancestry in case there's child actions inside of child actions.
/// </remarks>
public class MergeParentContextViewDataAttribute : ActionFilterAttribute
{
/// <summary>
/// Merge in the parent context's view data if this is a child action when the result is being executed
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.IsChildAction)
{
MergeCurrentParent(filterContext.Controller, filterContext.ParentActionViewContext);
}
base.OnResultExecuting(filterContext);
}
/// <summary>
/// Recursively merges in each parent view context into the target
/// </summary>
/// <param name="target"></param>
/// <param name="currentParent"></param>
private static void MergeCurrentParent(ControllerBase target, ViewContext currentParent)
{
if (currentParent != null && currentParent.ViewData != null && currentParent.ViewData.Any())
{
target.ViewData.MergeViewDataFrom(currentParent.ViewData);
//Recurse!
if (currentParent.IsChildAction)
{
MergeCurrentParent(target, currentParent.ParentActionViewContext);
}
}
}
}
}

View File

@@ -1,20 +1,20 @@
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Used to ensure that actions with duplicate names that are not child actions don't get executed when
/// we are Posting and not redirecting.
/// </summary>
/// <remarks>
/// See issue: http://issues.umbraco.org/issue/U4-1819
/// </remarks>
public class NotChildAction : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
var isChildAction = controllerContext.IsChildAction;
return !isChildAction;
}
}
}
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Used to ensure that actions with duplicate names that are not child actions don't get executed when
/// we are Posting and not redirecting.
/// </summary>
/// <remarks>
/// See issue: http://issues.umbraco.org/issue/U4-1819
/// </remarks>
public class NotChildAction : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
var isChildAction = controllerContext.IsChildAction;
return !isChildAction;
}
}
}

View File

@@ -1,93 +1,93 @@
using System;
using System.Collections.Concurrent;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Composing;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for plugin controllers.
/// </summary>
public abstract class PluginController : Controller, IDiscoverable
{
private static readonly ConcurrentDictionary<Type, PluginControllerMetadata> MetadataStorage
= new ConcurrentDictionary<Type, PluginControllerMetadata>();
private UmbracoHelper _umbracoHelper;
// for debugging purposes
internal Guid InstanceId { get; } = Guid.NewGuid();
// note
// properties marked as [Inject] below will be property-injected (vs constructor-injected) in
// order to keep the constuctor as light as possible, so that ppl implementing eg a SurfaceController
// don't need to implement complex constructors + need to refactor them each time we change ours.
// this means that these properties have a setter.
// what can go wrong?
/// <summary>
/// Gets or sets the Umbraco context.
/// </summary>
public virtual UmbracoContext UmbracoContext { get; }
/// <summary>
/// Gets or sets the database context.
/// </summary>
public IUmbracoDatabaseFactory DatabaseFactory { get; }
/// <summary>
/// Gets or sets the services context.
/// </summary>
public ServiceContext Services { get; }
/// <summary>
/// Gets or sets the application cache.
/// </summary>
public CacheHelper ApplicationCache { get; }
/// <summary>
/// Gets or sets the logger.
/// </summary>
public ILogger Logger { get; }
/// <summary>
/// Gets or sets the profiling logger.
/// </summary>
public ProfilingLogger ProfilingLogger { get; }
/// <summary>
/// Gets the membership helper.
/// </summary>
public MembershipHelper Members => Umbraco.MembershipHelper;
/// <summary>
/// Gets the Umbraco helper.
/// </summary>
public UmbracoHelper Umbraco
{
get
{
return _umbracoHelper
?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache));
}
internal set // tests
{
_umbracoHelper = value;
}
}
/// <summary>
/// Gets metadata for this instance.
/// </summary>
internal PluginControllerMetadata Metadata => GetMetadata(GetType());
using System;
using System.Collections.Concurrent;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Composing;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for plugin controllers.
/// </summary>
public abstract class PluginController : Controller, IDiscoverable
{
private static readonly ConcurrentDictionary<Type, PluginControllerMetadata> MetadataStorage
= new ConcurrentDictionary<Type, PluginControllerMetadata>();
private UmbracoHelper _umbracoHelper;
// for debugging purposes
internal Guid InstanceId { get; } = Guid.NewGuid();
// note
// properties marked as [Inject] below will be property-injected (vs constructor-injected) in
// order to keep the constuctor as light as possible, so that ppl implementing eg a SurfaceController
// don't need to implement complex constructors + need to refactor them each time we change ours.
// this means that these properties have a setter.
// what can go wrong?
/// <summary>
/// Gets or sets the Umbraco context.
/// </summary>
public virtual UmbracoContext UmbracoContext { get; }
/// <summary>
/// Gets or sets the database context.
/// </summary>
public IUmbracoDatabaseFactory DatabaseFactory { get; }
/// <summary>
/// Gets or sets the services context.
/// </summary>
public ServiceContext Services { get; }
/// <summary>
/// Gets or sets the application cache.
/// </summary>
public CacheHelper ApplicationCache { get; }
/// <summary>
/// Gets or sets the logger.
/// </summary>
public ILogger Logger { get; }
/// <summary>
/// Gets or sets the profiling logger.
/// </summary>
public ProfilingLogger ProfilingLogger { get; }
/// <summary>
/// Gets the membership helper.
/// </summary>
public MembershipHelper Members => Umbraco.MembershipHelper;
/// <summary>
/// Gets the Umbraco helper.
/// </summary>
public UmbracoHelper Umbraco
{
get
{
return _umbracoHelper
?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache));
}
internal set // tests
{
_umbracoHelper = value;
}
}
/// <summary>
/// Gets metadata for this instance.
/// </summary>
internal PluginControllerMetadata Metadata => GetMetadata(GetType());
protected PluginController()
: this(
Current.Container.GetInstance<UmbracoContext>(),
@@ -99,39 +99,39 @@ namespace Umbraco.Web.Mvc
)
{
}
protected PluginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger)
{
UmbracoContext = umbracoContext;
DatabaseFactory = databaseFactory;
Services = services;
ApplicationCache = applicationCache;
Logger = logger;
ProfilingLogger = profilingLogger;
}
/// <summary>
/// Gets metadata for a controller type.
/// </summary>
/// <param name="controllerType">The controller type.</param>
/// <returns>Metadata for the controller type.</returns>
internal static PluginControllerMetadata GetMetadata(Type controllerType)
{
return MetadataStorage.GetOrAdd(controllerType, type =>
{
// plugin controller? back-office controller?
var pluginAttribute = controllerType.GetCustomAttribute<PluginControllerAttribute>(false);
var backOfficeAttribute = controllerType.GetCustomAttribute<IsBackOfficeAttribute>(true);
return new PluginControllerMetadata
{
AreaName = pluginAttribute?.AreaName,
ControllerName = ControllerExtensions.GetControllerName(controllerType),
ControllerNamespace = controllerType.Namespace,
ControllerType = controllerType,
IsBackOffice = backOfficeAttribute != null
};
});
}
}
}
protected PluginController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger)
{
UmbracoContext = umbracoContext;
DatabaseFactory = databaseFactory;
Services = services;
ApplicationCache = applicationCache;
Logger = logger;
ProfilingLogger = profilingLogger;
}
/// <summary>
/// Gets metadata for a controller type.
/// </summary>
/// <param name="controllerType">The controller type.</param>
/// <returns>Metadata for the controller type.</returns>
internal static PluginControllerMetadata GetMetadata(Type controllerType)
{
return MetadataStorage.GetOrAdd(controllerType, type =>
{
// plugin controller? back-office controller?
var pluginAttribute = controllerType.GetCustomAttribute<PluginControllerAttribute>(false);
var backOfficeAttribute = controllerType.GetCustomAttribute<IsBackOfficeAttribute>(true);
return new PluginControllerMetadata
{
AreaName = pluginAttribute?.AreaName,
ControllerName = ControllerExtensions.GetControllerName(controllerType),
ControllerNamespace = controllerType.Namespace,
ControllerType = controllerType,
IsBackOffice = backOfficeAttribute != null
};
});
}
}
}

View File

@@ -1,98 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Composing;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A custom area for controllers that are plugins
/// </summary>
internal class PluginControllerArea : AreaRegistration
{
private readonly IGlobalSettings _globalSettings;
private readonly IEnumerable<PluginControllerMetadata> _surfaceControllers;
private readonly IEnumerable<PluginControllerMetadata> _apiControllers;
private readonly string _areaName;
/// <summary>
/// The constructor accepts all types of plugin controllers and will verify that ALL of them have the same areaName assigned to them
/// based on their PluginControllerAttribute. If they are not the same an exception will be thrown.
/// </summary>
/// <param name="globalSettings"></param>
/// <param name="pluginControllers"></param>
public PluginControllerArea(IGlobalSettings globalSettings, IEnumerable<PluginControllerMetadata> pluginControllers)
{
_globalSettings = globalSettings;
var controllers = pluginControllers.ToArray();
if (controllers.Any(x => x.AreaName.IsNullOrWhiteSpace()))
{
throw new InvalidOperationException("Cannot create a PluginControllerArea unless all plugin controllers assigned have a PluginControllerAttribute assigned");
}
_areaName = controllers.First().AreaName;
foreach(var c in controllers)
{
if (c.AreaName != _areaName)
{
throw new InvalidOperationException("Cannot create a PluginControllerArea unless all plugin controllers assigned have the same AreaName. The first AreaName found was " + _areaName + " however, the controller of type " + c.GetType().FullName + " has an AreaName of " + c.AreaName);
}
}
//get the controllers
_surfaceControllers = controllers.Where(x => TypeHelper.IsTypeAssignableFrom<SurfaceController>(x.ControllerType));
_apiControllers = controllers.Where(x => TypeHelper.IsTypeAssignableFrom<UmbracoApiController>(x.ControllerType));
}
public override void RegisterArea(AreaRegistrationContext context)
{
MapRouteSurfaceControllers(context.Routes, _surfaceControllers);
MapRouteApiControllers(context.Routes, _apiControllers);
}
public override string AreaName
{
get { return _areaName; }
}
/// <summary>
/// Registers all surface controller routes
/// </summary>
/// <param name="routes"></param>
/// <param name="surfaceControllers"></param>
/// <remarks>
/// The routes will be:
///
/// /Umbraco/[AreaName]/[ControllerName]/[Action]/[Id]
/// </remarks>
private void MapRouteSurfaceControllers(RouteCollection routes, IEnumerable<PluginControllerMetadata> surfaceControllers)
{
foreach (var s in surfaceControllers)
{
var route = this.RouteControllerPlugin(_globalSettings, s.ControllerName, s.ControllerType, routes, "", "Index", UrlParameter.Optional, "surface");
//set the route handler to our SurfaceRouteHandler
route.RouteHandler = new SurfaceRouteHandler();
}
}
/// <summary>
/// Registers all api controller routes
/// </summary>
/// <param name="routes"></param>
/// <param name="apiControllers"></param>
private void MapRouteApiControllers(RouteCollection routes, IEnumerable<PluginControllerMetadata> apiControllers)
{
foreach (var s in apiControllers)
{
this.RouteControllerPlugin(_globalSettings, s.ControllerName, s.ControllerType, routes, "", "", UrlParameter.Optional, "api",
isMvc: false,
areaPathPrefix: s.IsBackOffice ? "backoffice" : null);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Composing;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A custom area for controllers that are plugins
/// </summary>
internal class PluginControllerArea : AreaRegistration
{
private readonly IGlobalSettings _globalSettings;
private readonly IEnumerable<PluginControllerMetadata> _surfaceControllers;
private readonly IEnumerable<PluginControllerMetadata> _apiControllers;
private readonly string _areaName;
/// <summary>
/// The constructor accepts all types of plugin controllers and will verify that ALL of them have the same areaName assigned to them
/// based on their PluginControllerAttribute. If they are not the same an exception will be thrown.
/// </summary>
/// <param name="globalSettings"></param>
/// <param name="pluginControllers"></param>
public PluginControllerArea(IGlobalSettings globalSettings, IEnumerable<PluginControllerMetadata> pluginControllers)
{
_globalSettings = globalSettings;
var controllers = pluginControllers.ToArray();
if (controllers.Any(x => x.AreaName.IsNullOrWhiteSpace()))
{
throw new InvalidOperationException("Cannot create a PluginControllerArea unless all plugin controllers assigned have a PluginControllerAttribute assigned");
}
_areaName = controllers.First().AreaName;
foreach(var c in controllers)
{
if (c.AreaName != _areaName)
{
throw new InvalidOperationException("Cannot create a PluginControllerArea unless all plugin controllers assigned have the same AreaName. The first AreaName found was " + _areaName + " however, the controller of type " + c.GetType().FullName + " has an AreaName of " + c.AreaName);
}
}
//get the controllers
_surfaceControllers = controllers.Where(x => TypeHelper.IsTypeAssignableFrom<SurfaceController>(x.ControllerType));
_apiControllers = controllers.Where(x => TypeHelper.IsTypeAssignableFrom<UmbracoApiController>(x.ControllerType));
}
public override void RegisterArea(AreaRegistrationContext context)
{
MapRouteSurfaceControllers(context.Routes, _surfaceControllers);
MapRouteApiControllers(context.Routes, _apiControllers);
}
public override string AreaName
{
get { return _areaName; }
}
/// <summary>
/// Registers all surface controller routes
/// </summary>
/// <param name="routes"></param>
/// <param name="surfaceControllers"></param>
/// <remarks>
/// The routes will be:
///
/// /Umbraco/[AreaName]/[ControllerName]/[Action]/[Id]
/// </remarks>
private void MapRouteSurfaceControllers(RouteCollection routes, IEnumerable<PluginControllerMetadata> surfaceControllers)
{
foreach (var s in surfaceControllers)
{
var route = this.RouteControllerPlugin(_globalSettings, s.ControllerName, s.ControllerType, routes, "", "Index", UrlParameter.Optional, "surface");
//set the route handler to our SurfaceRouteHandler
route.RouteHandler = new SurfaceRouteHandler();
}
}
/// <summary>
/// Registers all api controller routes
/// </summary>
/// <param name="routes"></param>
/// <param name="apiControllers"></param>
private void MapRouteApiControllers(RouteCollection routes, IEnumerable<PluginControllerMetadata> apiControllers)
{
foreach (var s in apiControllers)
{
this.RouteControllerPlugin(_globalSettings, s.ControllerName, s.ControllerType, routes, "", "", UrlParameter.Optional, "api",
isMvc: false,
areaPathPrefix: s.IsBackOffice ? "backoffice" : null);
}
}
}
}

View File

@@ -1,26 +1,26 @@
using System;
using System.Linq;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// An attribute applied to a plugin controller that requires that it is routed to its own area
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginControllerAttribute : Attribute
{
public string AreaName { get; private set; }
public PluginControllerAttribute(string areaName)
{
//validate this, only letters and digits allowed.
if (areaName.Any(c => !Char.IsLetterOrDigit(c)))
{
throw new FormatException("The areaName specified " + areaName + " can only contains letters and digits");
}
AreaName = areaName;
}
}
}
using System;
using System.Linq;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// An attribute applied to a plugin controller that requires that it is routed to its own area
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginControllerAttribute : Attribute
{
public string AreaName { get; private set; }
public PluginControllerAttribute(string areaName)
{
//validate this, only letters and digits allowed.
if (areaName.Any(c => !Char.IsLetterOrDigit(c)))
{
throw new FormatException("The areaName specified " + areaName + " can only contains letters and digits");
}
AreaName = areaName;
}
}
}

View File

@@ -1,21 +1,21 @@
using System;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents some metadata about the controller
/// </summary>
internal class PluginControllerMetadata
{
internal Type ControllerType { get; set; }
internal string ControllerName { get; set; }
internal string ControllerNamespace { get; set; }
internal string AreaName { get; set; }
/// <summary>
/// This is determined by another attribute [IsBackOffice] which slightly modifies the route path
/// allowing us to determine if it is indeed a back office request or not
/// </summary>
internal bool IsBackOffice { get; set; }
}
}
using System;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents some metadata about the controller
/// </summary>
internal class PluginControllerMetadata
{
internal Type ControllerType { get; set; }
internal string ControllerName { get; set; }
internal string ControllerNamespace { get; set; }
internal string AreaName { get; set; }
/// <summary>
/// This is determined by another attribute [IsBackOffice] which slightly modifies the route path
/// allowing us to determine if it is indeed a back office request or not
/// </summary>
internal bool IsBackOffice { get; set; }
}
}

View File

@@ -1,106 +1,106 @@
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Lucene.Net.Util;
using Umbraco.Core.IO;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A view engine to look into the App_Plugins folder for views for packaged controllers
/// </summary>
public class PluginViewEngine : RazorViewEngine
{
/// <summary>
/// Constructor
/// </summary>
public PluginViewEngine()
{
SetViewLocations();
}
private void SetViewLocations()
{
//these are the originals:
//base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
//base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
//base.AreaPartialViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
//base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
//base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
//base.PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
//base.FileExtensions = new string[] { "cshtml", "vbhtml" };
var viewLocationsArray = new[]
{
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.vbhtml")
};
//set all of the area view locations to the plugin folder
AreaViewLocationFormats = viewLocationsArray
.Concat(new[]
{
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.vbhtml")
})
.ToArray();
AreaMasterLocationFormats = viewLocationsArray;
AreaPartialViewLocationFormats = new[]
{
//will be used when we have partial view and child action macros
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Partials/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Partials/{0}.vbhtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/MacroPartials/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/MacroPartials/{0}.vbhtml"),
//for partials
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.vbhtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.vbhtml")
};
}
/// <summary>
/// Ensures that the correct web.config for razor exists in the /Views folder.
/// </summary>
private void EnsureFolderAndWebConfig(ViewEngineResult result)
{
if (result.View == null) return;
var razorResult = result.View as RazorView;
if (razorResult == null) return;
var folder = Path.GetDirectoryName(IOHelper.MapPath(razorResult.ViewPath));
//now we need to get the /View/ folder
var viewFolder = folder.Substring(0, folder.LastIndexOf("\\Views\\")) + "\\Views";
//ensure the web.config file is in the ~/Views folder
Directory.CreateDirectory(viewFolder);
if (!File.Exists(Path.Combine(viewFolder, "web.config")))
{
using (var writer = File.CreateText(Path.Combine(viewFolder, "web.config")))
{
writer.Write(Strings.WebConfigTemplate);
}
}
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
var result = base.FindView(controllerContext, viewName, masterName, useCache);
EnsureFolderAndWebConfig(result);
return result;
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
var result = base.FindPartialView(controllerContext, partialViewName, useCache);
EnsureFolderAndWebConfig(result);
return result;
}
}
}
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Lucene.Net.Util;
using Umbraco.Core.IO;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A view engine to look into the App_Plugins folder for views for packaged controllers
/// </summary>
public class PluginViewEngine : RazorViewEngine
{
/// <summary>
/// Constructor
/// </summary>
public PluginViewEngine()
{
SetViewLocations();
}
private void SetViewLocations()
{
//these are the originals:
//base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
//base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
//base.AreaPartialViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
//base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
//base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
//base.PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
//base.FileExtensions = new string[] { "cshtml", "vbhtml" };
var viewLocationsArray = new[]
{
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.vbhtml")
};
//set all of the area view locations to the plugin folder
AreaViewLocationFormats = viewLocationsArray
.Concat(new[]
{
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.vbhtml")
})
.ToArray();
AreaMasterLocationFormats = viewLocationsArray;
AreaPartialViewLocationFormats = new[]
{
//will be used when we have partial view and child action macros
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Partials/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Partials/{0}.vbhtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/MacroPartials/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/MacroPartials/{0}.vbhtml"),
//for partials
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.vbhtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"),
string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.vbhtml")
};
}
/// <summary>
/// Ensures that the correct web.config for razor exists in the /Views folder.
/// </summary>
private void EnsureFolderAndWebConfig(ViewEngineResult result)
{
if (result.View == null) return;
var razorResult = result.View as RazorView;
if (razorResult == null) return;
var folder = Path.GetDirectoryName(IOHelper.MapPath(razorResult.ViewPath));
//now we need to get the /View/ folder
var viewFolder = folder.Substring(0, folder.LastIndexOf("\\Views\\")) + "\\Views";
//ensure the web.config file is in the ~/Views folder
Directory.CreateDirectory(viewFolder);
if (!File.Exists(Path.Combine(viewFolder, "web.config")))
{
using (var writer = File.CreateText(Path.Combine(viewFolder, "web.config")))
{
writer.Write(Strings.WebConfigTemplate);
}
}
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
var result = base.FindView(controllerContext, viewName, masterName, useCache);
EnsureFolderAndWebConfig(result);
return result;
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
var result = base.FindPartialView(controllerContext, partialViewName, useCache);
EnsureFolderAndWebConfig(result);
return result;
}
}
}

View File

@@ -1,12 +1,12 @@
using System;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the data required to proxy a request to a surface controller for posted data
/// </summary>
internal class PostedDataProxyInfo : RouteDefinition
{
public string Area { get; set; }
}
}
using System;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the data required to proxy a request to a surface controller for posted data
/// </summary>
internal class PostedDataProxyInfo : RouteDefinition
{
public string Area { get; set; }
}
}

View File

@@ -1,31 +1,31 @@
using System.IO;
using System.Web.Mvc;
using Umbraco.Core.Logging;
using Umbraco.Web;
using Umbraco.Web.Composing;
namespace Umbraco.Core.Profiling
{
public class ProfilingView : IView
{
private readonly IView _inner;
private readonly string _name;
private readonly string _viewPath;
public ProfilingView(IView inner)
{
_inner = inner;
_name = inner.GetType().Name;
var razorView = inner as RazorView;
_viewPath = razorView != null ? razorView.ViewPath : "Unknown";
}
public void Render(ViewContext viewContext, TextWriter writer)
{
using (Current.Profiler.Step(string.Format("{0}.Render: {1}", _name, _viewPath)))
{
_inner.Render(viewContext, writer);
}
}
}
}
using System.IO;
using System.Web.Mvc;
using Umbraco.Core.Logging;
using Umbraco.Web;
using Umbraco.Web.Composing;
namespace Umbraco.Core.Profiling
{
public class ProfilingView : IView
{
private readonly IView _inner;
private readonly string _name;
private readonly string _viewPath;
public ProfilingView(IView inner)
{
_inner = inner;
_name = inner.GetType().Name;
var razorView = inner as RazorView;
_viewPath = razorView != null ? razorView.ViewPath : "Unknown";
}
public void Render(ViewContext viewContext, TextWriter writer)
{
using (Current.Profiler.Step(string.Format("{0}.Render: {1}", _name, _viewPath)))
{
_inner.Render(viewContext, writer);
}
}
}
}

View File

@@ -1,51 +1,51 @@
using System.Web.Mvc;
using Umbraco.Core.Logging;
using Umbraco.Web;
using Umbraco.Web.Composing;
namespace Umbraco.Core.Profiling
{
public class ProfilingViewEngine: IViewEngine
{
internal readonly IViewEngine Inner;
private readonly string _name;
public ProfilingViewEngine(IViewEngine inner)
{
Inner = inner;
_name = inner.GetType().Name;
}
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
using (Current.Profiler.Step(string.Format("{0}.FindPartialView, {1}, {2}", _name, partialViewName, useCache)))
{
return WrapResult(Inner.FindPartialView(controllerContext, partialViewName, useCache));
}
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
using (Current.Profiler.Step(string.Format("{0}.FindView, {1}, {2}, {3}", _name, viewName, masterName, useCache)))
{
return WrapResult(Inner.FindView(controllerContext, viewName, masterName, useCache));
}
}
private static ViewEngineResult WrapResult(ViewEngineResult innerResult)
{
var profiledResult = innerResult.View != null ?
new ViewEngineResult(new ProfilingView(innerResult.View), innerResult.ViewEngine) :
new ViewEngineResult(innerResult.SearchedLocations);
return profiledResult;
}
public void ReleaseView(ControllerContext controllerContext, IView view)
{
using (Current.Profiler.Step(string.Format("{0}.ReleaseView, {1}", _name, view.GetType().Name)))
{
Inner.ReleaseView(controllerContext, view);
}
}
}
}
using System.Web.Mvc;
using Umbraco.Core.Logging;
using Umbraco.Web;
using Umbraco.Web.Composing;
namespace Umbraco.Core.Profiling
{
public class ProfilingViewEngine: IViewEngine
{
internal readonly IViewEngine Inner;
private readonly string _name;
public ProfilingViewEngine(IViewEngine inner)
{
Inner = inner;
_name = inner.GetType().Name;
}
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
using (Current.Profiler.Step(string.Format("{0}.FindPartialView, {1}, {2}", _name, partialViewName, useCache)))
{
return WrapResult(Inner.FindPartialView(controllerContext, partialViewName, useCache));
}
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
using (Current.Profiler.Step(string.Format("{0}.FindView, {1}, {2}, {3}", _name, viewName, masterName, useCache)))
{
return WrapResult(Inner.FindView(controllerContext, viewName, masterName, useCache));
}
}
private static ViewEngineResult WrapResult(ViewEngineResult innerResult)
{
var profiledResult = innerResult.View != null ?
new ViewEngineResult(new ProfilingView(innerResult.View), innerResult.ViewEngine) :
new ViewEngineResult(innerResult.SearchedLocations);
return profiledResult;
}
public void ReleaseView(ControllerContext controllerContext, IView view)
{
using (Current.Profiler.Step(string.Format("{0}.ReleaseView, {1}", _name, view.GetType().Name)))
{
Inner.ReleaseView(controllerContext, view);
}
}
}
}

View File

@@ -1,58 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Allows an Action to execute with an arbitrary number of QueryStrings
/// </summary>
/// <remarks>
/// Just like you can POST an arbitrary number of parameters to an Action, you can't GET an arbitrary number
/// but this will allow you to do it
///
/// http://stackoverflow.com/questions/488061/passing-multiple-parameters-to-controller-in-asp-net-mvc-also-generating-on-the
/// </remarks>
public class QueryStringFilterAttribute : ActionFilterAttribute
{
public string ParameterName { get; private set; }
public QueryStringFilterAttribute(string parameterName)
{
if (string.IsNullOrEmpty(parameterName))
throw new ArgumentException("ParameterName is required.");
ParameterName = parameterName;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var nonNullKeys = filterContext.HttpContext.Request.QueryString.AllKeys.Where(x => !string.IsNullOrEmpty(x));
var vals = nonNullKeys.ToDictionary(q => q, q => (object)filterContext.HttpContext.Request.QueryString[q]);
var qs = ToFormCollection(vals);
filterContext.ActionParameters[ParameterName] = qs;
base.OnActionExecuting(filterContext);
}
/// <summary>
/// Converts a dictionary to a FormCollection
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static FormCollection ToFormCollection(IDictionary<string, object> d)
{
var n = new FormCollection();
foreach (var i in d)
{
n.Add(i.Key, Convert.ToString(i.Value));
}
return n;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Allows an Action to execute with an arbitrary number of QueryStrings
/// </summary>
/// <remarks>
/// Just like you can POST an arbitrary number of parameters to an Action, you can't GET an arbitrary number
/// but this will allow you to do it
///
/// http://stackoverflow.com/questions/488061/passing-multiple-parameters-to-controller-in-asp-net-mvc-also-generating-on-the
/// </remarks>
public class QueryStringFilterAttribute : ActionFilterAttribute
{
public string ParameterName { get; private set; }
public QueryStringFilterAttribute(string parameterName)
{
if (string.IsNullOrEmpty(parameterName))
throw new ArgumentException("ParameterName is required.");
ParameterName = parameterName;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var nonNullKeys = filterContext.HttpContext.Request.QueryString.AllKeys.Where(x => !string.IsNullOrEmpty(x));
var vals = nonNullKeys.ToDictionary(q => q, q => (object)filterContext.HttpContext.Request.QueryString[q]);
var qs = ToFormCollection(vals);
filterContext.ActionParameters[ParameterName] = qs;
base.OnActionExecuting(filterContext);
}
/// <summary>
/// Converts a dictionary to a FormCollection
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
public static FormCollection ToFormCollection(IDictionary<string, object> d)
{
var n = new FormCollection();
foreach (var i in d)
{
n.Add(i.Key, Convert.ToString(i.Value));
}
return n;
}
}
}

View File

@@ -1,231 +1,231 @@
using System;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Redirects to an Umbraco page by Id or Entity
/// </summary>
public class RedirectToUmbracoPageResult : ActionResult
{
private IPublishedContent _publishedContent;
private readonly int _pageId;
private NameValueCollection _queryStringValues;
private readonly UmbracoContext _umbracoContext;
private string _url;
public string Url
{
get
{
if (!_url.IsNullOrWhiteSpace()) return _url;
if (PublishedContent == null)
{
throw new InvalidOperationException(string.Format("Cannot redirect, no entity was found for id {0}", _pageId));
}
var result = _umbracoContext.UrlProvider.GetUrl(PublishedContent.Id);
if (result != "#")
{
_url = result;
return _url;
}
throw new InvalidOperationException(string.Format("Could not route to entity with id {0}, the NiceUrlProvider could not generate a URL", _pageId));
}
}
public int PageId
{
get { return _pageId; }
}
public IPublishedContent PublishedContent
{
get
{
if (_publishedContent != null) return _publishedContent;
//need to get the URL for the page
_publishedContent = UmbracoContext.Current.ContentCache.GetById(_pageId);
return _publishedContent;
}
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
public RedirectToUmbracoPageResult(int pageId)
: this(pageId, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues)
: this(pageId, queryStringValues, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
public RedirectToUmbracoPageResult(int pageId, string queryString)
: this(pageId, queryString, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent)
: this(publishedContent, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues)
: this(publishedContent, queryStringValues, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="queryString"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString)
: this(publishedContent, queryString, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, UmbracoContext umbracoContext)
{
_pageId = pageId;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues, UmbracoContext umbracoContext)
{
_pageId = pageId;
_queryStringValues = queryStringValues;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, string queryString, UmbracoContext umbracoContext)
{
_pageId = pageId;
_queryStringValues = ParseQueryString(queryString);
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_queryStringValues = queryStringValues;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryString"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_queryStringValues = ParseQueryString(queryString);
_umbracoContext = umbracoContext;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null) throw new ArgumentNullException("context");
if (context.IsChildAction)
{
throw new InvalidOperationException("Cannot redirect from a Child Action");
}
var destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
if (_queryStringValues != null && _queryStringValues.Count > 0)
{
destinationUrl = destinationUrl += "?" + string.Join("&",
_queryStringValues.AllKeys.Select(x => x + "=" + HttpUtility.UrlEncode(_queryStringValues[x])));
}
context.Controller.TempData.Keep();
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
}
private NameValueCollection ParseQueryString(string queryString)
{
if (!string.IsNullOrEmpty(queryString))
{
return HttpUtility.ParseQueryString(queryString);
}
return null;
}
}
}
using System;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Redirects to an Umbraco page by Id or Entity
/// </summary>
public class RedirectToUmbracoPageResult : ActionResult
{
private IPublishedContent _publishedContent;
private readonly int _pageId;
private NameValueCollection _queryStringValues;
private readonly UmbracoContext _umbracoContext;
private string _url;
public string Url
{
get
{
if (!_url.IsNullOrWhiteSpace()) return _url;
if (PublishedContent == null)
{
throw new InvalidOperationException(string.Format("Cannot redirect, no entity was found for id {0}", _pageId));
}
var result = _umbracoContext.UrlProvider.GetUrl(PublishedContent.Id);
if (result != "#")
{
_url = result;
return _url;
}
throw new InvalidOperationException(string.Format("Could not route to entity with id {0}, the NiceUrlProvider could not generate a URL", _pageId));
}
}
public int PageId
{
get { return _pageId; }
}
public IPublishedContent PublishedContent
{
get
{
if (_publishedContent != null) return _publishedContent;
//need to get the URL for the page
_publishedContent = UmbracoContext.Current.ContentCache.GetById(_pageId);
return _publishedContent;
}
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
public RedirectToUmbracoPageResult(int pageId)
: this(pageId, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues)
: this(pageId, queryStringValues, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
public RedirectToUmbracoPageResult(int pageId, string queryString)
: this(pageId, queryString, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent)
: this(publishedContent, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues)
: this(publishedContent, queryStringValues, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="queryString"></param>
/// <param name="queryStringValues"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString)
: this(publishedContent, queryString, UmbracoContext.Current)
{
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, UmbracoContext umbracoContext)
{
_pageId = pageId;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, NameValueCollection queryStringValues, UmbracoContext umbracoContext)
{
_pageId = pageId;
_queryStringValues = queryStringValues;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(int pageId, string queryString, UmbracoContext umbracoContext)
{
_pageId = pageId;
_queryStringValues = ParseQueryString(queryString);
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, NameValueCollection queryStringValues, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_queryStringValues = queryStringValues;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Creates a new RedirectToUmbracoResult
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryString"></param>
/// <param name="umbracoContext"></param>
public RedirectToUmbracoPageResult(IPublishedContent publishedContent, string queryString, UmbracoContext umbracoContext)
{
_publishedContent = publishedContent;
_pageId = publishedContent.Id;
_queryStringValues = ParseQueryString(queryString);
_umbracoContext = umbracoContext;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null) throw new ArgumentNullException("context");
if (context.IsChildAction)
{
throw new InvalidOperationException("Cannot redirect from a Child Action");
}
var destinationUrl = UrlHelper.GenerateContentUrl(Url, context.HttpContext);
if (_queryStringValues != null && _queryStringValues.Count > 0)
{
destinationUrl = destinationUrl += "?" + string.Join("&",
_queryStringValues.AllKeys.Select(x => x + "=" + HttpUtility.UrlEncode(_queryStringValues[x])));
}
context.Controller.TempData.Keep();
context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
}
private NameValueCollection ParseQueryString(string queryString)
{
if (!string.IsNullOrEmpty(queryString))
{
return HttpUtility.ParseQueryString(queryString);
}
return null;
}
}
}

View File

@@ -1,42 +1,42 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Web.Mvc.Async;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Ensures that if an action for the Template name is not explicitly defined by a user, that the 'Index' action will execute
/// </summary>
public class RenderActionInvoker : AsyncControllerActionInvoker
{
/// <summary>
/// Ensures that if an action for the Template name is not explicitly defined by a user, that the 'Index' action will execute
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="controllerDescriptor"></param>
/// <param name="actionName"></param>
/// <returns></returns>
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var ad = base.FindAction(controllerContext, controllerDescriptor, actionName);
//now we need to check if it exists, if not we need to return the Index by default
if (ad == null)
{
//check if the controller is an instance of IRenderController and find the index
if (controllerContext.Controller is IRenderController)
{
return controllerDescriptor.FindAction(controllerContext, "Index");
}
}
return ad;
}
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using System.Web.Mvc.Async;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Ensures that if an action for the Template name is not explicitly defined by a user, that the 'Index' action will execute
/// </summary>
public class RenderActionInvoker : AsyncControllerActionInvoker
{
/// <summary>
/// Ensures that if an action for the Template name is not explicitly defined by a user, that the 'Index' action will execute
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="controllerDescriptor"></param>
/// <param name="actionName"></param>
/// <returns></returns>
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var ad = base.FindAction(controllerContext, controllerDescriptor, actionName);
//now we need to check if it exists, if not we need to return the Index by default
if (ad == null)
{
//check if the controller is an instance of IRenderController and find the index
if (controllerContext.Controller is IRenderController)
{
return controllerDescriptor.FindAction(controllerContext, "Index");
}
}
return ad;
}
}
}

View File

@@ -1,49 +1,49 @@
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <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 : UmbracoControllerFactory
{
/// <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 override bool CanHandle(RequestContext request)
{
var dataToken = request.RouteData.DataTokens["area"];
return dataToken == null || string.IsNullOrWhiteSpace(dataToken.ToString());
}
/// <summary>
/// Creates the controller
/// </summary>
/// <param name="requestContext"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
/// <remarks>
/// We always set the correct ActionInvoker on our custom created controller, this is very important for route hijacking!
/// </remarks>
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var instance = base.CreateController(requestContext, controllerName);
var controllerInstance = instance as Controller;
if (controllerInstance != null)
{
//set the action invoker!
controllerInstance.ActionInvoker = new RenderActionInvoker();
}
return instance;
}
}
}
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <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 : UmbracoControllerFactory
{
/// <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 override bool CanHandle(RequestContext request)
{
var dataToken = request.RouteData.DataTokens["area"];
return dataToken == null || string.IsNullOrWhiteSpace(dataToken.ToString());
}
/// <summary>
/// Creates the controller
/// </summary>
/// <param name="requestContext"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
/// <remarks>
/// We always set the correct ActionInvoker on our custom created controller, this is very important for route hijacking!
/// </remarks>
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var instance = base.CreateController(requestContext, controllerName);
var controllerInstance = instance as Controller;
if (controllerInstance != null)
{
//set the action invoker!
controllerInstance.ActionInvoker = new RenderActionInvoker();
}
return instance;
}
}
}

View File

@@ -1,101 +1,101 @@
using System;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the default front-end rendering controller.
/// </summary>
[PreRenderViewActionFilter]
public class RenderMvcController : UmbracoController, IRenderMvcController
{
private PublishedRequest _publishedRequest;
using System;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the default front-end rendering controller.
/// </summary>
[PreRenderViewActionFilter]
public class RenderMvcController : UmbracoController, IRenderMvcController
{
private PublishedRequest _publishedRequest;
// fixme - delete?
public RenderMvcController()
{
ActionInvoker = new RenderActionInvoker();
}
public RenderMvcController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger) : base(globalSettings, umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger)
{
ActionInvoker = new RenderActionInvoker();
}
/// <summary>
/// Gets the Umbraco context.
/// </summary>
public override UmbracoContext UmbracoContext => PublishedRequest.UmbracoContext;
/// <summary>
/// Gets the current content item.
/// </summary>
protected IPublishedContent CurrentPage => PublishedRequest.PublishedContent;
/// <summary>
/// Gets the current published content request.
/// </summary>
protected internal virtual PublishedRequest PublishedRequest
{
get
{
if (_publishedRequest != null)
return _publishedRequest;
if (RouteData.DataTokens.ContainsKey(Core.Constants.Web.PublishedDocumentRequestDataToken) == false)
{
throw new InvalidOperationException("DataTokens must contain an 'umbraco-doc-request' key with a PublishedContentRequest object");
}
_publishedRequest = (PublishedRequest)RouteData.DataTokens[Core.Constants.Web.PublishedDocumentRequestDataToken];
return _publishedRequest;
}
}
/// <summary>
/// Ensures that a physical view file exists on disk.
/// </summary>
/// <param name="template">The view name.</param>
protected bool EnsurePhsyicalViewExists(string template)
{
var result = ViewEngines.Engines.FindView(ControllerContext, template, null);
if (result.View != null) return true;
Logger.Warn<RenderMvcController>("No physical template file was found for template " + template);
return false;
}
/// <summary>
/// Gets an action result based on the template name found in the route values and a model.
/// </summary>
/// <typeparam name="T">The type of the model.</typeparam>
/// <param name="model">The model.</param>
/// <returns>The action result.</returns>
/// <remarks>If the template found in the route values doesn't physically exist, then an empty ContentResult will be returned.</remarks>
protected ActionResult CurrentTemplate<T>(T model)
{
var template = ControllerContext.RouteData.Values["action"].ToString();
if (EnsurePhsyicalViewExists(template) == false)
throw new Exception("No physical template file was found for template " + template);
return View(template, model);
}
/// <summary>
/// The default action to render the front-end view.
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[RenderIndexActionSelector]
public virtual ActionResult Index(ContentModel model)
{
return CurrentTemplate(model);
}
}
}
public RenderMvcController()
{
ActionInvoker = new RenderActionInvoker();
}
public RenderMvcController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger) : base(globalSettings, umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger)
{
ActionInvoker = new RenderActionInvoker();
}
/// <summary>
/// Gets the Umbraco context.
/// </summary>
public override UmbracoContext UmbracoContext => PublishedRequest.UmbracoContext;
/// <summary>
/// Gets the current content item.
/// </summary>
protected IPublishedContent CurrentPage => PublishedRequest.PublishedContent;
/// <summary>
/// Gets the current published content request.
/// </summary>
protected internal virtual PublishedRequest PublishedRequest
{
get
{
if (_publishedRequest != null)
return _publishedRequest;
if (RouteData.DataTokens.ContainsKey(Core.Constants.Web.PublishedDocumentRequestDataToken) == false)
{
throw new InvalidOperationException("DataTokens must contain an 'umbraco-doc-request' key with a PublishedContentRequest object");
}
_publishedRequest = (PublishedRequest)RouteData.DataTokens[Core.Constants.Web.PublishedDocumentRequestDataToken];
return _publishedRequest;
}
}
/// <summary>
/// Ensures that a physical view file exists on disk.
/// </summary>
/// <param name="template">The view name.</param>
protected bool EnsurePhsyicalViewExists(string template)
{
var result = ViewEngines.Engines.FindView(ControllerContext, template, null);
if (result.View != null) return true;
Logger.Warn<RenderMvcController>("No physical template file was found for template " + template);
return false;
}
/// <summary>
/// Gets an action result based on the template name found in the route values and a model.
/// </summary>
/// <typeparam name="T">The type of the model.</typeparam>
/// <param name="model">The model.</param>
/// <returns>The action result.</returns>
/// <remarks>If the template found in the route values doesn't physically exist, then an empty ContentResult will be returned.</remarks>
protected ActionResult CurrentTemplate<T>(T model)
{
var template = ControllerContext.RouteData.Values["action"].ToString();
if (EnsurePhsyicalViewExists(template) == false)
throw new Exception("No physical template file was found for template " + template);
return View(template, model);
}
/// <summary>
/// The default action to render the front-end view.
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[RenderIndexActionSelector]
public virtual ActionResult Index(ContentModel model)
{
return CurrentTemplate(model);
}
}
}

View File

@@ -1,457 +1,457 @@
using System;
using System.Linq;
using System.Web;
using System.Web.Compilation;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Composing;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
using System.Collections.Generic;
using Current = Umbraco.Web.Composing.Current;
using LightInject;
using Umbraco.Web.Features;
namespace Umbraco.Web.Mvc
{
public class RenderRouteHandler : IRouteHandler
{
// Define reserved dictionary keys for controller, action and area specified in route additional values data
private static class ReservedAdditionalKeys
{
internal const string Controller = "c";
internal const string Action = "a";
internal const string Area = "ar";
}
private readonly IControllerFactory _controllerFactory;
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly UmbracoContext _umbracoContext;
// fixme - that one could / should accept a PublishedRouter (engine) to work on the PublishedRequest (published content request)
public RenderRouteHandler(IUmbracoContextAccessor umbracoContextAccessor, IControllerFactory controllerFactory)
{
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
_controllerFactory = controllerFactory ?? throw new ArgumentNullException(nameof(controllerFactory));
}
// fixme - what about that one?
// called by TemplateRenderer - which is created in
// library - could get an engine without problem it's all ugly anyways
// UmbracoComponentRenderer - ?? that one is not so obvious
public RenderRouteHandler(UmbracoContext umbracoContext, IControllerFactory controllerFactory)
{
_umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
_controllerFactory = controllerFactory ?? throw new ArgumentNullException(nameof(controllerFactory));
}
private UmbracoContext UmbracoContext => _umbracoContext ?? _umbracoContextAccessor.UmbracoContext;
private UmbracoFeatures Features => Current.Container.GetInstance<UmbracoFeatures>(); // fixme inject
#region IRouteHandler Members
/// <summary>
/// Assigns the correct controller based on the Umbraco request and returns a standard MvcHandler to prcess the response,
/// this also stores the render model into the data tokens for the current RouteData.
/// </summary>
/// <param name="requestContext"></param>
/// <returns></returns>
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
if (UmbracoContext == null)
{
throw new NullReferenceException("There is no current UmbracoContext, it must be initialized before the RenderRouteHandler executes");
}
var request = UmbracoContext.PublishedRequest;
if (request == null)
{
throw new NullReferenceException("There is no current PublishedContentRequest, it must be initialized before the RenderRouteHandler executes");
}
SetupRouteDataForRequest(
new ContentModel(request.PublishedContent),
requestContext,
request);
return GetHandlerForRoute(requestContext, request);
}
#endregion
/// <summary>
/// Ensures that all of the correct DataTokens are added to the route values which are all required for rendering front-end umbraco views
/// </summary>
/// <param name="contentModel"></param>
/// <param name="requestContext"></param>
/// <param name="frequest"></param>
internal void SetupRouteDataForRequest(ContentModel contentModel, RequestContext requestContext, PublishedRequest frequest)
{
//put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine
requestContext.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, contentModel); //required for the RenderModelBinder and view engine
requestContext.RouteData.DataTokens.Add(Core.Constants.Web.PublishedDocumentRequestDataToken, frequest); //required for RenderMvcController
requestContext.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, UmbracoContext); //required for UmbracoViewPage
}
private void UpdateRouteDataForRequest(ContentModel contentModel, RequestContext requestContext)
{
if (contentModel == null) throw new ArgumentNullException(nameof(contentModel));
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
requestContext.RouteData.DataTokens[Core.Constants.Web.UmbracoDataToken] = contentModel;
// the rest should not change -- it's only the published content that has changed
}
/// <summary>
/// Checks the request and query strings to see if it matches the definition of having a Surface controller
/// posted/get value, if so, then we return a PostedDataProxyInfo object with the correct information.
/// </summary>
/// <param name="requestContext"></param>
/// <returns></returns>
internal static PostedDataProxyInfo GetFormInfo(RequestContext requestContext)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
//if it is a POST/GET then a value must be in the request
if (requestContext.HttpContext.Request.QueryString["ufprt"].IsNullOrWhiteSpace()
&& requestContext.HttpContext.Request.Form["ufprt"].IsNullOrWhiteSpace())
{
return null;
}
string encodedVal;
switch (requestContext.HttpContext.Request.RequestType)
{
case "POST":
//get the value from the request.
//this field will contain an encrypted version of the surface route vals.
encodedVal = requestContext.HttpContext.Request.Form["ufprt"];
break;
case "GET":
//this field will contain an encrypted version of the surface route vals.
encodedVal = requestContext.HttpContext.Request.QueryString["ufprt"];
break;
default:
return null;
}
string decryptedString;
try
{
decryptedString = encodedVal.DecryptWithMachineKey();
}
catch (FormatException)
{
Current.Logger.Warn<RenderRouteHandler>("A value was detected in the ufprt parameter but Umbraco could not decrypt the string");
return null;
}
var parsedQueryString = HttpUtility.ParseQueryString(decryptedString);
var decodedParts = new Dictionary<string, string>();
foreach (var key in parsedQueryString.AllKeys)
{
decodedParts[key] = parsedQueryString[key];
}
//validate all required keys exist
//the controller
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Controller))
return null;
//the action
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Action))
return null;
//the area
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Area))
return null;
foreach (var item in decodedParts.Where(x => new[] {
ReservedAdditionalKeys.Controller,
ReservedAdditionalKeys.Action,
ReservedAdditionalKeys.Area }.Contains(x.Key) == false))
{
// Populate route with additional values which aren't reserved values so they eventually to action parameters
requestContext.RouteData.Values[item.Key] = item.Value;
}
//return the proxy info without the surface id... could be a local controller.
return new PostedDataProxyInfo
{
ControllerName = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Controller).Value),
ActionName = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Action).Value),
Area = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Area).Value),
};
}
/// <summary>
/// Handles a posted form to an Umbraco Url and ensures the correct controller is routed to and that
/// the right DataTokens are set.
/// </summary>
/// <param name="requestContext"></param>
/// <param name="postedInfo"></param>
internal static IHttpHandler HandlePostedValues(RequestContext requestContext, PostedDataProxyInfo postedInfo)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (postedInfo == null) throw new ArgumentNullException(nameof(postedInfo));
//set the standard route values/tokens
requestContext.RouteData.Values["controller"] = postedInfo.ControllerName;
requestContext.RouteData.Values["action"] = postedInfo.ActionName;
IHttpHandler handler;
//get the route from the defined routes
using (RouteTable.Routes.GetReadLock())
{
Route surfaceRoute;
if (postedInfo.Area.IsNullOrWhiteSpace())
{
//find the controller in the route table without an area
var surfaceRoutes = RouteTable.Routes.OfType<Route>()
.Where(x => x.Defaults != null &&
x.Defaults.ContainsKey("controller") &&
x.Defaults["controller"].ToString().InvariantEquals(postedInfo.ControllerName) &&
x.DataTokens.ContainsKey("area") == false).ToList();
// If more than one route is found, find one with a matching action
if (surfaceRoutes.Count > 1)
{
surfaceRoute = surfaceRoutes.FirstOrDefault(x =>
x.Defaults["action"] != null &&
x.Defaults["action"].ToString().InvariantEquals(postedInfo.ActionName));
}
else
{
surfaceRoute = surfaceRoutes.SingleOrDefault();
}
}
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().InvariantEquals(postedInfo.ControllerName) &&
x.DataTokens.ContainsKey("area") &&
x.DataTokens["area"].ToString().InvariantEquals(postedInfo.Area));
}
if (surfaceRoute == null)
throw new InvalidOperationException("Could not find a Surface controller route in the RouteTable for controller name " + postedInfo.ControllerName);
//set the area if one is there.
if (surfaceRoute.DataTokens.ContainsKey("area"))
{
requestContext.RouteData.DataTokens["area"] = surfaceRoute.DataTokens["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);
}
return handler;
}
/// <summary>
/// Returns a RouteDefinition object based on the current renderModel
/// </summary>
/// <param name="requestContext"></param>
/// <param name="request"></param>
/// <returns></returns>
internal virtual RouteDefinition GetUmbracoRouteDefinition(RequestContext requestContext, PublishedRequest request)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (request == null) throw new ArgumentNullException(nameof(request));
var defaultControllerType = Current.DefaultRenderMvcControllerType;
var defaultControllerName = ControllerExtensions.GetControllerName(defaultControllerType);
//creates the default route definition which maps to the 'UmbracoController' controller
var def = new RouteDefinition
{
ControllerName = defaultControllerName,
ControllerType = defaultControllerType,
PublishedRequest = request,
ActionName = ((Route)requestContext.RouteData.Route).Defaults["action"].ToString(),
HasHijackedRoute = false
};
//check that a template is defined), if it doesn't and there is a hijacked route it will just route
// to the index Action
if (request.HasTemplate)
{
//the template Alias should always be already saved with a safe name.
//if there are hyphens in the name and there is a hijacked route, then the Action will need to be attributed
// with the action name attribute.
var templateName = request.TemplateAlias.Split('.')[0].ToSafeAlias();
def.ActionName = templateName;
}
//check if there's a custom controller assigned, base on the document type alias.
var controllerType = _controllerFactory.GetControllerTypeInternal(requestContext, request.PublishedContent.ContentType.Alias);
//check if that controller exists
if (controllerType != null)
{
//ensure the controller is of type IRenderMvcController and ControllerBase
if (TypeHelper.IsTypeAssignableFrom<IRenderController>(controllerType)
&& TypeHelper.IsTypeAssignableFrom<ControllerBase>(controllerType))
{
//set the controller and name to the custom one
def.ControllerType = controllerType;
def.ControllerName = ControllerExtensions.GetControllerName(controllerType);
if (def.ControllerName != defaultControllerName)
{
def.HasHijackedRoute = true;
}
}
else
{
Current.Logger.Warn<RenderRouteHandler>(() =>
$"The current Document Type {request.PublishedContent.ContentType.Alias} matches a locally declared controller of type {controllerType.FullName}. Custom Controllers for Umbraco routing must implement '{typeof(IRenderController).FullName}' and inherit from '{typeof(ControllerBase).FullName}'.");
//we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults
// that have already been set above.
}
}
//store the route definition
requestContext.RouteData.DataTokens[Core.Constants.Web.UmbracoRouteDefinitionDataToken] = def;
return def;
}
internal IHttpHandler GetHandlerOnMissingTemplate(PublishedRequest request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
// missing template, so we're in a 404 here
// so the content, if any, is a custom 404 page of some sort
if (request.HasPublishedContent == false)
// means the builder could not find a proper document to handle 404
return new PublishedContentNotFoundHandler();
if (request.HasTemplate == false)
// means the engine could find a proper document, but the document has no template
// at that point there isn't much we can do and there is no point returning
// to Mvc since Mvc can't do much
return new PublishedContentNotFoundHandler("In addition, no template exists to render the custom 404.");
// so we have a template, so we should have a rendering engine
if (request.RenderingEngine == RenderingEngine.WebForms) // back to webforms ?
return GetWebFormsHandler();
if (request.RenderingEngine != RenderingEngine.Mvc) // else ?
return new PublishedContentNotFoundHandler("In addition, no rendering engine exists to render the custom 404.");
return null;
}
/// <summary>
/// this will determine the controller and set the values in the route data
/// </summary>
/// <param name="requestContext"></param>
/// <param name="request"></param>
internal IHttpHandler GetHandlerForRoute(RequestContext requestContext, PublishedRequest request)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (request == null) throw new ArgumentNullException(nameof(request));
var routeDef = GetUmbracoRouteDefinition(requestContext, request);
//Need to check for a special case if there is form data being posted back to an Umbraco URL
var postedInfo = GetFormInfo(requestContext);
if (postedInfo != null)
{
return HandlePostedValues(requestContext, postedInfo);
}
//Now we can check if we are supposed to render WebForms when the route has not been hijacked
if (request.RenderingEngine == RenderingEngine.WebForms
&& request.HasTemplate
&& routeDef.HasHijackedRoute == false)
{
return GetWebFormsHandler();
}
//Here we need to check if there is no hijacked route and no template assigned,
//if this is the case we want to return a blank page, but we'll leave that up to the NoTemplateHandler.
//We also check if templates have been disabled since if they are then we're allowed to render even though there's no template,
//for example for json rendering in headless.
if ((request.HasTemplate == false && Features.Disabled.DisableTemplates == false)
&& routeDef.HasHijackedRoute == false)
{
// fixme - better find a way to inject that engine? or at least Current.Engine of some sort!
var engine = Core.Composing.Current.Container.GetInstance<PublishedRouter>();
request.UpdateOnMissingTemplate(); // request will go 404
// HandleHttpResponseStatus returns a value indicating that the request should
// not be processed any further, eg because it has been redirect. then, exit.
if (UmbracoModule.HandleHttpResponseStatus(requestContext.HttpContext, request, Current.Logger))
return null;
var handler = GetHandlerOnMissingTemplate(request);
// if it's not null it can be either the PublishedContentNotFoundHandler (no document was
// found to handle 404, or document with no template was found) or the WebForms handler
// (a document was found and its template is WebForms)
// if it's null it means that a document was found and its template is Mvc
// if we have a handler, return now
if (handler != null)
return handler;
// else we are running Mvc
// update the route data - because the PublishedContent has changed
UpdateRouteDataForRequest(
new ContentModel(request.PublishedContent),
requestContext);
// update the route definition
routeDef = GetUmbracoRouteDefinition(requestContext, request);
}
//no post values, just route to the controller/action requried (local)
requestContext.RouteData.Values["controller"] = routeDef.ControllerName;
if (string.IsNullOrWhiteSpace(routeDef.ActionName) == false)
requestContext.RouteData.Values["action"] = routeDef.ActionName;
// Set the session state requirements
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext, routeDef.ControllerName));
// reset the friendly path so in the controllers and anything occuring after this point in time,
//the URL is reset back to the original request.
requestContext.HttpContext.RewritePath(UmbracoContext.OriginalRequestUrl.PathAndQuery);
return new UmbracoMvcHandler(requestContext);
}
/// <summary>
/// Returns the handler for webforms requests
/// </summary>
/// <returns></returns>
internal static IHttpHandler GetWebFormsHandler()
{
return (global::umbraco.UmbracoDefault)BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault));
}
private SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext, string controllerName)
{
return _controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
}
}
}
using System;
using System.Linq;
using System.Web;
using System.Web.Compilation;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Composing;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
using System.Collections.Generic;
using Current = Umbraco.Web.Composing.Current;
using LightInject;
using Umbraco.Web.Features;
namespace Umbraco.Web.Mvc
{
public class RenderRouteHandler : IRouteHandler
{
// Define reserved dictionary keys for controller, action and area specified in route additional values data
private static class ReservedAdditionalKeys
{
internal const string Controller = "c";
internal const string Action = "a";
internal const string Area = "ar";
}
private readonly IControllerFactory _controllerFactory;
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly UmbracoContext _umbracoContext;
// fixme - that one could / should accept a PublishedRouter (engine) to work on the PublishedRequest (published content request)
public RenderRouteHandler(IUmbracoContextAccessor umbracoContextAccessor, IControllerFactory controllerFactory)
{
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
_controllerFactory = controllerFactory ?? throw new ArgumentNullException(nameof(controllerFactory));
}
// fixme - what about that one?
// called by TemplateRenderer - which is created in
// library - could get an engine without problem it's all ugly anyways
// UmbracoComponentRenderer - ?? that one is not so obvious
public RenderRouteHandler(UmbracoContext umbracoContext, IControllerFactory controllerFactory)
{
_umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
_controllerFactory = controllerFactory ?? throw new ArgumentNullException(nameof(controllerFactory));
}
private UmbracoContext UmbracoContext => _umbracoContext ?? _umbracoContextAccessor.UmbracoContext;
private UmbracoFeatures Features => Current.Container.GetInstance<UmbracoFeatures>(); // fixme inject
#region IRouteHandler Members
/// <summary>
/// Assigns the correct controller based on the Umbraco request and returns a standard MvcHandler to prcess the response,
/// this also stores the render model into the data tokens for the current RouteData.
/// </summary>
/// <param name="requestContext"></param>
/// <returns></returns>
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
if (UmbracoContext == null)
{
throw new NullReferenceException("There is no current UmbracoContext, it must be initialized before the RenderRouteHandler executes");
}
var request = UmbracoContext.PublishedRequest;
if (request == null)
{
throw new NullReferenceException("There is no current PublishedContentRequest, it must be initialized before the RenderRouteHandler executes");
}
SetupRouteDataForRequest(
new ContentModel(request.PublishedContent),
requestContext,
request);
return GetHandlerForRoute(requestContext, request);
}
#endregion
/// <summary>
/// Ensures that all of the correct DataTokens are added to the route values which are all required for rendering front-end umbraco views
/// </summary>
/// <param name="contentModel"></param>
/// <param name="requestContext"></param>
/// <param name="frequest"></param>
internal void SetupRouteDataForRequest(ContentModel contentModel, RequestContext requestContext, PublishedRequest frequest)
{
//put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine
requestContext.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, contentModel); //required for the RenderModelBinder and view engine
requestContext.RouteData.DataTokens.Add(Core.Constants.Web.PublishedDocumentRequestDataToken, frequest); //required for RenderMvcController
requestContext.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, UmbracoContext); //required for UmbracoViewPage
}
private void UpdateRouteDataForRequest(ContentModel contentModel, RequestContext requestContext)
{
if (contentModel == null) throw new ArgumentNullException(nameof(contentModel));
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
requestContext.RouteData.DataTokens[Core.Constants.Web.UmbracoDataToken] = contentModel;
// the rest should not change -- it's only the published content that has changed
}
/// <summary>
/// Checks the request and query strings to see if it matches the definition of having a Surface controller
/// posted/get value, if so, then we return a PostedDataProxyInfo object with the correct information.
/// </summary>
/// <param name="requestContext"></param>
/// <returns></returns>
internal static PostedDataProxyInfo GetFormInfo(RequestContext requestContext)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
//if it is a POST/GET then a value must be in the request
if (requestContext.HttpContext.Request.QueryString["ufprt"].IsNullOrWhiteSpace()
&& requestContext.HttpContext.Request.Form["ufprt"].IsNullOrWhiteSpace())
{
return null;
}
string encodedVal;
switch (requestContext.HttpContext.Request.RequestType)
{
case "POST":
//get the value from the request.
//this field will contain an encrypted version of the surface route vals.
encodedVal = requestContext.HttpContext.Request.Form["ufprt"];
break;
case "GET":
//this field will contain an encrypted version of the surface route vals.
encodedVal = requestContext.HttpContext.Request.QueryString["ufprt"];
break;
default:
return null;
}
string decryptedString;
try
{
decryptedString = encodedVal.DecryptWithMachineKey();
}
catch (FormatException)
{
Current.Logger.Warn<RenderRouteHandler>("A value was detected in the ufprt parameter but Umbraco could not decrypt the string");
return null;
}
var parsedQueryString = HttpUtility.ParseQueryString(decryptedString);
var decodedParts = new Dictionary<string, string>();
foreach (var key in parsedQueryString.AllKeys)
{
decodedParts[key] = parsedQueryString[key];
}
//validate all required keys exist
//the controller
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Controller))
return null;
//the action
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Action))
return null;
//the area
if (decodedParts.All(x => x.Key != ReservedAdditionalKeys.Area))
return null;
foreach (var item in decodedParts.Where(x => new[] {
ReservedAdditionalKeys.Controller,
ReservedAdditionalKeys.Action,
ReservedAdditionalKeys.Area }.Contains(x.Key) == false))
{
// Populate route with additional values which aren't reserved values so they eventually to action parameters
requestContext.RouteData.Values[item.Key] = item.Value;
}
//return the proxy info without the surface id... could be a local controller.
return new PostedDataProxyInfo
{
ControllerName = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Controller).Value),
ActionName = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Action).Value),
Area = HttpUtility.UrlDecode(decodedParts.Single(x => x.Key == ReservedAdditionalKeys.Area).Value),
};
}
/// <summary>
/// Handles a posted form to an Umbraco Url and ensures the correct controller is routed to and that
/// the right DataTokens are set.
/// </summary>
/// <param name="requestContext"></param>
/// <param name="postedInfo"></param>
internal static IHttpHandler HandlePostedValues(RequestContext requestContext, PostedDataProxyInfo postedInfo)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (postedInfo == null) throw new ArgumentNullException(nameof(postedInfo));
//set the standard route values/tokens
requestContext.RouteData.Values["controller"] = postedInfo.ControllerName;
requestContext.RouteData.Values["action"] = postedInfo.ActionName;
IHttpHandler handler;
//get the route from the defined routes
using (RouteTable.Routes.GetReadLock())
{
Route surfaceRoute;
if (postedInfo.Area.IsNullOrWhiteSpace())
{
//find the controller in the route table without an area
var surfaceRoutes = RouteTable.Routes.OfType<Route>()
.Where(x => x.Defaults != null &&
x.Defaults.ContainsKey("controller") &&
x.Defaults["controller"].ToString().InvariantEquals(postedInfo.ControllerName) &&
x.DataTokens.ContainsKey("area") == false).ToList();
// If more than one route is found, find one with a matching action
if (surfaceRoutes.Count > 1)
{
surfaceRoute = surfaceRoutes.FirstOrDefault(x =>
x.Defaults["action"] != null &&
x.Defaults["action"].ToString().InvariantEquals(postedInfo.ActionName));
}
else
{
surfaceRoute = surfaceRoutes.SingleOrDefault();
}
}
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().InvariantEquals(postedInfo.ControllerName) &&
x.DataTokens.ContainsKey("area") &&
x.DataTokens["area"].ToString().InvariantEquals(postedInfo.Area));
}
if (surfaceRoute == null)
throw new InvalidOperationException("Could not find a Surface controller route in the RouteTable for controller name " + postedInfo.ControllerName);
//set the area if one is there.
if (surfaceRoute.DataTokens.ContainsKey("area"))
{
requestContext.RouteData.DataTokens["area"] = surfaceRoute.DataTokens["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);
}
return handler;
}
/// <summary>
/// Returns a RouteDefinition object based on the current renderModel
/// </summary>
/// <param name="requestContext"></param>
/// <param name="request"></param>
/// <returns></returns>
internal virtual RouteDefinition GetUmbracoRouteDefinition(RequestContext requestContext, PublishedRequest request)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (request == null) throw new ArgumentNullException(nameof(request));
var defaultControllerType = Current.DefaultRenderMvcControllerType;
var defaultControllerName = ControllerExtensions.GetControllerName(defaultControllerType);
//creates the default route definition which maps to the 'UmbracoController' controller
var def = new RouteDefinition
{
ControllerName = defaultControllerName,
ControllerType = defaultControllerType,
PublishedRequest = request,
ActionName = ((Route)requestContext.RouteData.Route).Defaults["action"].ToString(),
HasHijackedRoute = false
};
//check that a template is defined), if it doesn't and there is a hijacked route it will just route
// to the index Action
if (request.HasTemplate)
{
//the template Alias should always be already saved with a safe name.
//if there are hyphens in the name and there is a hijacked route, then the Action will need to be attributed
// with the action name attribute.
var templateName = request.TemplateAlias.Split('.')[0].ToSafeAlias();
def.ActionName = templateName;
}
//check if there's a custom controller assigned, base on the document type alias.
var controllerType = _controllerFactory.GetControllerTypeInternal(requestContext, request.PublishedContent.ContentType.Alias);
//check if that controller exists
if (controllerType != null)
{
//ensure the controller is of type IRenderMvcController and ControllerBase
if (TypeHelper.IsTypeAssignableFrom<IRenderController>(controllerType)
&& TypeHelper.IsTypeAssignableFrom<ControllerBase>(controllerType))
{
//set the controller and name to the custom one
def.ControllerType = controllerType;
def.ControllerName = ControllerExtensions.GetControllerName(controllerType);
if (def.ControllerName != defaultControllerName)
{
def.HasHijackedRoute = true;
}
}
else
{
Current.Logger.Warn<RenderRouteHandler>(() =>
$"The current Document Type {request.PublishedContent.ContentType.Alias} matches a locally declared controller of type {controllerType.FullName}. Custom Controllers for Umbraco routing must implement '{typeof(IRenderController).FullName}' and inherit from '{typeof(ControllerBase).FullName}'.");
//we cannot route to this custom controller since it is not of the correct type so we'll continue with the defaults
// that have already been set above.
}
}
//store the route definition
requestContext.RouteData.DataTokens[Core.Constants.Web.UmbracoRouteDefinitionDataToken] = def;
return def;
}
internal IHttpHandler GetHandlerOnMissingTemplate(PublishedRequest request)
{
if (request == null) throw new ArgumentNullException(nameof(request));
// missing template, so we're in a 404 here
// so the content, if any, is a custom 404 page of some sort
if (request.HasPublishedContent == false)
// means the builder could not find a proper document to handle 404
return new PublishedContentNotFoundHandler();
if (request.HasTemplate == false)
// means the engine could find a proper document, but the document has no template
// at that point there isn't much we can do and there is no point returning
// to Mvc since Mvc can't do much
return new PublishedContentNotFoundHandler("In addition, no template exists to render the custom 404.");
// so we have a template, so we should have a rendering engine
if (request.RenderingEngine == RenderingEngine.WebForms) // back to webforms ?
return GetWebFormsHandler();
if (request.RenderingEngine != RenderingEngine.Mvc) // else ?
return new PublishedContentNotFoundHandler("In addition, no rendering engine exists to render the custom 404.");
return null;
}
/// <summary>
/// this will determine the controller and set the values in the route data
/// </summary>
/// <param name="requestContext"></param>
/// <param name="request"></param>
internal IHttpHandler GetHandlerForRoute(RequestContext requestContext, PublishedRequest request)
{
if (requestContext == null) throw new ArgumentNullException(nameof(requestContext));
if (request == null) throw new ArgumentNullException(nameof(request));
var routeDef = GetUmbracoRouteDefinition(requestContext, request);
//Need to check for a special case if there is form data being posted back to an Umbraco URL
var postedInfo = GetFormInfo(requestContext);
if (postedInfo != null)
{
return HandlePostedValues(requestContext, postedInfo);
}
//Now we can check if we are supposed to render WebForms when the route has not been hijacked
if (request.RenderingEngine == RenderingEngine.WebForms
&& request.HasTemplate
&& routeDef.HasHijackedRoute == false)
{
return GetWebFormsHandler();
}
//Here we need to check if there is no hijacked route and no template assigned,
//if this is the case we want to return a blank page, but we'll leave that up to the NoTemplateHandler.
//We also check if templates have been disabled since if they are then we're allowed to render even though there's no template,
//for example for json rendering in headless.
if ((request.HasTemplate == false && Features.Disabled.DisableTemplates == false)
&& routeDef.HasHijackedRoute == false)
{
// fixme - better find a way to inject that engine? or at least Current.Engine of some sort!
var engine = Core.Composing.Current.Container.GetInstance<PublishedRouter>();
request.UpdateOnMissingTemplate(); // request will go 404
// HandleHttpResponseStatus returns a value indicating that the request should
// not be processed any further, eg because it has been redirect. then, exit.
if (UmbracoModule.HandleHttpResponseStatus(requestContext.HttpContext, request, Current.Logger))
return null;
var handler = GetHandlerOnMissingTemplate(request);
// if it's not null it can be either the PublishedContentNotFoundHandler (no document was
// found to handle 404, or document with no template was found) or the WebForms handler
// (a document was found and its template is WebForms)
// if it's null it means that a document was found and its template is Mvc
// if we have a handler, return now
if (handler != null)
return handler;
// else we are running Mvc
// update the route data - because the PublishedContent has changed
UpdateRouteDataForRequest(
new ContentModel(request.PublishedContent),
requestContext);
// update the route definition
routeDef = GetUmbracoRouteDefinition(requestContext, request);
}
//no post values, just route to the controller/action requried (local)
requestContext.RouteData.Values["controller"] = routeDef.ControllerName;
if (string.IsNullOrWhiteSpace(routeDef.ActionName) == false)
requestContext.RouteData.Values["action"] = routeDef.ActionName;
// Set the session state requirements
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext, routeDef.ControllerName));
// reset the friendly path so in the controllers and anything occuring after this point in time,
//the URL is reset back to the original request.
requestContext.HttpContext.RewritePath(UmbracoContext.OriginalRequestUrl.PathAndQuery);
return new UmbracoMvcHandler(requestContext);
}
/// <summary>
/// Returns the handler for webforms requests
/// </summary>
/// <returns></returns>
internal static IHttpHandler GetWebFormsHandler()
{
return (global::umbraco.UmbracoDefault)BuildManager.CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault));
}
private SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext, string controllerName)
{
return _controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
}
}
}

View File

@@ -1,100 +1,100 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Core.IO;
using Umbraco.Web.Models;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A view engine to look into the template location specified in the config for the front-end/Rendering part of the cms,
/// this includes paths to render partial macros and media item templates.
/// </summary>
public class RenderViewEngine : ReflectedFixedRazorViewEngine
{
private readonly IEnumerable<string> _supplementedViewLocations = new[] { "/{0}.cshtml" };
//NOTE: we will make the main view location the last to be searched since if it is the first to be searched and there is both a view and a partial
// view in both locations and the main view is rendering a partial view with the same name, we will get a stack overflow exception.
// http://issues.umbraco.org/issue/U4-1287, http://issues.umbraco.org/issue/U4-1215
private readonly IEnumerable<string> _supplementedPartialViewLocations = new[] { "/Partials/{0}.cshtml", "/MacroPartials/{0}.cshtml", "/{0}.cshtml" };
/// <summary>
/// Constructor
/// </summary>
public RenderViewEngine()
{
const string templateFolder = Constants.ViewLocation;
// the Render view engine doesn't support Area's so make those blank
ViewLocationFormats = _supplementedViewLocations.Select(x => templateFolder + x).ToArray();
PartialViewLocationFormats = _supplementedPartialViewLocations.Select(x => templateFolder + x).ToArray();
AreaPartialViewLocationFormats = Array.Empty<string>();
AreaViewLocationFormats = Array.Empty<string>();
EnsureFoldersAndFiles();
}
/// <summary>
/// Ensures that the correct web.config for razor exists in the /Views folder, the partials folder exist and the ViewStartPage exists.
/// </summary>
private static void EnsureFoldersAndFiles()
{
var viewFolder = IOHelper.MapPath(Constants.ViewLocation);
// ensure the web.config file is in the ~/Views folder
Directory.CreateDirectory(viewFolder);
var webConfigPath = Path.Combine(viewFolder, "web.config");
if (File.Exists(webConfigPath) == false)
{
using (var writer = File.CreateText(webConfigPath))
{
writer.Write(Strings.WebConfigTemplate);
}
}
//auto create the partials folder
var partialsFolder = Path.Combine(viewFolder, "Partials");
Directory.CreateDirectory(partialsFolder);
// We could create a _ViewStart page if it isn't there as well, but we may not allow editing of this page in the back office.
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
return ShouldFindView(controllerContext, false)
? base.FindView(controllerContext, viewName, masterName, useCache)
: new ViewEngineResult(new string[] { });
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
return ShouldFindView(controllerContext, true)
? base.FindPartialView(controllerContext, partialViewName, useCache)
: new ViewEngineResult(new string[] { });
}
/// <summary>
/// Determines if the view should be found, this is used for view lookup performance and also to ensure
/// less overlap with other user's view engines. This will return true if the Umbraco back office is rendering
/// and its a partial view or if the umbraco front-end is rendering but nothing else.
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="isPartial"></param>
/// <returns></returns>
private static bool ShouldFindView(ControllerContext controllerContext, bool isPartial)
{
var umbracoToken = controllerContext.GetDataTokenInViewContextHierarchy(Core.Constants.Web.UmbracoDataToken);
// first check if we're rendering a partial view for the back office, or surface controller, etc...
// anything that is not IUmbracoRenderModel as this should only pertain to Umbraco views.
if (isPartial && !(umbracoToken is ContentModel))
return true;
// only find views if we're rendering the umbraco front end
return umbracoToken is ContentModel;
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Core.IO;
using Umbraco.Web.Models;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A view engine to look into the template location specified in the config for the front-end/Rendering part of the cms,
/// this includes paths to render partial macros and media item templates.
/// </summary>
public class RenderViewEngine : ReflectedFixedRazorViewEngine
{
private readonly IEnumerable<string> _supplementedViewLocations = new[] { "/{0}.cshtml" };
//NOTE: we will make the main view location the last to be searched since if it is the first to be searched and there is both a view and a partial
// view in both locations and the main view is rendering a partial view with the same name, we will get a stack overflow exception.
// http://issues.umbraco.org/issue/U4-1287, http://issues.umbraco.org/issue/U4-1215
private readonly IEnumerable<string> _supplementedPartialViewLocations = new[] { "/Partials/{0}.cshtml", "/MacroPartials/{0}.cshtml", "/{0}.cshtml" };
/// <summary>
/// Constructor
/// </summary>
public RenderViewEngine()
{
const string templateFolder = Constants.ViewLocation;
// the Render view engine doesn't support Area's so make those blank
ViewLocationFormats = _supplementedViewLocations.Select(x => templateFolder + x).ToArray();
PartialViewLocationFormats = _supplementedPartialViewLocations.Select(x => templateFolder + x).ToArray();
AreaPartialViewLocationFormats = Array.Empty<string>();
AreaViewLocationFormats = Array.Empty<string>();
EnsureFoldersAndFiles();
}
/// <summary>
/// Ensures that the correct web.config for razor exists in the /Views folder, the partials folder exist and the ViewStartPage exists.
/// </summary>
private static void EnsureFoldersAndFiles()
{
var viewFolder = IOHelper.MapPath(Constants.ViewLocation);
// ensure the web.config file is in the ~/Views folder
Directory.CreateDirectory(viewFolder);
var webConfigPath = Path.Combine(viewFolder, "web.config");
if (File.Exists(webConfigPath) == false)
{
using (var writer = File.CreateText(webConfigPath))
{
writer.Write(Strings.WebConfigTemplate);
}
}
//auto create the partials folder
var partialsFolder = Path.Combine(viewFolder, "Partials");
Directory.CreateDirectory(partialsFolder);
// We could create a _ViewStart page if it isn't there as well, but we may not allow editing of this page in the back office.
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
return ShouldFindView(controllerContext, false)
? base.FindView(controllerContext, viewName, masterName, useCache)
: new ViewEngineResult(new string[] { });
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
return ShouldFindView(controllerContext, true)
? base.FindPartialView(controllerContext, partialViewName, useCache)
: new ViewEngineResult(new string[] { });
}
/// <summary>
/// Determines if the view should be found, this is used for view lookup performance and also to ensure
/// less overlap with other user's view engines. This will return true if the Umbraco back office is rendering
/// and its a partial view or if the umbraco front-end is rendering but nothing else.
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="isPartial"></param>
/// <returns></returns>
private static bool ShouldFindView(ControllerContext controllerContext, bool isPartial)
{
var umbracoToken = controllerContext.GetDataTokenInViewContextHierarchy(Core.Constants.Web.UmbracoDataToken);
// first check if we're rendering a partial view for the back office, or surface controller, etc...
// anything that is not IUmbracoRenderModel as this should only pertain to Umbraco views.
if (isPartial && !(umbracoToken is ContentModel))
return true;
// only find views if we're rendering the umbraco front end
return umbracoToken is ContentModel;
}
}
}

View File

@@ -1,31 +1,31 @@
using System;
using System.Web.Mvc;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the data required to route to a specific controller/action during an Umbraco request
/// </summary>
public class RouteDefinition
{
public string ControllerName { get; set; }
public string ActionName { get; set; }
/// <summary>
/// The Controller type found for routing to
/// </summary>
public Type ControllerType { get; set; }
/// <summary>
/// The current RenderModel found for the request
/// </summary>
public PublishedRequest PublishedRequest { get; set; }
/// <summary>
/// Gets/sets whether the current request has a hijacked route/user controller routed for it
/// </summary>
public bool HasHijackedRoute { get; set; }
}
}
using System;
using System.Web.Mvc;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the data required to route to a specific controller/action during an Umbraco request
/// </summary>
public class RouteDefinition
{
public string ControllerName { get; set; }
public string ActionName { get; set; }
/// <summary>
/// The Controller type found for routing to
/// </summary>
public Type ControllerType { get; set; }
/// <summary>
/// The current RenderModel found for the request
/// </summary>
public PublishedRequest PublishedRequest { get; set; }
/// <summary>
/// Gets/sets whether the current request has a hijacked route/user controller routed for it
/// </summary>
public bool HasHijackedRoute { get; set; }
}
}

View File

@@ -1,41 +1,41 @@
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
internal static class RouteValueDictionaryExtensions
{
/// <summary>
/// Converts a route value dictionary to a form collection
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public static FormCollection ToFormCollection(this RouteValueDictionary items)
{
var formCollection = new FormCollection();
foreach (var i in items)
{
formCollection.Add(i.Key, i.Value != null ? i.Value.ToString() : null);
}
return formCollection;
}
/// <summary>
/// Returns the value of a mandatory item in the route items
/// </summary>
/// <param name="items"></param>
/// <param name="key"> </param>
/// <returns></returns>
public static object GetRequiredObject(this RouteValueDictionary items, string key)
{
if (key == null) throw new ArgumentNullException("key");
if (items.Keys.Contains(key) == false)
throw new ArgumentNullException("The " + key + " parameter was not found but is required");
return items[key];
}
}
}
using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
internal static class RouteValueDictionaryExtensions
{
/// <summary>
/// Converts a route value dictionary to a form collection
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public static FormCollection ToFormCollection(this RouteValueDictionary items)
{
var formCollection = new FormCollection();
foreach (var i in items)
{
formCollection.Add(i.Key, i.Value != null ? i.Value.ToString() : null);
}
return formCollection;
}
/// <summary>
/// Returns the value of a mandatory item in the route items
/// </summary>
/// <param name="items"></param>
/// <param name="key"> </param>
/// <returns></returns>
public static object GetRequiredObject(this RouteValueDictionary items, string key)
{
if (key == null) throw new ArgumentNullException("key");
if (items.Keys.Contains(key) == false)
throw new ArgumentNullException("The " + key + " parameter was not found but is required");
return items[key];
}
}
}

View File

@@ -1,78 +1,78 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Umbraco.Web.Mvc {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Strings {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Strings() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Web.Mvc.Strings", typeof(Strings).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot;?&gt;
///&lt;configuration&gt;
///
/// &lt;configSections&gt;
/// &lt;sectionGroup name=&quot;system.web.webPages.razor&quot; type=&quot;System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35&quot;&gt;
/// &lt;section name=&quot;host&quot; type=&quot;System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35&quot; requirePermission=&quot;false&quot; /&gt;
/// &lt;section name=&quot;page [rest of string was truncated]&quot;;.
/// </summary>
internal static string WebConfigTemplate {
get {
return ResourceManager.GetString("WebConfigTemplate", resourceCulture);
}
}
}
}
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Umbraco.Web.Mvc {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Strings {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Strings() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Web.Mvc.Strings", typeof(Strings).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot;?&gt;
///&lt;configuration&gt;
///
/// &lt;configSections&gt;
/// &lt;sectionGroup name=&quot;system.web.webPages.razor&quot; type=&quot;System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35&quot;&gt;
/// &lt;section name=&quot;host&quot; type=&quot;System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35&quot; requirePermission=&quot;false&quot; /&gt;
/// &lt;section name=&quot;page [rest of string was truncated]&quot;;.
/// </summary>
internal static string WebConfigTemplate {
get {
return ResourceManager.GetString("WebConfigTemplate", resourceCulture);
}
}
}
}

View File

@@ -1,124 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="WebConfigTemplate" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\umbraco.web.ui\views\web.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="WebConfigTemplate" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\..\umbraco.web.ui\views\web.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
</root>

View File

@@ -1,188 +1,188 @@
using System;
using Umbraco.Core;
using System.Collections.Specialized;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for front-end add-in controllers.
/// </summary>
[MergeModelStateToChildAction]
[MergeParentContextViewData]
public abstract class SurfaceController : PluginController
{
// fixme - delete?
using System;
using Umbraco.Core;
using System.Collections.Specialized;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for front-end add-in controllers.
/// </summary>
[MergeModelStateToChildAction]
[MergeParentContextViewData]
public abstract class SurfaceController : PluginController
{
// fixme - delete?
protected SurfaceController()
{
}
protected SurfaceController(UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger)
: base(umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger)
{
}
/// <summary>
/// Redirects to the Umbraco page with the given id
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId)
{
return new RedirectToUmbracoPageResult(pageId, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(pageId, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, string queryString)
{
return new RedirectToUmbracoPageResult(pageId, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id
/// </summary>
/// <param name="publishedContent"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent)
{
return new RedirectToUmbracoPageResult(publishedContent, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(publishedContent, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, string queryString)
{
return new RedirectToUmbracoPageResult(publishedContent, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page
/// </summary>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage()
{
return new RedirectToUmbracoPageResult(CurrentPage, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page and passes provided querystring
/// </summary>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(CurrentPage, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page and passes provided querystring
/// </summary>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(string queryString)
{
return new RedirectToUmbracoPageResult(CurrentPage, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco URL
/// </summary>
/// <returns></returns>
/// <remarks>
/// this is useful if you need to redirect
/// to the current page but the current page is actually a rewritten URL normally done with something like
/// Server.Transfer.
/// </remarks>
protected RedirectToUmbracoUrlResult RedirectToCurrentUmbracoUrl()
{
return new RedirectToUmbracoUrlResult(UmbracoContext);
}
/// <summary>
/// Returns the currently rendered Umbraco page
/// </summary>
/// <returns></returns>
protected UmbracoPageResult CurrentUmbracoPage()
{
return new UmbracoPageResult(ProfilingLogger);
}
/// <summary>
/// Gets the current page.
/// </summary>
protected virtual IPublishedContent CurrentPage
{
get
{
var routeDefAttempt = TryGetRouteDefinitionFromAncestorViewContexts();
if (routeDefAttempt.Success == false)
throw routeDefAttempt.Exception;
var routeDef = routeDefAttempt.Result;
return routeDef.PublishedRequest.PublishedContent;
}
}
/// <summary>
/// we need to recursively find the route definition based on the parent view context
/// </summary>
/// <returns></returns>
/// <remarks>
/// We may have Child Actions within Child actions so we need to recursively look this up.
/// see: http://issues.umbraco.org/issue/U4-1844
/// </remarks>
private Attempt<RouteDefinition> TryGetRouteDefinitionFromAncestorViewContexts()
{
var currentContext = ControllerContext;
while (currentContext != null)
{
var currentRouteData = currentContext.RouteData;
if (currentRouteData.DataTokens.ContainsKey(Core.Constants.Web.UmbracoRouteDefinitionDataToken))
return Attempt.Succeed((RouteDefinition)currentRouteData.DataTokens[Core.Constants.Web.UmbracoRouteDefinitionDataToken]);
currentContext = currentContext.IsChildAction
? currentContext.ParentActionViewContext
: null;
}
return Attempt<RouteDefinition>.Fail(
new InvalidOperationException("Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"));
}
}
}
: base(umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger)
{
}
/// <summary>
/// Redirects to the Umbraco page with the given id
/// </summary>
/// <param name="pageId"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId)
{
return new RedirectToUmbracoPageResult(pageId, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(pageId, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="pageId"></param>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(int pageId, string queryString)
{
return new RedirectToUmbracoPageResult(pageId, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id
/// </summary>
/// <param name="publishedContent"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent)
{
return new RedirectToUmbracoPageResult(publishedContent, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(publishedContent, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the Umbraco page with the given id and passes provided querystring
/// </summary>
/// <param name="publishedContent"></param>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToUmbracoPage(IPublishedContent publishedContent, string queryString)
{
return new RedirectToUmbracoPageResult(publishedContent, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page
/// </summary>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage()
{
return new RedirectToUmbracoPageResult(CurrentPage, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page and passes provided querystring
/// </summary>
/// <param name="queryStringValues"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(NameValueCollection queryStringValues)
{
return new RedirectToUmbracoPageResult(CurrentPage, queryStringValues, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco page and passes provided querystring
/// </summary>
/// <param name="queryString"></param>
/// <returns></returns>
protected RedirectToUmbracoPageResult RedirectToCurrentUmbracoPage(string queryString)
{
return new RedirectToUmbracoPageResult(CurrentPage, queryString, UmbracoContext);
}
/// <summary>
/// Redirects to the currently rendered Umbraco URL
/// </summary>
/// <returns></returns>
/// <remarks>
/// this is useful if you need to redirect
/// to the current page but the current page is actually a rewritten URL normally done with something like
/// Server.Transfer.
/// </remarks>
protected RedirectToUmbracoUrlResult RedirectToCurrentUmbracoUrl()
{
return new RedirectToUmbracoUrlResult(UmbracoContext);
}
/// <summary>
/// Returns the currently rendered Umbraco page
/// </summary>
/// <returns></returns>
protected UmbracoPageResult CurrentUmbracoPage()
{
return new UmbracoPageResult(ProfilingLogger);
}
/// <summary>
/// Gets the current page.
/// </summary>
protected virtual IPublishedContent CurrentPage
{
get
{
var routeDefAttempt = TryGetRouteDefinitionFromAncestorViewContexts();
if (routeDefAttempt.Success == false)
throw routeDefAttempt.Exception;
var routeDef = routeDefAttempt.Result;
return routeDef.PublishedRequest.PublishedContent;
}
}
/// <summary>
/// we need to recursively find the route definition based on the parent view context
/// </summary>
/// <returns></returns>
/// <remarks>
/// We may have Child Actions within Child actions so we need to recursively look this up.
/// see: http://issues.umbraco.org/issue/U4-1844
/// </remarks>
private Attempt<RouteDefinition> TryGetRouteDefinitionFromAncestorViewContexts()
{
var currentContext = ControllerContext;
while (currentContext != null)
{
var currentRouteData = currentContext.RouteData;
if (currentRouteData.DataTokens.ContainsKey(Core.Constants.Web.UmbracoRouteDefinitionDataToken))
return Attempt.Succeed((RouteDefinition)currentRouteData.DataTokens[Core.Constants.Web.UmbracoRouteDefinitionDataToken]);
currentContext = currentContext.IsChildAction
? currentContext.ParentActionViewContext
: null;
}
return Attempt<RouteDefinition>.Fail(
new InvalidOperationException("Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"));
}
}
}

View File

@@ -1,16 +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);
}
}
}
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);
}
}
}

View File

@@ -1,109 +1,109 @@
using System;
using System.Web;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Web.Composing;
using Umbraco.Core.Configuration;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
public sealed class UmbracoAuthorizeAttribute : AuthorizeAttribute
{
// see note in HttpInstallAuthorizeAttribute
private readonly UmbracoContext _umbracoContext;
private readonly IRuntimeState _runtimeState;
private readonly string _redirectUrl;
private IRuntimeState RuntimeState => _runtimeState ?? Current.RuntimeState;
private UmbracoContext UmbracoContext => _umbracoContext ?? Current.UmbracoContext;
/// <summary>
/// THIS SHOULD BE ONLY USED FOR UNIT TESTS
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="runtimeState"></param>
public UmbracoAuthorizeAttribute(UmbracoContext umbracoContext, IRuntimeState runtimeState)
{
if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext));
if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState));
_umbracoContext = umbracoContext;
_runtimeState = runtimeState;
}
/// <summary>
/// Default constructor
/// </summary>
public UmbracoAuthorizeAttribute()
{ }
/// <summary>
/// Constructor specifying to redirect to the specified location if not authorized
/// </summary>
/// <param name="redirectUrl"></param>
public UmbracoAuthorizeAttribute(string redirectUrl)
{
_redirectUrl = redirectUrl ?? throw new ArgumentNullException(nameof(redirectUrl));
}
/// <summary>
/// Constructor specifying to redirect to the umbraco login page if not authorized
/// </summary>
/// <param name="redirectToUmbracoLogin"></param>
public UmbracoAuthorizeAttribute(bool redirectToUmbracoLogin)
{
if (redirectToUmbracoLogin)
{
_redirectUrl = UmbracoConfig.For.GlobalSettings().Path.EnsureStartsWith("~");
}
}
/// <summary>
/// Ensures that the user must be in the Administrator or the Install role
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
try
{
// if not configured (install or upgrade) then we can continue
// otherwise we need to ensure that a user is logged in
return RuntimeState.Level == RuntimeLevel.Install
|| RuntimeState.Level == RuntimeLevel.Upgrade
|| UmbracoContext.Security.ValidateCurrentUser();
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// Override to to ensure no redirect occurs
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (_redirectUrl.IsNullOrWhiteSpace())
{
filterContext.Result = (ActionResult)new HttpUnauthorizedResult("You must login to view this resource.");
}
else
{
filterContext.Result = new RedirectResult(_redirectUrl);
}
// DON'T do a FormsAuth redirect... argh!! thankfully we're running .Net 4.5 :)
filterContext.RequestContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
}
}
using System;
using System.Web;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Web.Composing;
using Umbraco.Core.Configuration;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Ensures authorization is successful for a back office user.
/// </summary>
public sealed class UmbracoAuthorizeAttribute : AuthorizeAttribute
{
// see note in HttpInstallAuthorizeAttribute
private readonly UmbracoContext _umbracoContext;
private readonly IRuntimeState _runtimeState;
private readonly string _redirectUrl;
private IRuntimeState RuntimeState => _runtimeState ?? Current.RuntimeState;
private UmbracoContext UmbracoContext => _umbracoContext ?? Current.UmbracoContext;
/// <summary>
/// THIS SHOULD BE ONLY USED FOR UNIT TESTS
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="runtimeState"></param>
public UmbracoAuthorizeAttribute(UmbracoContext umbracoContext, IRuntimeState runtimeState)
{
if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext));
if (runtimeState == null) throw new ArgumentNullException(nameof(runtimeState));
_umbracoContext = umbracoContext;
_runtimeState = runtimeState;
}
/// <summary>
/// Default constructor
/// </summary>
public UmbracoAuthorizeAttribute()
{ }
/// <summary>
/// Constructor specifying to redirect to the specified location if not authorized
/// </summary>
/// <param name="redirectUrl"></param>
public UmbracoAuthorizeAttribute(string redirectUrl)
{
_redirectUrl = redirectUrl ?? throw new ArgumentNullException(nameof(redirectUrl));
}
/// <summary>
/// Constructor specifying to redirect to the umbraco login page if not authorized
/// </summary>
/// <param name="redirectToUmbracoLogin"></param>
public UmbracoAuthorizeAttribute(bool redirectToUmbracoLogin)
{
if (redirectToUmbracoLogin)
{
_redirectUrl = UmbracoConfig.For.GlobalSettings().Path.EnsureStartsWith("~");
}
}
/// <summary>
/// Ensures that the user must be in the Administrator or the Install role
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
try
{
// if not configured (install or upgrade) then we can continue
// otherwise we need to ensure that a user is logged in
return RuntimeState.Level == RuntimeLevel.Install
|| RuntimeState.Level == RuntimeLevel.Upgrade
|| UmbracoContext.Security.ValidateCurrentUser();
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// Override to to ensure no redirect occurs
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (_redirectUrl.IsNullOrWhiteSpace())
{
filterContext.Result = (ActionResult)new HttpUnauthorizedResult("You must login to view this resource.");
}
else
{
filterContext.Result = new RedirectResult(_redirectUrl);
}
// DON'T do a FormsAuth redirect... argh!! thankfully we're running .Net 4.5 :)
filterContext.RequestContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
}
}

View File

@@ -1,28 +1,28 @@
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for authorized Umbraco controllers.
/// </summary>
/// <remarks>
/// This controller essentially just uses a global UmbracoAuthorizeAttribute, inheritors that require more granular control over the
/// authorization of each method can use this attribute instead of inheriting from this controller.
/// </remarks>
[UmbracoAuthorize]
[DisableBrowserCache]
public abstract class UmbracoAuthorizedController : UmbracoController
{
protected UmbracoAuthorizedController()
{
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for authorized Umbraco controllers.
/// </summary>
/// <remarks>
/// This controller essentially just uses a global UmbracoAuthorizeAttribute, inheritors that require more granular control over the
/// authorization of each method can use this attribute instead of inheriting from this controller.
/// </remarks>
[UmbracoAuthorize]
[DisableBrowserCache]
public abstract class UmbracoAuthorizedController : UmbracoController
{
protected UmbracoAuthorizedController()
{
}
protected UmbracoAuthorizedController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger) : base(globalSettings, umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger)
{
}
}
}
protected UmbracoAuthorizedController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger) : base(globalSettings, umbracoContext, databaseFactory, services, applicationCache, logger, profilingLogger)
{
}
}
}

View File

@@ -1,87 +1,87 @@
using System;
using System.Web;
using System.Web.Mvc;
using Microsoft.Owin;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for Umbraco controllers.
/// </summary>
public abstract class UmbracoController : Controller
{
private UmbracoHelper _umbracoHelper;
// for debugging purposes
internal Guid InstanceId { get; } = Guid.NewGuid();
// note
// properties marked as [Inject] below will be property-injected (vs constructor-injected) in
// order to keep the constuctor as light as possible, so that ppl implementing eg a SurfaceController
// don't need to implement complex constructors + need to refactor them each time we change ours.
// this means that these properties have a setter.
// what can go wrong?
/// <summary>
/// Gets or sets the Umbraco context.
/// </summary>
using System;
using System.Web;
using System.Web.Mvc;
using Microsoft.Owin;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Provides a base class for Umbraco controllers.
/// </summary>
public abstract class UmbracoController : Controller
{
private UmbracoHelper _umbracoHelper;
// for debugging purposes
internal Guid InstanceId { get; } = Guid.NewGuid();
// note
// properties marked as [Inject] below will be property-injected (vs constructor-injected) in
// order to keep the constuctor as light as possible, so that ppl implementing eg a SurfaceController
// don't need to implement complex constructors + need to refactor them each time we change ours.
// this means that these properties have a setter.
// what can go wrong?
/// <summary>
/// Gets or sets the Umbraco context.
/// </summary>
public virtual IGlobalSettings GlobalSettings { get; set; }
/// <summary>
/// Gets or sets the Umbraco context.
/// </summary>
public virtual UmbracoContext UmbracoContext { get; set; }
/// <summary>
/// Gets or sets the database context.
/// </summary>
public IUmbracoDatabaseFactory DatabaseFactory { get; set; }
/// <summary>
/// Gets or sets the services context.
/// </summary>
public ServiceContext Services { get; set; }
/// <summary>
/// Gets or sets the application cache.
/// </summary>
public CacheHelper ApplicationCache { get; set; }
/// <summary>
/// Gets or sets the logger.
/// </summary>
public ILogger Logger { get; set; }
/// <summary>
/// Gets or sets the profiling logger.
/// </summary>
public ProfilingLogger ProfilingLogger { get; set; }
protected IOwinContext OwinContext => Request.GetOwinContext();
/// <summary>
/// Gets the membership helper.
/// </summary>
public MembershipHelper Members => Umbraco.MembershipHelper;
/// <summary>
/// Gets the Umbraco helper.
/// </summary>
public UmbracoHelper Umbraco => _umbracoHelper
?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache));
/// <summary>
/// Gets the web security helper.
/// </summary>
public virtual WebSecurity Security => UmbracoContext.Security;
/// <summary>
/// Gets or sets the Umbraco context.
/// </summary>
public virtual UmbracoContext UmbracoContext { get; set; }
/// <summary>
/// Gets or sets the database context.
/// </summary>
public IUmbracoDatabaseFactory DatabaseFactory { get; set; }
/// <summary>
/// Gets or sets the services context.
/// </summary>
public ServiceContext Services { get; set; }
/// <summary>
/// Gets or sets the application cache.
/// </summary>
public CacheHelper ApplicationCache { get; set; }
/// <summary>
/// Gets or sets the logger.
/// </summary>
public ILogger Logger { get; set; }
/// <summary>
/// Gets or sets the profiling logger.
/// </summary>
public ProfilingLogger ProfilingLogger { get; set; }
protected IOwinContext OwinContext => Request.GetOwinContext();
/// <summary>
/// Gets the membership helper.
/// </summary>
public MembershipHelper Members => Umbraco.MembershipHelper;
/// <summary>
/// Gets the Umbraco helper.
/// </summary>
public UmbracoHelper Umbraco => _umbracoHelper
?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache));
/// <summary>
/// Gets the web security helper.
/// </summary>
public virtual WebSecurity Security => UmbracoContext.Security;
protected UmbracoController()
: this(
Current.Container.GetInstance<IGlobalSettings>(),
@@ -91,19 +91,19 @@ namespace Umbraco.Web.Mvc
Current.Container.GetInstance<CacheHelper>(),
Current.Container.GetInstance<ILogger>(),
Current.Container.GetInstance<ProfilingLogger>()
)
{
)
{
}
protected UmbracoController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger)
{
GlobalSettings = globalSettings;
UmbracoContext = umbracoContext;
DatabaseFactory = databaseFactory;
Services = services;
ApplicationCache = applicationCache;
Logger = logger;
ProfilingLogger = profilingLogger;
}
}
}
protected UmbracoController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, CacheHelper applicationCache, ILogger logger, ProfilingLogger profilingLogger)
{
GlobalSettings = globalSettings;
UmbracoContext = umbracoContext;
DatabaseFactory = databaseFactory;
Services = services;
ApplicationCache = applicationCache;
Logger = logger;
ProfilingLogger = profilingLogger;
}
}
}

View File

@@ -1,85 +1,85 @@
using System;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Web.Composing;
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)
{
var controllerType = GetControllerType(requestContext, controllerName) ??
_innerFactory.GetControllerType(
requestContext,
ControllerExtensions.GetControllerName(Current.DefaultRenderMvcControllerType));
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 virtual 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);
}
}
}
}
using System;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
using Umbraco.Core;
using Umbraco.Web.Composing;
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)
{
var controllerType = GetControllerType(requestContext, controllerName) ??
_innerFactory.GetControllerType(
requestContext,
ControllerExtensions.GetControllerName(Current.DefaultRenderMvcControllerType));
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 virtual 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);
}
}
}
}

View File

@@ -1,32 +1,32 @@
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// MVC handler to facilitate the TemplateRenderer. This handler can execute an MVC request and return it as a string.
///
/// Original:
///
/// 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
{
public UmbracoMvcHandler(RequestContext requestContext)
: base(requestContext)
{
}
/// <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()
{
base.ProcessRequest(RequestContext.HttpContext);
}
}
}
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// MVC handler to facilitate the TemplateRenderer. This handler can execute an MVC request and return it as a string.
///
/// Original:
///
/// 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
{
public UmbracoMvcHandler(RequestContext requestContext)
: base(requestContext)
{
}
/// <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()
{
base.ProcessRequest(RequestContext.HttpContext);
}
}
}

View File

@@ -1,181 +1,181 @@
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Profiling;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Used by posted forms to proxy the result to the page in which the current URL matches on
/// </summary>
public class UmbracoPageResult : ActionResult
{
private readonly ProfilingLogger _profilingLogger;
public UmbracoPageResult(ProfilingLogger profilingLogger)
{
_profilingLogger = profilingLogger;
}
[Obsolete("Use the ctor specifying all depenendencies instead")]
public UmbracoPageResult()
: this(new ProfilingLogger(Current.Logger, Current.Profiler))
{
}
public override void ExecuteResult(ControllerContext context)
{
ResetRouteData(context.RouteData);
ValidateRouteData(context.RouteData);
var routeDef = (RouteDefinition)context.RouteData.DataTokens[Umbraco.Core.Constants.Web.UmbracoRouteDefinitionDataToken];
//Special case, if it is webforms but we're posting to an MVC surface controller, then we
// need to return the webforms result instead
if (routeDef.PublishedRequest.RenderingEngine == RenderingEngine.WebForms)
{
EnsureViewContextForWebForms(context);
var webFormsHandler = RenderRouteHandler.GetWebFormsHandler();
webFormsHandler.ProcessRequest(HttpContext.Current);
}
else
{
var factory = ControllerBuilder.Current.GetControllerFactory();
context.RouteData.Values["action"] = routeDef.ActionName;
ControllerBase controller = null;
try
{
controller = CreateController(context, factory, routeDef);
CopyControllerData(context, controller);
ExecuteControllerAction(context, controller);
}
finally
{
CleanupController(controller, factory);
}
}
}
/// <summary>
/// Executes the controller action
/// </summary>
private void ExecuteControllerAction(ControllerContext context, IController controller)
{
using (_profilingLogger.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.Core.Constants.Web.UmbracoRouteDefinitionDataToken) == false)
{
throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name +
" in the context of an Http POST when using a SurfaceController form");
}
}
/// <summary>
/// When POSTing to MVC but rendering in WebForms we need to do some trickery, we'll create a dummy viewcontext with all of the
/// current modelstate, tempdata, viewdata so that if we're rendering partial view macros within the webforms view, they will
/// get all of this merged into them.
/// </summary>
/// <param name="context"></param>
private static void EnsureViewContextForWebForms(ControllerContext context)
{
var tempDataDictionary = new TempDataDictionary();
tempDataDictionary.Save(context, new SessionStateTempDataProvider());
var viewCtx = new ViewContext(context, new DummyView(), new ViewDataDictionary(), tempDataDictionary, new StringWriter());
viewCtx.ViewData.ModelState.Merge(new ModelStateDictionary(context.Controller.ViewData.ModelState));
foreach (var d in context.Controller.ViewData)
viewCtx.ViewData[d.Key] = d.Value;
//now we need to add it to the special route tokens so it's picked up later
context.HttpContext.Request.RequestContext.RouteData.DataTokens[Constants.DataTokenCurrentViewContext] = viewCtx;
}
/// <summary>
/// Ensure ModelState, ViewData and TempData is copied across
/// </summary>
private static void CopyControllerData(ControllerContext context, ControllerBase controller)
{
controller.ViewData.ModelState.Merge(context.Controller.ViewData.ModelState);
foreach (var d in context.Controller.ViewData)
controller.ViewData[d.Key] = d.Value;
//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
var targetController = controller as Controller;
var sourceController = context.Controller as Controller;
if (targetController != null && sourceController != null)
{
targetController.TempDataProvider = sourceController.TempDataProvider;
targetController.TempData = sourceController.TempData;
targetController.TempData.Save(sourceController.ControllerContext, sourceController.TempDataProvider);
}
}
/// <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();
}
private class DummyView : IView
{
public void Render(ViewContext viewContext, TextWriter writer)
{
}
}
}
}
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Profiling;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Used by posted forms to proxy the result to the page in which the current URL matches on
/// </summary>
public class UmbracoPageResult : ActionResult
{
private readonly ProfilingLogger _profilingLogger;
public UmbracoPageResult(ProfilingLogger profilingLogger)
{
_profilingLogger = profilingLogger;
}
[Obsolete("Use the ctor specifying all depenendencies instead")]
public UmbracoPageResult()
: this(new ProfilingLogger(Current.Logger, Current.Profiler))
{
}
public override void ExecuteResult(ControllerContext context)
{
ResetRouteData(context.RouteData);
ValidateRouteData(context.RouteData);
var routeDef = (RouteDefinition)context.RouteData.DataTokens[Umbraco.Core.Constants.Web.UmbracoRouteDefinitionDataToken];
//Special case, if it is webforms but we're posting to an MVC surface controller, then we
// need to return the webforms result instead
if (routeDef.PublishedRequest.RenderingEngine == RenderingEngine.WebForms)
{
EnsureViewContextForWebForms(context);
var webFormsHandler = RenderRouteHandler.GetWebFormsHandler();
webFormsHandler.ProcessRequest(HttpContext.Current);
}
else
{
var factory = ControllerBuilder.Current.GetControllerFactory();
context.RouteData.Values["action"] = routeDef.ActionName;
ControllerBase controller = null;
try
{
controller = CreateController(context, factory, routeDef);
CopyControllerData(context, controller);
ExecuteControllerAction(context, controller);
}
finally
{
CleanupController(controller, factory);
}
}
}
/// <summary>
/// Executes the controller action
/// </summary>
private void ExecuteControllerAction(ControllerContext context, IController controller)
{
using (_profilingLogger.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.Core.Constants.Web.UmbracoRouteDefinitionDataToken) == false)
{
throw new InvalidOperationException("Can only use " + typeof(UmbracoPageResult).Name +
" in the context of an Http POST when using a SurfaceController form");
}
}
/// <summary>
/// When POSTing to MVC but rendering in WebForms we need to do some trickery, we'll create a dummy viewcontext with all of the
/// current modelstate, tempdata, viewdata so that if we're rendering partial view macros within the webforms view, they will
/// get all of this merged into them.
/// </summary>
/// <param name="context"></param>
private static void EnsureViewContextForWebForms(ControllerContext context)
{
var tempDataDictionary = new TempDataDictionary();
tempDataDictionary.Save(context, new SessionStateTempDataProvider());
var viewCtx = new ViewContext(context, new DummyView(), new ViewDataDictionary(), tempDataDictionary, new StringWriter());
viewCtx.ViewData.ModelState.Merge(new ModelStateDictionary(context.Controller.ViewData.ModelState));
foreach (var d in context.Controller.ViewData)
viewCtx.ViewData[d.Key] = d.Value;
//now we need to add it to the special route tokens so it's picked up later
context.HttpContext.Request.RequestContext.RouteData.DataTokens[Constants.DataTokenCurrentViewContext] = viewCtx;
}
/// <summary>
/// Ensure ModelState, ViewData and TempData is copied across
/// </summary>
private static void CopyControllerData(ControllerContext context, ControllerBase controller)
{
controller.ViewData.ModelState.Merge(context.Controller.ViewData.ModelState);
foreach (var d in context.Controller.ViewData)
controller.ViewData[d.Key] = d.Value;
//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
var targetController = controller as Controller;
var sourceController = context.Controller as Controller;
if (targetController != null && sourceController != null)
{
targetController.TempDataProvider = sourceController.TempDataProvider;
targetController.TempData = sourceController.TempData;
targetController.TempData.Save(sourceController.ControllerContext, sourceController.TempDataProvider);
}
}
/// <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();
}
private class DummyView : IView
{
public void Render(ViewContext viewContext, TextWriter writer)
{
}
}
}
}

View File

@@ -1,7 +1,7 @@
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Mvc
{
public abstract class UmbracoViewPage : UmbracoViewPage<IPublishedContent>
{ }
}
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Mvc
{
public abstract class UmbracoViewPage : UmbracoViewPage<IPublishedContent>
{ }
}

View File

@@ -1,260 +1,260 @@
using System;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.WebPages;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the properties and methods that are needed in order to render an Umbraco view.
/// </summary>
public abstract class UmbracoViewPage<TModel> : WebViewPage<TModel>
{
private UmbracoContext _umbracoContext;
private UmbracoHelper _helper;
// note
// properties marked as [Inject] below will be property-injected (vs constructor-injected) since
// we have no control over the view constructor (generated by eg the Razor engine).
// this means that these properties have a setter.
// what can go wrong?
/// <summary>
/// Gets or sets the database context.
/// </summary>
public ServiceContext Services { get; set; }
/// <summary>
/// Gets or sets the application cache.
/// </summary>
public CacheHelper ApplicationCache { get; set; }
// fixme
// previously, Services and ApplicationCache would derive from UmbracoContext.Application, which
// was an ApplicationContext - so that everything derived from UmbracoContext.
// UmbracoContext is fetched from the data tokens, thus allowing the view to be rendered with a
// custom context and NOT the Current.UmbracoContext - eg outside the normal Umbraco routing
// process.
// leaving it as-it for the time being but really - the UmbracoContext should be injected just
// like the Services & ApplicationCache properties, and have a setter for those special weird
// cases.
/// <summary>
/// Gets the Umbraco context.
/// </summary>
public UmbracoContext UmbracoContext => _umbracoContext
?? (_umbracoContext = ViewContext.GetUmbracoContext() ?? Current.UmbracoContext);
/// <summary>
/// Gets the public content request.
/// </summary>
internal PublishedRequest PublishedRequest
{
get
{
const string token = Core.Constants.Web.PublishedDocumentRequestDataToken;
// we should always try to return the object from the data tokens just in case its a custom object and not
// the one from UmbracoContext. Fallback to UmbracoContext if necessary.
// try view context
if (ViewContext.RouteData.DataTokens.ContainsKey(token))
return (PublishedRequest) ViewContext.RouteData.DataTokens.GetRequiredObject(token);
// child action, try parent view context
if (ViewContext.IsChildAction && ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey(token))
return (PublishedRequest) ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject(token);
// fallback to UmbracoContext
return UmbracoContext.PublishedRequest;
}
}
/// <summary>
/// Gets the Umbraco helper.
/// </summary>
public virtual UmbracoHelper Umbraco
{
get
{
if (_helper != null) return _helper;
var model = ViewData.Model;
var content = model as IPublishedContent;
if (content == null && model is IContentModel)
content = ((IContentModel) model).Content;
_helper = content == null
? new UmbracoHelper(UmbracoContext, Services, ApplicationCache)
: new UmbracoHelper(UmbracoContext, Services, ApplicationCache, content);
return _helper;
}
}
/// <summary>
/// Gets the membership helper.
/// </summary>
public MembershipHelper Members => Umbraco.MembershipHelper;
protected UmbracoViewPage()
: this(
Current.Container.GetInstance<ServiceContext>(),
Current.Container.GetInstance<CacheHelper>()
)
{
}
protected UmbracoViewPage(ServiceContext services, CacheHelper applicationCache)
{
Services = services;
ApplicationCache = applicationCache;
}
// view logic below:
/// <summary>
/// Ensure that the current view context is added to the route data tokens so we can extract it if we like
/// </summary>
/// <remarks>
/// Currently this is required by mvc macro engines
/// </remarks>
protected override void InitializePage()
{
base.InitializePage();
if (ViewContext.IsChildAction) return;
// this is used purely for partial view macros that contain forms and mostly
// just when rendered within the RTE - this should already be set with the
// EnsurePartialViewMacroViewContextFilterAttribute
if (ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext) == false)
ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, ViewContext);
}
// maps model
protected override void SetViewData(ViewDataDictionary viewData)
{
// capture the model before we tinker with the viewData
var viewDataModel = viewData.Model;
// map the view data (may change its type, may set model to null)
viewData = MapViewDataDictionary(viewData, typeof (TModel));
// bind the model
viewData.Model = ContentModelBinder.BindModel(viewDataModel, typeof (TModel));
// set the view data
base.SetViewData(viewData);
}
// viewData is the ViewDataDictionary (maybe <TModel>) that we have
// modelType is the type of the model that we need to bind to
//
// figure out whether viewData can accept modelType else replace it
//
private static ViewDataDictionary MapViewDataDictionary(ViewDataDictionary viewData, Type modelType)
{
var viewDataType = viewData.GetType();
// if viewData is not generic then it is a simple ViewDataDictionary instance and its
// Model property is of type 'object' and will accept anything, so it is safe to use
// viewData
if (viewDataType.IsGenericType == false)
return viewData;
// ensure it is the proper generic type
var def = viewDataType.GetGenericTypeDefinition();
if (def != typeof(ViewDataDictionary<>))
throw new Exception("Could not map viewData of type \"" + viewDataType.FullName + "\".");
// get the viewData model type and compare with the actual view model type:
// viewData is ViewDataDictionary<viewDataModelType> and we will want to assign an
// object of type modelType to the Model property of type viewDataModelType, we
// need to check whether that is possible
var viewDataModelType = viewDataType.GenericTypeArguments[0];
if (viewDataModelType.IsAssignableFrom(modelType))
return viewData;
// if not possible then we need to create a new ViewDataDictionary
var nViewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
var tViewData = new ViewDataDictionary(viewData) { Model = null }; // temp view data to copy values
var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData);
return nViewData;
}
/// <summary>
/// This will detect the end /body tag and insert the preview badge if in preview mode
/// </summary>
/// <param name="value"></param>
public override void WriteLiteral(object value)
{
// filter / add preview banner
if (Response.ContentType.InvariantEquals("text/html")) // ASP.NET default value
{
if (UmbracoContext.Current.IsDebug || UmbracoContext.Current.InPreviewMode)
{
var text = value.ToString();
var pos = text.IndexOf("</body>", StringComparison.InvariantCultureIgnoreCase);
if (pos > -1)
{
string markupToInject;
if (UmbracoContext.Current.InPreviewMode)
{
// creating previewBadge markup
markupToInject =
string.Format(UmbracoConfig.For.UmbracoSettings().Content.PreviewBadge,
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path));
}
else
{
// creating mini-profiler markup
markupToInject = Html.RenderProfiler().ToHtmlString();
}
var sb = new StringBuilder(text);
sb.Insert(pos, markupToInject);
base.WriteLiteral(sb.ToString());
return;
}
}
}
base.WriteLiteral(value);
}
public HelperResult RenderSection(string name, Func<dynamic, HelperResult> defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
public HelperResult RenderSection(string name, HelperResult defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
public HelperResult RenderSection(string name, string defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
public HelperResult RenderSection(string name, IHtmlString defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
}
}
using System;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.WebPages;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web.Models;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Represents the properties and methods that are needed in order to render an Umbraco view.
/// </summary>
public abstract class UmbracoViewPage<TModel> : WebViewPage<TModel>
{
private UmbracoContext _umbracoContext;
private UmbracoHelper _helper;
// note
// properties marked as [Inject] below will be property-injected (vs constructor-injected) since
// we have no control over the view constructor (generated by eg the Razor engine).
// this means that these properties have a setter.
// what can go wrong?
/// <summary>
/// Gets or sets the database context.
/// </summary>
public ServiceContext Services { get; set; }
/// <summary>
/// Gets or sets the application cache.
/// </summary>
public CacheHelper ApplicationCache { get; set; }
// fixme
// previously, Services and ApplicationCache would derive from UmbracoContext.Application, which
// was an ApplicationContext - so that everything derived from UmbracoContext.
// UmbracoContext is fetched from the data tokens, thus allowing the view to be rendered with a
// custom context and NOT the Current.UmbracoContext - eg outside the normal Umbraco routing
// process.
// leaving it as-it for the time being but really - the UmbracoContext should be injected just
// like the Services & ApplicationCache properties, and have a setter for those special weird
// cases.
/// <summary>
/// Gets the Umbraco context.
/// </summary>
public UmbracoContext UmbracoContext => _umbracoContext
?? (_umbracoContext = ViewContext.GetUmbracoContext() ?? Current.UmbracoContext);
/// <summary>
/// Gets the public content request.
/// </summary>
internal PublishedRequest PublishedRequest
{
get
{
const string token = Core.Constants.Web.PublishedDocumentRequestDataToken;
// we should always try to return the object from the data tokens just in case its a custom object and not
// the one from UmbracoContext. Fallback to UmbracoContext if necessary.
// try view context
if (ViewContext.RouteData.DataTokens.ContainsKey(token))
return (PublishedRequest) ViewContext.RouteData.DataTokens.GetRequiredObject(token);
// child action, try parent view context
if (ViewContext.IsChildAction && ViewContext.ParentActionViewContext.RouteData.DataTokens.ContainsKey(token))
return (PublishedRequest) ViewContext.ParentActionViewContext.RouteData.DataTokens.GetRequiredObject(token);
// fallback to UmbracoContext
return UmbracoContext.PublishedRequest;
}
}
/// <summary>
/// Gets the Umbraco helper.
/// </summary>
public virtual UmbracoHelper Umbraco
{
get
{
if (_helper != null) return _helper;
var model = ViewData.Model;
var content = model as IPublishedContent;
if (content == null && model is IContentModel)
content = ((IContentModel) model).Content;
_helper = content == null
? new UmbracoHelper(UmbracoContext, Services, ApplicationCache)
: new UmbracoHelper(UmbracoContext, Services, ApplicationCache, content);
return _helper;
}
}
/// <summary>
/// Gets the membership helper.
/// </summary>
public MembershipHelper Members => Umbraco.MembershipHelper;
protected UmbracoViewPage()
: this(
Current.Container.GetInstance<ServiceContext>(),
Current.Container.GetInstance<CacheHelper>()
)
{
}
protected UmbracoViewPage(ServiceContext services, CacheHelper applicationCache)
{
Services = services;
ApplicationCache = applicationCache;
}
// view logic below:
/// <summary>
/// Ensure that the current view context is added to the route data tokens so we can extract it if we like
/// </summary>
/// <remarks>
/// Currently this is required by mvc macro engines
/// </remarks>
protected override void InitializePage()
{
base.InitializePage();
if (ViewContext.IsChildAction) return;
// this is used purely for partial view macros that contain forms and mostly
// just when rendered within the RTE - this should already be set with the
// EnsurePartialViewMacroViewContextFilterAttribute
if (ViewContext.RouteData.DataTokens.ContainsKey(Constants.DataTokenCurrentViewContext) == false)
ViewContext.RouteData.DataTokens.Add(Constants.DataTokenCurrentViewContext, ViewContext);
}
// maps model
protected override void SetViewData(ViewDataDictionary viewData)
{
// capture the model before we tinker with the viewData
var viewDataModel = viewData.Model;
// map the view data (may change its type, may set model to null)
viewData = MapViewDataDictionary(viewData, typeof (TModel));
// bind the model
viewData.Model = ContentModelBinder.BindModel(viewDataModel, typeof (TModel));
// set the view data
base.SetViewData(viewData);
}
// viewData is the ViewDataDictionary (maybe <TModel>) that we have
// modelType is the type of the model that we need to bind to
//
// figure out whether viewData can accept modelType else replace it
//
private static ViewDataDictionary MapViewDataDictionary(ViewDataDictionary viewData, Type modelType)
{
var viewDataType = viewData.GetType();
// if viewData is not generic then it is a simple ViewDataDictionary instance and its
// Model property is of type 'object' and will accept anything, so it is safe to use
// viewData
if (viewDataType.IsGenericType == false)
return viewData;
// ensure it is the proper generic type
var def = viewDataType.GetGenericTypeDefinition();
if (def != typeof(ViewDataDictionary<>))
throw new Exception("Could not map viewData of type \"" + viewDataType.FullName + "\".");
// get the viewData model type and compare with the actual view model type:
// viewData is ViewDataDictionary<viewDataModelType> and we will want to assign an
// object of type modelType to the Model property of type viewDataModelType, we
// need to check whether that is possible
var viewDataModelType = viewDataType.GenericTypeArguments[0];
if (viewDataModelType.IsAssignableFrom(modelType))
return viewData;
// if not possible then we need to create a new ViewDataDictionary
var nViewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
var tViewData = new ViewDataDictionary(viewData) { Model = null }; // temp view data to copy values
var nViewData = (ViewDataDictionary)Activator.CreateInstance(nViewDataType, tViewData);
return nViewData;
}
/// <summary>
/// This will detect the end /body tag and insert the preview badge if in preview mode
/// </summary>
/// <param name="value"></param>
public override void WriteLiteral(object value)
{
// filter / add preview banner
if (Response.ContentType.InvariantEquals("text/html")) // ASP.NET default value
{
if (UmbracoContext.Current.IsDebug || UmbracoContext.Current.InPreviewMode)
{
var text = value.ToString();
var pos = text.IndexOf("</body>", StringComparison.InvariantCultureIgnoreCase);
if (pos > -1)
{
string markupToInject;
if (UmbracoContext.Current.InPreviewMode)
{
// creating previewBadge markup
markupToInject =
string.Format(UmbracoConfig.For.UmbracoSettings().Content.PreviewBadge,
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
Server.UrlEncode(UmbracoContext.Current.HttpContext.Request.Path));
}
else
{
// creating mini-profiler markup
markupToInject = Html.RenderProfiler().ToHtmlString();
}
var sb = new StringBuilder(text);
sb.Insert(pos, markupToInject);
base.WriteLiteral(sb.ToString());
return;
}
}
}
base.WriteLiteral(value);
}
public HelperResult RenderSection(string name, Func<dynamic, HelperResult> defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
public HelperResult RenderSection(string name, HelperResult defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
public HelperResult RenderSection(string name, string defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
public HelperResult RenderSection(string name, IHtmlString defaultContents)
{
return WebViewPageExtensions.RenderSection(this, name, defaultContents);
}
}
}

View File

@@ -1,112 +1,112 @@
using System;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Xml;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Extension methods for UrlHelper
/// </summary>
public static class UrlHelperExtensions
{
/// <summary>
/// Utility method for checking for valid proxy urls or redirect urls to prevent Open Redirect security issues
/// </summary>
/// <param name="urlHelper"></param>
/// <param name="url">The url to validate</param>
/// <param name="callerUrl">The url of the current local domain (to ensure we can validate if the requested url is local without dependency on the request)</param>
/// <returns>True if it's an allowed url</returns>
public static bool ValidateProxyUrl(this UrlHelper urlHelper, string url, string callerUrl)
{
if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
{
return false;
}
if (url.StartsWith("//"))
return false;
Uri requestUri;
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out requestUri))
{
if (string.IsNullOrEmpty(callerUrl) == false)
{
Uri localUri;
if (Uri.TryCreate(callerUrl, UriKind.RelativeOrAbsolute, out localUri))
{
// check for local urls
//Cannot start with // since that is not a local url
if (requestUri.OriginalString.StartsWith("//") == false
//cannot be non-absolute and also contain the char : since that will indicate a protocol
&& (requestUri.IsAbsoluteUri == false && requestUri.OriginalString.Contains(":") == false)
//needs to be non-absolute or the hosts must match the current request
&& (requestUri.IsAbsoluteUri == false || requestUri.Host == localUri.Host))
{
return true;
}
}
else
{
return false;
}
}
//we cannot continue if the url is not absolute
if (requestUri.IsAbsoluteUri == false)
{
return false;
}
// check for valid proxy urls
var feedProxyXml = XmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig));
if (feedProxyXml != null &&
feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null)
{
return true;
}
}
else
{
return false;
}
return false;
}
/// <summary>
/// Returns the base path (not including the 'action') of the MVC controller "SaveFileController"
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string GetSaveFileServicePath(this UrlHelper url)
{
var result = url.Action("SavePartialView", "SaveFile", new {area = UmbracoConfig.For.GlobalSettings().GetUmbracoMvcArea()});
return result.TrimEnd("SavePartialView").EnsureEndsWith('/');
}
/// <summary>
/// Returns the base path (not including the 'action') of the MVC controller "BulkPublishController"
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string GetBulkPublishServicePath(this UrlHelper url)
{
var result = url.Action("PublishDocument", "BulkPublish", new { area = UmbracoConfig.For.GlobalSettings().GetUmbracoMvcArea() });
return result.TrimEnd("PublishDocument").EnsureEndsWith('/');
}
/// <summary>
/// Returns the base path (not including the 'action') of the MVC controller "CoreStringsController"
/// </summary>
/// <param name="url">The url helper.</param>
/// <returns>The base path of the controller.</returns>
public static string GetCoreStringsControllerPath(this UrlHelper url)
{
var result = url.Action("ToSafeAlias", "CoreStrings", new { area = UmbracoConfig.For.GlobalSettings().GetUmbracoMvcArea() });
return result.TrimEnd("ToSafeAlias");
}
}
}
using System;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Xml;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// Extension methods for UrlHelper
/// </summary>
public static class UrlHelperExtensions
{
/// <summary>
/// Utility method for checking for valid proxy urls or redirect urls to prevent Open Redirect security issues
/// </summary>
/// <param name="urlHelper"></param>
/// <param name="url">The url to validate</param>
/// <param name="callerUrl">The url of the current local domain (to ensure we can validate if the requested url is local without dependency on the request)</param>
/// <returns>True if it's an allowed url</returns>
public static bool ValidateProxyUrl(this UrlHelper urlHelper, string url, string callerUrl)
{
if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
{
return false;
}
if (url.StartsWith("//"))
return false;
Uri requestUri;
if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out requestUri))
{
if (string.IsNullOrEmpty(callerUrl) == false)
{
Uri localUri;
if (Uri.TryCreate(callerUrl, UriKind.RelativeOrAbsolute, out localUri))
{
// check for local urls
//Cannot start with // since that is not a local url
if (requestUri.OriginalString.StartsWith("//") == false
//cannot be non-absolute and also contain the char : since that will indicate a protocol
&& (requestUri.IsAbsoluteUri == false && requestUri.OriginalString.Contains(":") == false)
//needs to be non-absolute or the hosts must match the current request
&& (requestUri.IsAbsoluteUri == false || requestUri.Host == localUri.Host))
{
return true;
}
}
else
{
return false;
}
}
//we cannot continue if the url is not absolute
if (requestUri.IsAbsoluteUri == false)
{
return false;
}
// check for valid proxy urls
var feedProxyXml = XmlHelper.OpenAsXmlDocument(IOHelper.MapPath(SystemFiles.FeedProxyConfig));
if (feedProxyXml != null &&
feedProxyXml.SelectSingleNode(string.Concat("//allow[@host = '", requestUri.Host, "']")) != null)
{
return true;
}
}
else
{
return false;
}
return false;
}
/// <summary>
/// Returns the base path (not including the 'action') of the MVC controller "SaveFileController"
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string GetSaveFileServicePath(this UrlHelper url)
{
var result = url.Action("SavePartialView", "SaveFile", new {area = UmbracoConfig.For.GlobalSettings().GetUmbracoMvcArea()});
return result.TrimEnd("SavePartialView").EnsureEndsWith('/');
}
/// <summary>
/// Returns the base path (not including the 'action') of the MVC controller "BulkPublishController"
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string GetBulkPublishServicePath(this UrlHelper url)
{
var result = url.Action("PublishDocument", "BulkPublish", new { area = UmbracoConfig.For.GlobalSettings().GetUmbracoMvcArea() });
return result.TrimEnd("PublishDocument").EnsureEndsWith('/');
}
/// <summary>
/// Returns the base path (not including the 'action') of the MVC controller "CoreStringsController"
/// </summary>
/// <param name="url">The url helper.</param>
/// <returns>The base path of the controller.</returns>
public static string GetCoreStringsControllerPath(this UrlHelper url)
{
var result = url.Action("ToSafeAlias", "CoreStrings", new { area = UmbracoConfig.For.GlobalSettings().GetUmbracoMvcArea() });
return result.TrimEnd("ToSafeAlias");
}
}
}

View File

@@ -1,53 +1,53 @@
using System.IO;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
internal static class ViewContextExtensions
{
/// <summary>
/// Creates a new ViewContext from an existing one but specifies a new Model for the ViewData
/// </summary>
/// <param name="vc"></param>
/// <param name="model"></param>
/// <returns></returns>
public static ViewContext CopyWithModel(this ViewContext vc, object model)
{
return new ViewContext
{
Controller = vc.Controller,
HttpContext = vc.HttpContext,
RequestContext = vc.RequestContext,
RouteData = vc.RouteData,
TempData = vc.TempData,
View = vc.View,
ViewData = new ViewDataDictionary(vc)
{
Model = model
},
FormContext = vc.FormContext,
ClientValidationEnabled = vc.ClientValidationEnabled,
UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled,
Writer = vc.Writer
};
}
//public static ViewContext CloneWithWriter(this ViewContext vc, TextWriter writer)
//{
// return new ViewContext
// {
// Controller = vc.Controller,
// HttpContext = vc.HttpContext,
// RequestContext = vc.RequestContext,
// RouteData = vc.RouteData,
// TempData = vc.TempData,
// View = vc.View,
// ViewData = vc.ViewData,
// FormContext = vc.FormContext,
// ClientValidationEnabled = vc.ClientValidationEnabled,
// UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled,
// Writer = writer
// };
//}
}
}
using System.IO;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
internal static class ViewContextExtensions
{
/// <summary>
/// Creates a new ViewContext from an existing one but specifies a new Model for the ViewData
/// </summary>
/// <param name="vc"></param>
/// <param name="model"></param>
/// <returns></returns>
public static ViewContext CopyWithModel(this ViewContext vc, object model)
{
return new ViewContext
{
Controller = vc.Controller,
HttpContext = vc.HttpContext,
RequestContext = vc.RequestContext,
RouteData = vc.RouteData,
TempData = vc.TempData,
View = vc.View,
ViewData = new ViewDataDictionary(vc)
{
Model = model
},
FormContext = vc.FormContext,
ClientValidationEnabled = vc.ClientValidationEnabled,
UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled,
Writer = vc.Writer
};
}
//public static ViewContext CloneWithWriter(this ViewContext vc, TextWriter writer)
//{
// return new ViewContext
// {
// Controller = vc.Controller,
// HttpContext = vc.HttpContext,
// RequestContext = vc.RequestContext,
// RouteData = vc.RouteData,
// TempData = vc.TempData,
// View = vc.View,
// ViewData = vc.ViewData,
// FormContext = vc.FormContext,
// ClientValidationEnabled = vc.ClientValidationEnabled,
// UnobtrusiveJavaScriptEnabled = vc.UnobtrusiveJavaScriptEnabled,
// Writer = writer
// };
//}
}
}

View File

@@ -1,49 +1,49 @@
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
internal static class ViewDataContainerExtensions
{
private class ViewDataContainer : IViewDataContainer
{
public ViewDataContainer()
{
ViewData = new ViewDataDictionary();
}
public ViewDataDictionary ViewData { get; set; }
}
/// <summary>
/// Creates a new IViewDataContainer but with a filtered ModelState
/// </summary>
/// <param name="container"></param>
/// <param name="prefix"></param>
/// <returns></returns>
public static IViewDataContainer FilterContainer(this IViewDataContainer container, string prefix)
{
var newContainer = new ViewDataContainer();
newContainer.ViewData.ModelState.Merge(container.ViewData.ModelState, prefix);
//change the html field name too
newContainer.ViewData.TemplateInfo.HtmlFieldPrefix = prefix;
return newContainer;
}
/// <summary>
/// Returns a new IViewContainer based on the current one but supplies a different model to the ViewData
/// </summary>
/// <param name="container"></param>
/// <param name="model"></param>
/// <returns></returns>
public static IViewDataContainer CopyWithModel(this IViewDataContainer container, object model)
{
return new ViewDataContainer
{
ViewData = new ViewDataDictionary(container.ViewData)
{
Model = model
}
};
}
}
}
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
internal static class ViewDataContainerExtensions
{
private class ViewDataContainer : IViewDataContainer
{
public ViewDataContainer()
{
ViewData = new ViewDataDictionary();
}
public ViewDataDictionary ViewData { get; set; }
}
/// <summary>
/// Creates a new IViewDataContainer but with a filtered ModelState
/// </summary>
/// <param name="container"></param>
/// <param name="prefix"></param>
/// <returns></returns>
public static IViewDataContainer FilterContainer(this IViewDataContainer container, string prefix)
{
var newContainer = new ViewDataContainer();
newContainer.ViewData.ModelState.Merge(container.ViewData.ModelState, prefix);
//change the html field name too
newContainer.ViewData.TemplateInfo.HtmlFieldPrefix = prefix;
return newContainer;
}
/// <summary>
/// Returns a new IViewContainer based on the current one but supplies a different model to the ViewData
/// </summary>
/// <param name="container"></param>
/// <param name="model"></param>
/// <returns></returns>
public static IViewDataContainer CopyWithModel(this IViewDataContainer container, object model)
{
return new ViewDataContainer
{
ViewData = new ViewDataDictionary(container.ViewData)
{
Model = model
}
};
}
}
}

View File

@@ -1,18 +1,18 @@
using System.Web.Mvc;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
internal static class ViewDataDictionaryExtensions
{
/// <summary>
/// Merges the source view data into the destination view data
/// </summary>
/// <param name="destination"></param>
/// <param name="source"></param>
public static void MergeViewDataFrom(this ViewDataDictionary destination, ViewDataDictionary source)
{
destination.MergeLeft(source);
}
}
}
using System.Web.Mvc;
using Umbraco.Core;
namespace Umbraco.Web.Mvc
{
internal static class ViewDataDictionaryExtensions
{
/// <summary>
/// Merges the source view data into the destination view data
/// </summary>
/// <param name="destination"></param>
/// <param name="source"></param>
public static void MergeViewDataFrom(this ViewDataDictionary destination, ViewDataDictionary source)
{
destination.MergeLeft(source);
}
}
}