Merge branch temp8 into temp8-di
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
156
src/Umbraco.Web/Mvc/Strings.Designer.cs
generated
156
src/Umbraco.Web/Mvc/Strings.Designer.cs
generated
@@ -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 <?xml version="1.0"?>
|
||||
///<configuration>
|
||||
///
|
||||
/// <configSections>
|
||||
/// <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
/// <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
/// <section name="page [rest of string was truncated]";.
|
||||
/// </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 <?xml version="1.0"?>
|
||||
///<configuration>
|
||||
///
|
||||
/// <configSections>
|
||||
/// <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
/// <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
/// <section name="page [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string WebConfigTemplate {
|
||||
get {
|
||||
return ResourceManager.GetString("WebConfigTemplate", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// };
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user