From 4a72e8c3df0bdf7d9000000d43085ebd5fb6e51a Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 6 Jan 2015 17:39:07 +1100 Subject: [PATCH] Creates ReflectedFixedRazorViewEngine and version checkers to dynamically update code depending on the major versions of MVC and WebApi --- build/NuSpecs/UmbracoCms.Core.nuspec | 10 +- build/UmbracoVersion.txt | 2 +- src/Umbraco.Web/Mvc/MvcVersionCheck.cs | 10 ++ src/Umbraco.Web/Mvc/PluginViewEngine.cs | 9 +- .../Mvc/ReflectedFixedRazorViewEngine.cs | 98 +++++++++++++++++++ src/Umbraco.Web/Mvc/RenderViewEngine.cs | 4 +- .../Trees/ApplicationTreeExtensions.cs | 24 ++++- src/Umbraco.Web/Umbraco.Web.csproj | 3 + src/Umbraco.Web/WebApi/MvcVersionCheck.cs | 10 ++ 9 files changed, 156 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Web/Mvc/MvcVersionCheck.cs create mode 100644 src/Umbraco.Web/Mvc/ReflectedFixedRazorViewEngine.cs create mode 100644 src/Umbraco.Web/WebApi/MvcVersionCheck.cs diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index c680dce419..b003aca2cb 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -15,11 +15,11 @@ en-US umbraco - - - - - + + + + + diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 85a02bb894..ed2ae80c6e 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,2 +1,2 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) -7.2.1 \ No newline at end of file +7.3.0 \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/MvcVersionCheck.cs b/src/Umbraco.Web/Mvc/MvcVersionCheck.cs new file mode 100644 index 0000000000..348ce05d7d --- /dev/null +++ b/src/Umbraco.Web/Mvc/MvcVersionCheck.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Web.Mvc +{ + internal class MvcVersionCheck + { + public static System.Version MvcVersion + { + get { return typeof (System.Web.Mvc.Controller).Assembly.GetName().Version; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/PluginViewEngine.cs b/src/Umbraco.Web/Mvc/PluginViewEngine.cs index 6c0d80d058..cc4174b76a 100644 --- a/src/Umbraco.Web/Mvc/PluginViewEngine.cs +++ b/src/Umbraco.Web/Mvc/PluginViewEngine.cs @@ -1,15 +1,16 @@ using System.IO; using System.Linq; using System.Web.Mvc; +using Lucene.Net.Util; using Microsoft.Web.Mvc; using Umbraco.Core.IO; namespace Umbraco.Web.Mvc { - /// + /// /// A view engine to look into the App_Plugins folder for views for packaged controllers /// - public class PluginViewEngine : FixedRazorViewEngine + public class PluginViewEngine : ReflectedFixedRazorViewEngine { /// @@ -47,9 +48,9 @@ namespace Umbraco.Web.Mvc }) .ToArray(); - AreaMasterLocationFormats = viewLocationsArray; + AreaMasterLocationFormats = viewLocationsArray; - AreaPartialViewLocationFormats = new[] + AreaPartialViewLocationFormats = new[] { //will be used when we have partial view and child action macros string.Concat(SystemDirectories.AppPlugins, "/{2}/Views/Partials/{0}.cshtml"), diff --git a/src/Umbraco.Web/Mvc/ReflectedFixedRazorViewEngine.cs b/src/Umbraco.Web/Mvc/ReflectedFixedRazorViewEngine.cs new file mode 100644 index 0000000000..5cb082c032 --- /dev/null +++ b/src/Umbraco.Web/Mvc/ReflectedFixedRazorViewEngine.cs @@ -0,0 +1,98 @@ +using System; +using System.Reflection; +using System.Web.Mvc; + +namespace Umbraco.Web.Mvc +{ + /// + /// This is here to support compatibility with both MVC4 and MVC5 + /// + public abstract class ReflectedFixedRazorViewEngine : IViewEngine + { + protected ReflectedFixedRazorViewEngine() + { + if (MvcVersionCheck.MvcVersion >= System.Version.Parse("5.0.0")) + { + _wrappedEngine = new RazorViewEngine(); + } + else + { + var assembly = Assembly.Load("Microsoft.Web.Mvc.FixedDisplayModes"); + var engineType = assembly.GetType("Microsoft.Web.Mvc.FixedRazorViewEngine"); + _wrappedEngine = (IViewEngine)Activator.CreateInstance(engineType); + } + } + + public string[] ViewLocationFormats + { + get { return _viewLocationFormats; } + set + { + _wrappedEngine.GetType().GetProperty("ViewLocationFormats").SetValue(_wrappedEngine, value); + _viewLocationFormats = value; + } + } + + public string[] PartialViewLocationFormats + { + get { return _partialViewLocationFormats; } + set + { + _wrappedEngine.GetType().GetProperty("PartialViewLocationFormats").SetValue(_wrappedEngine, value); + _partialViewLocationFormats = value; + } + } + + public string[] AreaViewLocationFormats + { + get { return _areaViewLocationFormats; } + set + { + _wrappedEngine.GetType().GetProperty("AreaViewLocationFormats").SetValue(_wrappedEngine, value); + _areaViewLocationFormats = value; + } + } + + public string[] AreaMasterLocationFormats + { + get { return _areaMasterLocationFormats; } + set + { + _wrappedEngine.GetType().GetProperty("AreaMasterLocationFormats").SetValue(_wrappedEngine, value); + _areaMasterLocationFormats = value; + } + } + + public string[] AreaPartialViewLocationFormats + { + get { return _areaPartialViewLocationFormats; } + set + { + _wrappedEngine.GetType().GetProperty("AreaPartialViewLocationFormats").SetValue(_wrappedEngine, value); + _areaPartialViewLocationFormats = value; + } + } + + private readonly IViewEngine _wrappedEngine; + private string[] _areaViewLocationFormats; + private string[] _areaMasterLocationFormats; + private string[] _areaPartialViewLocationFormats; + private string[] _viewLocationFormats; + private string[] _partialViewLocationFormats; + + public virtual ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) + { + return _wrappedEngine.FindPartialView(controllerContext, partialViewName, useCache); + } + + public virtual ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) + { + return _wrappedEngine.FindView(controllerContext, viewName, masterName, useCache); + } + + public void ReleaseView(ControllerContext controllerContext, IView view) + { + _wrappedEngine.ReleaseView(controllerContext, view); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/RenderViewEngine.cs b/src/Umbraco.Web/Mvc/RenderViewEngine.cs index 960ff1bb71..794eecf0ca 100644 --- a/src/Umbraco.Web/Mvc/RenderViewEngine.cs +++ b/src/Umbraco.Web/Mvc/RenderViewEngine.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.Mvc /// 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. /// - public class RenderViewEngine : FixedRazorViewEngine + public class RenderViewEngine : ReflectedFixedRazorViewEngine { private readonly IEnumerable _supplementedViewLocations = new[] { "/{0}.cshtml" }; @@ -33,7 +33,7 @@ namespace Umbraco.Web.Mvc var replacePartialWithUmbracoFolder = _supplementedPartialViewLocations.ForEach(location => templateFolder + location); //The Render view engine doesn't support Area's so make those blank - ViewLocationFormats = replaceWithUmbracoFolder.ToArray(); + ViewLocationFormats = replaceWithUmbracoFolder.ToArray(); PartialViewLocationFormats = replacePartialWithUmbracoFolder.ToArray(); AreaPartialViewLocationFormats = new string[] { }; diff --git a/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs index 8142637d02..c1e7f47846 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs @@ -78,6 +78,7 @@ namespace Umbraco.Web.Trees var proxiedRouteData = new HttpRouteData( controllerContext.RouteData.Route, new HttpRouteValueDictionary(new {action = "GetRootNode", controller = ControllerExtensions.GetControllerName(instance.GetType())})); + //create a proxied controller context var proxiedControllerContext = new HttpControllerContext( controllerContext.Configuration, @@ -86,10 +87,29 @@ namespace Umbraco.Web.Trees { ControllerDescriptor = new HttpControllerDescriptor(controllerContext.ControllerDescriptor.Configuration, ControllerExtensions.GetControllerName(instance.GetType()), instance.GetType()) }; - + + if (WebApiVersionCheck.WebApiVersion >= Version.Parse("5.0.0")) + { + //In WebApi2, this is required to be set: + // proxiedControllerContext.RequestContext = controllerContext.RequestContext + // but we need to do this with reflection because of codebase changes between version 4/5 + var controllerContextRequestContext = controllerContext.GetType().GetProperty("RequestContext").GetValue(controllerContext); + proxiedControllerContext.GetType().GetProperty("RequestContext").SetValue(proxiedControllerContext, controllerContextRequestContext); + } + instance.ControllerContext = proxiedControllerContext; instance.Request = controllerContext.Request; - + + if (WebApiVersionCheck.WebApiVersion >= Version.Parse("5.0.0")) + { + //now we can change the request context's route data to be the proxied route data - NOTE: we cannot do this directly above + // because it will detect that the request context is different throw an exception. This is a change in webapi2 and we need to set + // this with reflection due to codebase changes between version 4/5 + // instance.RequestContext.RouteData = proxiedRouteData; + var instanceRequestContext = instance.GetType().GetProperty("RequestContext").GetValue(instance); + instanceRequestContext.GetType().GetProperty("RouteData").SetValue(instanceRequestContext, proxiedRouteData); + } + //invoke auth filters for this sub request var result = await instance.ControllerContext.InvokeAuthorizationFiltersForRequest(); //if a result is returned it means they are unauthorized, just throw the response. diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 21ac991bbc..9c9c8703bf 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -270,6 +270,8 @@ + + @@ -810,6 +812,7 @@ + diff --git a/src/Umbraco.Web/WebApi/MvcVersionCheck.cs b/src/Umbraco.Web/WebApi/MvcVersionCheck.cs new file mode 100644 index 0000000000..c2f2580c95 --- /dev/null +++ b/src/Umbraco.Web/WebApi/MvcVersionCheck.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Web.WebApi +{ + internal class WebApiVersionCheck + { + public static System.Version WebApiVersion + { + get { return typeof(System.Web.Http.ApiController).Assembly.GetName().Version; } + } + } +} \ No newline at end of file