Added initial MVC supporting files and updated some unit tests.

This commit is contained in:
shannon@ShandemVaio
2012-08-07 21:40:34 +06:00
parent d9eb8b426c
commit d92d309c61
22 changed files with 793 additions and 64 deletions

View File

@@ -17,9 +17,15 @@ namespace Umbraco.Core
{
private DisposableTimer _timer;
private bool _isInitialized = false;
private bool _isStarted = false;
private bool _isComplete = false;
public virtual IBootManager Initialize()
{
if (_isInitialized)
throw new InvalidOperationException("The boot manager has already been initialized");
LogHelper.Info<CoreBootManager>("Umbraco application starting");
_timer = DisposableTimer.Start(x => LogHelper.Info<CoreBootManager>("Umbraco application startup complete" + " (took " + x + "ms)"));
@@ -30,6 +36,9 @@ namespace Umbraco.Core
};
InitializeResolvers();
_isInitialized = true;
return this;
}
@@ -40,10 +49,16 @@ namespace Umbraco.Core
/// <returns></returns>
public virtual IBootManager Startup(Action<ApplicationContext> afterStartup)
{
if (_isStarted)
throw new InvalidOperationException("The boot manager has already been initialized");
if (afterStartup != null)
{
afterStartup(ApplicationContext.Current);
}
}
_isStarted = true;
return this;
}
@@ -54,6 +69,9 @@ namespace Umbraco.Core
/// <returns></returns>
public virtual IBootManager Complete(Action<ApplicationContext> afterComplete)
{
if (_isComplete)
throw new InvalidOperationException("The boot manager has already been completed");
//freeze resolution to not allow Resolvers to be modified
Resolution.Freeze();
@@ -64,7 +82,9 @@ namespace Umbraco.Core
{
afterComplete(ApplicationContext.Current);
}
_isComplete = true;
return this;
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml;
using NUnit.Framework;
using SqlCE4Umbraco;
@@ -92,6 +93,42 @@ namespace Umbraco.Tests
}
}
/// <summary>
/// Initlializes the UmbracoContext with specific XML
/// </summary>
/// <param name="umbracoContext"></param>
/// <param name="template"></param>
private void SetupUmbracoContextForTest(UmbracoContext umbracoContext, Template template)
{
umbracoContext.GetXmlDelegate = () =>
{
var xDoc = new XmlDocument();
//create a custom xml structure to return
xDoc.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?><!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
]>
<root id=""-1"">
<Home id=""1046"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""2"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1046"" isDoc=""""><content><![CDATA[]]></content>
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""1"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc=""""><content><![CDATA[]]></content>
<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""1"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc=""""><content><![CDATA[]]></content>
</Home>
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""2"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc=""""><content><![CDATA[]]></content>
</Home>
</Home>
<Home id=""1175"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""2"" createDate=""2012-07-20T18:08:01"" updateDate=""2012-07-20T18:49:32"" nodeName=""Sub 2"" urlName=""sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1175"" isDoc=""""><content><![CDATA[]]></content>
</Home>
</Home>
<Home id=""1172"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test"" writerName=""admin"" creatorName=""admin"" path=""-1,1172"" isDoc="""" />
</root>");
//return the custom x doc
return xDoc;
};
}
[TestCase("/umbraco_client/Tree/treeIcons.css", false)]
[TestCase("/umbraco_client/Tree/Themes/umbraco/style.css?cdv=37", false)]
[TestCase("/umbraco_client/scrollingmenu/style.css?cdv=37", false)]
@@ -130,14 +167,19 @@ namespace Umbraco.Tests
Assert.AreEqual(assert, result);
}
[TestCase("/", true)]
[TestCase("/home.aspx", true)]
[TestCase("/home.aspx?altTemplate=blah", true)]
public void Process_Front_End_Document_Request(string url, bool assert)
//NOTE: This test shows how we can test most of the HttpModule, it however is testing too much,
// we need to write unit tests for each of the components: NiceUrlProvider, all of the Lookup classes, etc...
// to ensure that each one is individually tested.
[TestCase("/", 1046)]
[TestCase("/home.aspx", 1046)]
[TestCase("/home/sub1.aspx", 1173)]
[TestCase("/home.aspx?altTemplate=blah", 1046)]
public void Process_Front_End_Document_Request_Match_Node(string url, int nodeId)
{
var httpContextFactory = new FakeHttpContextFactory(url);
var httpContext = httpContextFactory.HttpContext;
var umbracoContext = new UmbracoContext(httpContext, ApplicationContext.Current, new DefaultRoutesCache(false));
var umbracoContext = new UmbracoContext(httpContext, ApplicationContext.Current, new NullRoutesCache());
var contentStore = new ContentStore(umbracoContext);
var niceUrls = new NiceUrlProvider(contentStore, umbracoContext);
umbracoContext.RoutingContext = new RoutingContext(
@@ -159,43 +201,46 @@ namespace Umbraco.Tests
_module.AssignDocumentRequest(httpContext, umbracoContext, httpContext.Request.Url);
Assert.AreEqual(assert, umbracoContext.DocumentRequest != null);
if (assert)
{
Assert.AreEqual(assert, umbracoContext.DocumentRequest.Node != null);
}
Assert.IsNotNull(umbracoContext.DocumentRequest);
Assert.IsNotNull(umbracoContext.DocumentRequest.Node);
Assert.IsFalse(umbracoContext.DocumentRequest.IsRedirect);
Assert.IsFalse(umbracoContext.DocumentRequest.Is404);
Assert.AreEqual(umbracoContext.DocumentRequest.Culture, Thread.CurrentThread.CurrentCulture);
Assert.AreEqual(umbracoContext.DocumentRequest.Culture, Thread.CurrentThread.CurrentUICulture);
Assert.AreEqual(nodeId, umbracoContext.DocumentRequest.NodeId);
}
private void SetupUmbracoContextForTest(UmbracoContext umbracoContext, Template template)
/// <summary>
/// Used for testing, does not cache anything
/// </summary>
private class NullRoutesCache : IRoutesCache
{
umbracoContext.GetXmlDelegate = () =>
public void Store(int nodeId, string route)
{
var xDoc = new XmlDocument();
}
//create a custom xml structure to return
public string GetRoute(int nodeId)
{
return null; //default;
}
xDoc.LoadXml(@"<?xml version=""1.0"" encoding=""utf-8""?><!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
public int GetNodeId(string route)
{
return 0; //default;
}
]>
<root id=""-1"">
<Home id=""1046"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""2"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1046"" isDoc=""""><content><![CDATA[]]></content>
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""1"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc=""""><content><![CDATA[]]></content>
<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""1"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc=""""><content><![CDATA[]]></content>
</Home>
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""2"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc=""""><content><![CDATA[]]></content>
</Home>
</Home>
<Home id=""1175"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""2"" createDate=""2012-07-20T18:08:01"" updateDate=""2012-07-20T18:49:32"" nodeName=""Sub 2"" urlName=""sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1175"" isDoc=""""><content><![CDATA[]]></content>
</Home>
</Home>
<Home id=""1172"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + template.Id + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test"" writerName=""admin"" creatorName=""admin"" path=""-1,1172"" isDoc="""" />
</root>");
//return the custom x doc
return xDoc;
};
public void ClearNode(int nodeId)
{
}
public void Clear()
{
}
}
}
}

View File

@@ -5,19 +5,6 @@
<appender-ref ref="ConsoleAppender" />
</root>
<logger name="Umbraco.Framework.Persistence.NHibernate">
<level value="WARN" />
</logger>
<logger name="Umbraco.Hive.Configuration.ProviderMappingGroup">
<level value="WARN" />
</logger>
<!-- Don't log every task trigger by default -->
<logger name="Umbraco.Framework.Tasks.ApplicationTaskManager">
<level value="WARN" />
</logger>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" >
<target value="Console.Out" />
<layout type="log4net.Layout.PatternLayout">

View File

@@ -104,6 +104,8 @@ namespace Umbraco.Web
return CreateXpathQuery(startNodeId, path, GlobalSettings.HideTopLevelNodeFromPath);
}
protected string CreateXpathQuery(int startNodeId, string path, bool hideTopLevelNodeFromPath)
{
string xpath;
@@ -131,7 +133,7 @@ namespace Umbraco.Web
int partsIndex = 0;
xpathBuilder.Append("/root");
if (startNodeId == 0)
{
if (hideTopLevelNodeFromPath)

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Web.Mvc
{
internal static class Constants
{
public const string ViewLocation = "~/Views";
}
}

View File

@@ -1,8 +1,9 @@
using System;
using System.Threading;
namespace Umbraco.Web.Mvc
{
public static class ControllerExtensions
public static class ControllerExtensions
{
/// <summary>
/// Return the controller name from the controller type

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Resolving;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// A resolver for storing IFilteredControllerFactories
/// </summary>
internal sealed class FilteredControllerFactoriesResolver : ManyObjectsResolverBase<FilteredControllerFactoriesResolver, IFilteredControllerFactory>
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="factories"></param>
internal FilteredControllerFactoriesResolver(IEnumerable<Type> factories)
: base(factories)
{
}
public IEnumerable<IFilteredControllerFactory> Factories
{
get { return Values; }
}
}
}

View File

@@ -0,0 +1,17 @@
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
namespace Umbraco.Web.Mvc
{
public interface IFilteredControllerFactory : IControllerFactory
{
/// <summary>
/// Determines whether this instance can handle the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns><c>true</c> if this instance can handle the specified request; otherwise, <c>false</c>.</returns>
/// <remarks></remarks>
bool CanHandle(RequestContext request);
}
}

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
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 FilteredControllerFactoriesResolver _slaveFactories;
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
private readonly ConcurrentDictionary<string, Type> _controllerCache = new ConcurrentDictionary<string, Type>();
public MasterControllerFactory(FilteredControllerFactoriesResolver factoryResolver)
{
_slaveFactories = factoryResolver;
}
/// <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 = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext));
return factory != null
? factory.CreateController(requestContext, controllerName)
: base.CreateController(requestContext, controllerName);
}
/// <summary>
/// Releases the specified controller.
/// </summary>
/// <param name="controller">The controller to release.</param>
/// <remarks></remarks>
public override void ReleaseController(IController controller)
{
using (new WriteLock(_locker))
{
bool released = false;
if (controller is Controller)
{
var requestContext = ((Controller)controller).ControllerContext.RequestContext;
var factory = _slaveFactories.Factories.FirstOrDefault(x => x.CanHandle(requestContext));
if (factory != null)
{
factory.ReleaseController(controller);
released = true;
}
}
if (!released) base.ReleaseController(controller);
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Web.Mvc;
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 : ControllerActionInvoker
{
/// <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 RenderMvcController
if (controllerContext.Controller is RenderMvcController)
{
return new ReflectedActionDescriptor(controllerContext.Controller.GetType().GetMethod("Index"), "Index", controllerDescriptor);
}
}
return ad;
}
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.SessionState;
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 : IFilteredControllerFactory
{
private readonly OverridenDefaultControllerFactory _innerFactory = new OverridenDefaultControllerFactory();
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public RenderControllerFactory()
{
}
/// <summary>
/// Determines whether this instance can handle the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns><c>true</c> if this instance can handle the specified request; otherwise, <c>false</c>.</returns>
/// <remarks></remarks>
public bool CanHandle(RequestContext request)
{
var dataToken = request.RouteData.DataTokens["area"];
return dataToken == null || string.IsNullOrWhiteSpace(dataToken.ToString());
}
/// <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 IController CreateController(RequestContext requestContext, string controllerName)
{
Type controllerType = _innerFactory.GetControllerType(requestContext, controllerName) ??
_innerFactory.GetControllerType(requestContext, ControllerExtensions.GetControllerName(typeof(RenderMvcController)));
return _innerFactory.GetControllerInstance(requestContext, controllerType);
}
/// <summary>
/// Gets the controller's session behavior.
/// </summary>
/// <returns>
/// The controller's session behavior.
/// </returns>
/// <param name="requestContext">The request context.</param><param name="controllerName">The name of the controller whose session behavior you want to get.</param>
public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
{
return ((IControllerFactory)_innerFactory).GetControllerSessionBehavior(requestContext, controllerName);
}
/// <summary>
/// Releases the specified controller.
/// </summary>
/// <param name="controller">The controller.</param>
public void ReleaseController(IController controller)
{
_innerFactory.ReleaseController(controller);
}
/// <summary>
/// By default, <see cref="DefaultControllerFactory"/> only exposes <see cref="IControllerFactory.CreateController"/> which throws an exception
/// if the controller is not found. Since we want to try creating a controller, and then fall back to <see cref="RenderMvcController"/> if one isn't found,
/// this nested class changes the visibility of <see cref="DefaultControllerFactory"/>'s internal methods in order to not have to rely on a try-catch.
/// </summary>
/// <remarks></remarks>
public class OverridenDefaultControllerFactory : DefaultControllerFactory
{
public new IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return base.GetControllerInstance(requestContext, controllerType);
}
public new Type GetControllerType(RequestContext requestContext, string controllerName)
{
return base.GetControllerType(requestContext, controllerName);
}
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Xml;
using umbraco;
using umbraco.interfaces;
namespace Umbraco.Web.Mvc
{
public class RenderModel
{
public XmlNode CurrentXmlNode { get; set; }
public INode CurrentNode { get; set; }
//TODO: We probably want to change this!
internal page UmbracoPage { get; set; }
}
}

View File

@@ -0,0 +1,30 @@
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
public class RenderModelBinder : IModelBinder
{
/// <summary>
/// Binds the model to a value by using the specified controller context and binding context.
/// </summary>
/// <returns>
/// The bound value.
/// </returns>
/// <param name="controllerContext">The controller context.</param><param name="bindingContext">The binding context.</param>
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var requestMatchesType = typeof(RenderModel) == bindingContext.ModelType;
if (requestMatchesType)
{
//get the model from the route data
if (!controllerContext.RouteData.DataTokens.ContainsKey("umbraco"))
return null;
var model = controllerContext.RouteData.DataTokens["umbraco"] as RenderModel;
return model;
}
return null;
}
}
}

View File

@@ -0,0 +1,28 @@
using System.IO;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
public class RenderMvcController : Controller
{
public RenderMvcController()
{
ActionInvoker = new RenderActionInvoker();
}
public virtual ActionResult Index(RenderModel model)
{
var template = ControllerContext.RouteData.Values["action"].ToString();
if (!System.IO.File.Exists(
Path.Combine(Server.MapPath(Constants.ViewLocation), template + ".cshtml")))
{
return Content("");
}
return View(template, model);
}
}
}

View File

@@ -0,0 +1,151 @@
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
using umbraco.cms.businesslogic.template;
namespace Umbraco.Web.Mvc
{
public class RenderRouteHandler : IRouteHandler
{
internal const string SingletonServiceName = "RenderRouteHandler";
public RenderRouteHandler(IControllerFactory controllerFactory)
{
_controllerFactory = controllerFactory;
}
private readonly IControllerFactory _controllerFactory;
#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)
{
//need to ensure some http items are set!
var path = requestContext.HttpContext.Request.QueryString["path"];
var qry = requestContext.HttpContext.Request.QueryString["qry"];
requestContext.HttpContext.Items["UmbPage"] = requestContext.HttpContext.Request.QueryString["path"];
requestContext.HttpContext.Items["VirtualUrl"] = string.Format("{0}{1}", path, qry);
//var handlerUrl = requestHandler.cleanUrl();
//var v4Handler = new requestHandler(UmbracoContext.Current.GetXml(), handlerUrl);
//var v4Page = new page(v4Handler.currentPage);
////this is a fix for this issue:
////https://bitbucket.org/Shandem/umbramvco/issue/1/need-to-set-pageid-in-conextitems
////to support some of the @Library methods:
//requestContext.HttpContext.Items["pageID"] = v4Page.PageID;
////this is a fix for issue:
//// https://bitbucket.org/Shandem/umbramvco/issue/2/current-culture
//// make sure we have the correct culture
//var tempCulture = v4Page.GetCulture();
//if (tempCulture != "")
//{
// Thread.CurrentThread.CurrentCulture =
// System.Globalization.CultureInfo.CreateSpecificCulture(tempCulture);
// Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
//}
//var renderModel = new RenderModel()
//{
// CurrentNode = new Node(v4Handler.currentPage),
// CurrentXmlNode = v4Handler.currentPage,
// UmbracoPage = v4Page
//};
////put the render model into the current RouteData
//requestContext.RouteData.DataTokens.Add("umbraco", renderModel);
//return GetHandlerForRoute(requestContext, renderModel);
return null;
}
#endregion
/// <summary>
/// Returns a RouteDefinition object based on the current renderModel
/// </summary>
/// <param name="requestContext"></param>
/// <param name="renderModel"></param>
/// <returns></returns>
protected virtual RouteDefinition GetUmbracoRouteDefinition(RequestContext requestContext, RenderModel renderModel)
{
//creates the default route definition which maps to the 'UmbracoController' controller
var def = new RouteDefinition
{
ControllerName = ControllerExtensions.GetControllerName<RenderMvcController>(),
Controller = new RenderMvcController(),
RenderModel = renderModel,
ActionName = ((Route)requestContext.RouteData.Route).Defaults["action"].ToString()
};
var templateId = renderModel.UmbracoPage.Template;
//check that a template is defined)
if (templateId > 0)
{
//check if there's a custom controller assigned, base on the document type alias.
var controller = _controllerFactory.CreateController(requestContext, renderModel.UmbracoPage.NodeTypeAlias);
//check if that controller exists
if (controller != null)
{
//ensure the controller is of type 'UmbracoController'
if (controller is RenderMvcController)
{
//set the controller and name to the custom one
def.Controller = (ControllerBase)controller;
def.ControllerName = ControllerExtensions.GetControllerName(controller.GetType());
}
else
{
//LogHelper.Warn<RenderRouteHandler>("The current Document Type {0} matches a locally declared controller of type {1}. Custom Controllers for Umbraco routing must inherit from '{2}'.", renderModel.CurrentNode.ContentType.Alias, controller.GetType().FullName, typeof(UmbracoController).FullName);
//exit as we cannnot route to the custom controller, just route to the standard one.
return def;
}
var template = Template.GetTemplate(templateId);
if (template != null)
{
//check if the custom controller has an action with the same name as the template name (we convert ToUmbracoAlias since the template name might have invalid chars).
//NOTE: This also means that all custom actions MUST be PascalCase.. but that should be standard.
var templateName = template.Alias.Split('.')[0].ToUmbracoAlias(StringAliasCaseType.PascalCase);
def.ActionName = templateName;
}
}
}
return def;
}
/// <summary>
/// this will determine the controller and set the values in the route data
/// </summary>
/// <param name="requestContext"></param>
/// <param name="renderModel"></param>
protected internal IHttpHandler GetHandlerForRoute(RequestContext requestContext, RenderModel renderModel)
{
var routeDef = GetUmbracoRouteDefinition(requestContext, renderModel);
//no post values, just route to the controller/action requried (local)
requestContext.RouteData.Values["controller"] = routeDef.ControllerName;
if (!string.IsNullOrWhiteSpace(routeDef.ActionName))
{
requestContext.RouteData.Values["action"] = routeDef.ActionName;
}
return new MvcHandler(requestContext);
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Umbraco.Core;
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 : RazorViewEngine
{
private readonly IEnumerable<string> _supplementedViewLocations = new[] { "/{0}.cshtml" };
private readonly IEnumerable<string> _supplementedPartialViewLocations = new[] { "/{0}.cshtml", "/Partials/{0}.cshtml", "/MacroPartials/{0}.cshtml" };
/// <summary>
/// Constructor
/// </summary>
public RenderViewEngine()
{
const string templateFolder = Constants.ViewLocation;
var replaceWithUmbracoFolder = _supplementedViewLocations.ForEach(location => templateFolder + location);
var replacePartialWithUmbracoFolder = _supplementedPartialViewLocations.ForEach(location => templateFolder + location);
//The Render view engine doesn't support Area's so make those blank
ViewLocationFormats = replaceWithUmbracoFolder.ToArray();
PartialViewLocationFormats = replacePartialWithUmbracoFolder.ToArray();
AreaPartialViewLocationFormats = new string[] { };
AreaViewLocationFormats = new string[] { };
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (!ShouldFindView(controllerContext, false))
{
return new ViewEngineResult(new string[] { });
}
return base.FindView(controllerContext, viewName, masterName, useCache);
}
public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
if (!ShouldFindView(controllerContext, true))
{
return new ViewEngineResult(new string[] { });
}
return base.FindPartialView(controllerContext, partialViewName, useCache);
}
/// <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 bool ShouldFindView(ControllerContext controllerContext, bool isPartial)
{
//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
&& controllerContext.RouteData.DataTokens.ContainsKey("umbraco")
&& !(controllerContext.RouteData.DataTokens["umbraco"] is RenderModel))
{
return true;
}
//only find views if we're rendering the umbraco front end
if (controllerContext.RouteData.DataTokens.ContainsKey("umbraco")
&& controllerContext.RouteData.DataTokens["umbraco"] != null
&& controllerContext.RouteData.DataTokens["umbraco"] is RenderModel)
{
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
/// <summary>
/// The View that front-end templates inherit from
/// </summary>
public abstract class RenderViewPage : WebViewPage<RenderModel>
{
//protected RenderViewPage()
//{
//}
//protected override void InitializePage()
//{
// //set the model to the current node if it is not set, this is generally not the case
// if (Model != null)
// {
// //this.ViewData.Model = Model;
// var backingItem = new DynamicBackingItem(Model.CurrentNode);
// var dynamicNode = new DynamicNode(backingItem);
// CurrentPage = dynamicNode;
// }
//}
//public dynamic CurrentPage { get; private set; }
//private ICultureDictionary _cultureDictionary;
//public string GetDictionary(string key)
//{
// if (_cultureDictionary == null)
// {
// _cultureDictionary = new UmbracoCultureDictionary();
// }
// return _cultureDictionary[key];
//}
//private RazorLibraryCore _library;
//public RazorLibraryCore Library
//{
// get { return _library ?? (_library = new RazorLibraryCore(Model.CurrentNode)); }
//}
}
}

View File

@@ -0,0 +1,23 @@
using System.Web.Mvc;
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 instance found for routing to
/// </summary>
public ControllerBase Controller { get; set; }
/// <summary>
/// The current RenderModel found for the request
/// </summary>
public object RenderModel { get; set; }
}
}

View File

@@ -65,7 +65,7 @@ namespace Umbraco.Web.Routing
? null
: _umbracoContext.RoutesCache.GetRoute(nodeId);
if (route != null)
if (!string.IsNullOrEmpty(route))
{
// route is <id>/<path> eg "-1/", "-1/foo", "123/", "123/foo/bar"...
int pos = route.IndexOf('/');

View File

@@ -239,6 +239,19 @@
<Link>Properties\SolutionInfo.cs</Link>
</Compile>
<Compile Include="ContentStore.cs" />
<Compile Include="Mvc\Constants.cs" />
<Compile Include="Mvc\FilteredControllerFactoriesResolver.cs" />
<Compile Include="Mvc\IFilteredControllerFactory.cs" />
<Compile Include="Mvc\MasterControllerFactory.cs" />
<Compile Include="Mvc\RenderActionInvoker.cs" />
<Compile Include="Mvc\RenderControllerFactory.cs" />
<Compile Include="Mvc\RenderModel.cs" />
<Compile Include="Mvc\RenderModelBinder.cs" />
<Compile Include="Mvc\RenderMvcController.cs" />
<Compile Include="Mvc\RenderRouteHandler.cs" />
<Compile Include="Mvc\RenderViewEngine.cs" />
<Compile Include="Mvc\RenderViewPage.cs" />
<Compile Include="Mvc\RouteDefinition.cs" />
<Compile Include="WebBootManager.cs" />
<Compile Include="LegacyRequestInitializer.cs" />
<Compile Include="Mvc\ControllerExtensions.cs" />

View File

@@ -26,9 +26,8 @@ namespace Umbraco.Web
public class UmbracoModule : IHttpModule
{
/// <summary>
/// Checks if the current request should process the request as a front-end umbraco request, if this is tru
/// it then creates the DocumentRequest object, finds the document, domain and culture and stores this back
/// to the UmbracoContext
/// Assogms a new DocumentRequest to the UmbracoContext and finds the document,
/// domain and culture associated with the current front-end request.
/// </summary>
/// <param name="httpContext"></param>
/// <param name="umbracoContext"> </param>

View File

@@ -1,6 +1,10 @@
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Web.Media.ThumbnailProviders;
using Umbraco.Web.Mvc;
using Umbraco.Web.Routing;
using umbraco.businesslogic;
@@ -29,6 +33,24 @@ namespace Umbraco.Web
ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency";
ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings;
//set master controller factory
ControllerBuilder.Current.SetControllerFactory(
new MasterControllerFactory(FilteredControllerFactoriesResolver.Current));
//set the render view engine
ViewEngines.Engines.Add(new RenderViewEngine());
//set model binder
ModelBinders.Binders.Add(new KeyValuePair<Type, IModelBinder>(typeof(RenderModel), new RenderModelBinder()));
//set routes
var route = RouteTable.Routes.MapRoute(
"Umbraco_default",
"Umbraco/RenderMvc/{action}/{id}",
new { controller = "RenderMvc", action = "Index", id = UrlParameter.Optional }
);
route.RouteHandler = new RenderRouteHandler(ControllerBuilder.Current.GetControllerFactory());
//find and initialize the application startup handlers
ApplicationStartupHandler.RegisterHandlers();
@@ -42,19 +64,27 @@ namespace Umbraco.Web
{
base.InitializeResolvers();
FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver(
//add all known factories, devs can then modify this list on application startup either by binding to events
//or in their own global.asax
new[]
{
typeof (RenderControllerFactory)
});
LastChanceLookupResolver.Current = new LastChanceLookupResolver(new DefaultLastChanceLookup());
DocumentLookupsResolver.Current = new DocumentLookupsResolver(
//add all known resolvers in the correct order, devs can then modify this list on application startup either by binding to events
//or in their own global.asax
new Type[]
{
typeof(LookupByNiceUrl),
typeof(LookupById),
typeof(LookupByNiceUrlAndTemplate),
typeof(LookupByProfile),
typeof(LookupByAlias)
});
new[]
{
typeof (LookupByNiceUrl),
typeof (LookupById),
typeof (LookupByNiceUrlAndTemplate),
typeof (LookupByProfile),
typeof (LookupByAlias)
});
RoutesCacheResolver.Current = new RoutesCacheResolver(new DefaultRoutesCache());