From 1c2f048402d93ee9fc41d6336583550d8e0a0fb2 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 3 Nov 2020 07:55:09 +0100 Subject: [PATCH 1/3] Migrated ProfilingViewEngine.cs Signed-off-by: Bjarke Berg --- .../ViewEngines/PluginViewEngine.cs | 48 +++++++++ .../ViewEngines/ProfilingViewEngine.cs | 45 +++++++++ .../ViewEngines}/RenderViewEngine.cs | 44 ++++----- src/Umbraco.Web/Mvc/PluginViewEngine.cs | 99 ------------------- src/Umbraco.Web/Mvc/ProfilingViewEngine.cs | 49 --------- .../Runtime/WebInitialComponent.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 3 - 7 files changed, 117 insertions(+), 175 deletions(-) create mode 100644 src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngine.cs rename src/{Umbraco.Web/Mvc => Umbraco.Web.Website/ViewEngines}/RenderViewEngine.cs (79%) delete mode 100644 src/Umbraco.Web/Mvc/PluginViewEngine.cs delete mode 100644 src/Umbraco.Web/Mvc/ProfilingViewEngine.cs diff --git a/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs new file mode 100644 index 0000000000..7c8deb9dd7 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs @@ -0,0 +1,48 @@ +using System.Diagnostics; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Umbraco.Web.Website.ViewEngines +{ + /// + /// A view engine to look into the App_Plugins folder for views for packaged controllers + /// + public class PluginViewEngine : RazorViewEngine + { + public PluginViewEngine( + IRazorPageFactoryProvider pageFactory, + IRazorPageActivator pageActivator, + HtmlEncoder htmlEncoder, + ILoggerFactory loggerFactory, + DiagnosticListener diagnosticListener) + : base(pageFactory, pageActivator, htmlEncoder, OverrideViewLocations(), loggerFactory, diagnosticListener) + { + } + + private static IOptions OverrideViewLocations() + { + return Options.Create(new RazorViewEngineOptions() + { + AreaViewLocationFormats = + { + //set all of the area view locations to the plugin folder + string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"), + string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"), + + //will be used when we have partial view and child action macros + string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/Partials/{0}.cshtml"), + string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/MacroPartials/{0}.cshtml"), + //for partialsCurrent. + string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"), + string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"), + }, + ViewLocationFormats = + { + string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"), + } + }); + } + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngine.cs new file mode 100644 index 0000000000..070c3f4e65 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngine.cs @@ -0,0 +1,45 @@ + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Umbraco.Core.Logging; + +namespace Umbraco.Web.Website.ViewEngines +{ + public class ProfilingViewEngine: IViewEngine + { + internal readonly IViewEngine Inner; + private readonly IProfiler _profiler; + private readonly string _name; + + public ProfilingViewEngine(IViewEngine inner, IProfiler profiler) + { + Inner = inner; + _profiler = profiler; + _name = inner.GetType().Name; + } + + public ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage) + { + using (_profiler.Step(string.Format("{0}.FindView, {1}, {2}", _name, viewName, isMainPage))) + { + return WrapResult(Inner.FindView(context, viewName, isMainPage)); + } + } + + private static ViewEngineResult WrapResult(ViewEngineResult innerResult) + { + var profiledResult = innerResult.View is null + ? ViewEngineResult.NotFound(innerResult.ViewName, innerResult.SearchedLocations) + : ViewEngineResult.Found(innerResult.ViewName, innerResult.View); + return profiledResult; + } + + public ViewEngineResult GetView(string executingFilePath, string viewPath, bool isMainPage) + { + using (_profiler.Step(string.Format("{0}.GetView, {1}, {2}, {3}", _name, executingFilePath, viewPath, isMainPage))) + { + return Inner.GetView(executingFilePath, viewPath, isMainPage); + } + } + } +} diff --git a/src/Umbraco.Web/Mvc/RenderViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs similarity index 79% rename from src/Umbraco.Web/Mvc/RenderViewEngine.cs rename to src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs index 5f508e8d61..2c81687091 100644 --- a/src/Umbraco.Web/Mvc/RenderViewEngine.cs +++ b/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Web.Mvc; -using Umbraco.Core.Composing; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.AspNetCore.Mvc.ViewEngines; using Umbraco.Core.Hosting; -using Umbraco.Core.IO; using Umbraco.Web.Models; -namespace Umbraco.Web.Mvc +namespace Umbraco.Web.Website.ViewEngines { /// /// A view engine to look into the template location specified in the config for the front-end/Rendering part of the cms, @@ -24,24 +24,24 @@ namespace Umbraco.Web.Mvc // http://issues.umbraco.org/issue/U4-1287, http://issues.umbraco.org/issue/U4-1215 private readonly IEnumerable _supplementedPartialViewLocations = new[] { "/Partials/{0}.cshtml", "/MacroPartials/{0}.cshtml", "/{0}.cshtml" }; - /// - /// Constructor - /// - public RenderViewEngine(IHostingEnvironment hostingEnvironment) - { - _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); - - 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(); - AreaViewLocationFormats = Array.Empty(); - - EnsureFoldersAndFiles(); - } + // /// + // /// Constructor + // /// + // public RenderViewEngine(IHostingEnvironment hostingEnvironment) + // { + // _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); + // + // 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(); + // AreaViewLocationFormats = Array.Empty(); + // + // EnsureFoldersAndFiles(); + // } /// /// Ensures that the correct web.config for razor exists in the /Views folder, the partials folder exist and the ViewStartPage exists. diff --git a/src/Umbraco.Web/Mvc/PluginViewEngine.cs b/src/Umbraco.Web/Mvc/PluginViewEngine.cs deleted file mode 100644 index aa81cb9a57..0000000000 --- a/src/Umbraco.Web/Mvc/PluginViewEngine.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.IO; -using System.Linq; -using System.Web.Mvc; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Mvc -{ - /// - /// A view engine to look into the App_Plugins folder for views for packaged controllers - /// - public class PluginViewEngine : RazorViewEngine - { - - /// - /// Constructor - /// - 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(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"), - }; - - //set all of the area view locations to the plugin folder - AreaViewLocationFormats = viewLocationsArray - .Concat(new[] - { - string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"), - }) - .ToArray(); - - AreaMasterLocationFormats = viewLocationsArray; - - AreaPartialViewLocationFormats = new[] - { - //will be used when we have partial view and child action macros - string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/Partials/{0}.cshtml"), - string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/MacroPartials/{0}.cshtml"), - //for partialsCurrent. - string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/{1}/{0}.cshtml"), - string.Concat(Core.Constants.SystemDirectories.AppPlugins, "/{2}/Views/Shared/{0}.cshtml"), - }; - - } - - /// - /// Ensures that the correct web.config for razor exists in the /Views folder. - /// - private void EnsureFolderAndWebConfig(ViewEngineResult result) - { - if (result.View == null) return; - var razorResult = result.View as RazorView; - if (razorResult == null) return; - - var folder = Path.GetDirectoryName(Current.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; - } - } -} diff --git a/src/Umbraco.Web/Mvc/ProfilingViewEngine.cs b/src/Umbraco.Web/Mvc/ProfilingViewEngine.cs deleted file mode 100644 index 097434541c..0000000000 --- a/src/Umbraco.Web/Mvc/ProfilingViewEngine.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Web.Mvc; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Mvc -{ - 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); - } - } - } -} diff --git a/src/Umbraco.Web/Runtime/WebInitialComponent.cs b/src/Umbraco.Web/Runtime/WebInitialComponent.cs index d02ccdd01e..10623a1ed8 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComponent.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComponent.cs @@ -78,7 +78,7 @@ namespace Umbraco.Web.Runtime viewEngines.Clear(); foreach (var engine in originalEngines) { - var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine); + var wrappedEngine = engine;// TODO introduce in NETCORE: is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine); viewEngines.Add(wrappedEngine); } } @@ -95,7 +95,7 @@ namespace Umbraco.Web.Runtime // set the render & plugin view engines ViewEngines.Engines.Add(new RenderViewEngine(_hostingEnvironment)); ViewEngines.Engines.Add(new PluginViewEngine()); - + ////add the profiling action filter //GlobalFilters.Filters.Add(new ProfilingActionFilter()); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a881e3f704..0d23f8d83b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -230,7 +230,6 @@ - @@ -295,7 +294,6 @@ - @@ -316,7 +314,6 @@ - From aefb596b4b35925a9bcc5e5d85b8cd0a4430951c Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 3 Nov 2020 14:14:01 +0100 Subject: [PATCH 2/3] More viewengine migration. Including wrapping in profiler etcs Signed-off-by: Bjarke Berg --- .../Constants-SystemDirectories.cs | 2 +- ...ewEngineWrapperMvcViewOptionsSetupTests.cs | 75 +++++++++++++++++++ .../Runtimes/WebRuntimeComponentTests.cs | 66 ---------------- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 - src/Umbraco.Web.UI.NetCore/appsettings.json | 2 +- .../Extensions/ActionContextExtensions.cs | 12 +-- ...racoWebstiteServiceCollectionExtensions.cs | 20 ++++- .../ViewEngines/IPluginViewEngine.cs | 8 ++ .../ViewEngines/IRenderViewEngine.cs | 8 ++ .../ViewEngines/PluginMvcViewOptionsSetup.cs | 26 +++++++ .../ViewEngines/PluginViewEngine.cs | 2 +- ...ingViewEngineWrapperMvcViewOptionsSetup.cs | 43 +++++++++++ .../ViewEngines/RenderMvcViewOptionsSetup.cs | 26 +++++++ .../ViewEngines/RenderViewEngine.cs | 54 ++++--------- .../Mvc/ControllerContextExtensions.cs | 2 +- .../Runtime/WebInitialComponent.cs | 4 +- 16 files changed, 225 insertions(+), 126 deletions(-) create mode 100644 src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetupTests.cs delete mode 100644 src/Umbraco.Tests/Runtimes/WebRuntimeComponentTests.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/IRenderViewEngine.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/PluginMvcViewOptionsSetup.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/RenderMvcViewOptionsSetup.cs diff --git a/src/Umbraco.Core/Constants-SystemDirectories.cs b/src/Umbraco.Core/Constants-SystemDirectories.cs index 53e14bb6ec..d4ca2a3c57 100644 --- a/src/Umbraco.Core/Constants-SystemDirectories.cs +++ b/src/Umbraco.Core/Constants-SystemDirectories.cs @@ -20,7 +20,7 @@ public const string AppCode = "~/App_Code"; - public const string AppPlugins = "~/App_Plugins"; + public const string AppPlugins = "/App_Plugins"; public const string MvcViews = "~/Views"; diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetupTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetupTests.cs new file mode 100644 index 0000000000..a74431b705 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetupTests.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Logging; +using Umbraco.Web.Website.ViewEngines; + +namespace Umbraco.Tests.Runtimes +{ + [TestFixture] + public class ProfilingViewEngineWrapperMvcViewOptionsSetupTests + { + private ProfilingViewEngineWrapperMvcViewOptionsSetup Sut => + new ProfilingViewEngineWrapperMvcViewOptionsSetup(Mock.Of()); + [Test] + public void WrapViewEngines_HasEngines_WrapsAll() + { + var options = new MvcViewOptions() + { + ViewEngines = + { + Mock.Of(), + Mock.Of(), + } + }; + + Sut.Configure(options); + + Assert.That(options.ViewEngines.Count, Is.EqualTo(2)); + Assert.That(options.ViewEngines[0], Is.InstanceOf()); + Assert.That(options.ViewEngines[1], Is.InstanceOf()); + } + + [Test] + public void WrapViewEngines_HasEngines_KeepsSortOrder() + { + var options = new MvcViewOptions() + { + ViewEngines = + { + Mock.Of(), + Mock.Of(), + } + }; + + Sut.Configure(options); + + Assert.That(options.ViewEngines.Count, Is.EqualTo(2)); + Assert.That(((ProfilingViewEngine)options.ViewEngines[0]).Inner, Is.InstanceOf()); + Assert.That(((ProfilingViewEngine)options.ViewEngines[1]).Inner, Is.InstanceOf()); + } + + [Test] + public void WrapViewEngines_HasProfiledEngine_AddsSameInstance() + { + var profiledEngine = new ProfilingViewEngine(Mock.Of(), Mock.Of()); + var options = new MvcViewOptions() + { + ViewEngines = + { + profiledEngine + } + }; + + Sut.Configure(options); + + Assert.That(options.ViewEngines[0], Is.SameAs(profiledEngine)); + } + + [Test] + public void WrapViewEngines_CollectionIsNull_DoesNotThrow() + { + Assert.DoesNotThrow(() => Sut.Configure(new MvcViewOptions())); + } + } +} diff --git a/src/Umbraco.Tests/Runtimes/WebRuntimeComponentTests.cs b/src/Umbraco.Tests/Runtimes/WebRuntimeComponentTests.cs deleted file mode 100644 index 42a0eeba54..0000000000 --- a/src/Umbraco.Tests/Runtimes/WebRuntimeComponentTests.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using NUnit.Framework; -using NUnit.Framework.Internal; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Mvc; -using Umbraco.Web.Runtime; - -namespace Umbraco.Tests.Runtimes -{ - [TestFixture] - public class WebRuntimeComponentTests - { - [Test] - public void WrapViewEngines_HasEngines_WrapsAll() - { - IList engines = new List - { - new RenderViewEngine(TestHelper.GetHostingEnvironment()), - new PluginViewEngine() - }; - - WebInitialComponent.WrapViewEngines(engines); - - Assert.That(engines.Count, Is.EqualTo(2)); - Assert.That(engines[0], Is.InstanceOf()); - Assert.That(engines[1], Is.InstanceOf()); - } - - [Test] - public void WrapViewEngines_HasEngines_KeepsSortOrder() - { - IList engines = new List - { - new RenderViewEngine(TestHelper.GetHostingEnvironment()), - new PluginViewEngine() - }; - - WebInitialComponent.WrapViewEngines(engines); - - Assert.That(engines.Count, Is.EqualTo(2)); - Assert.That(((ProfilingViewEngine)engines[0]).Inner, Is.InstanceOf()); - Assert.That(((ProfilingViewEngine)engines[1]).Inner, Is.InstanceOf()); - } - - [Test] - public void WrapViewEngines_HasProfiledEngine_AddsSameInstance() - { - var profiledEngine = new ProfilingViewEngine(new PluginViewEngine()); - IList engines = new List - { - profiledEngine - }; - - WebInitialComponent.WrapViewEngines(engines); - - Assert.That(engines[0], Is.SameAs(profiledEngine)); - } - - [Test] - public void WrapViewEngines_CollectionIsNull_DoesNotThrow() - { - Assert.DoesNotThrow(() => WebInitialComponent.WrapViewEngines(null)); - } - } -} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 13e9cbeef1..58aa4ac7ec 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -217,7 +217,6 @@ - diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 0d068bbd43..98dd77d9a1 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -38,7 +38,7 @@ "ConvertUrlsToAscii": "try" }, "RuntimeMinification": { - "dataFolder": "App_Data/TEMP/Smidge", + "dataFolder": "App_Data/TEMP/Smidge", "version": "1" }, "Security": { diff --git a/src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs b/src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs index 5a79fcf188..acb2ccbffa 100644 --- a/src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs @@ -11,19 +11,9 @@ namespace Umbraco.Extensions /// The action context. /// The name of the data token. /// The data token, or null. - /// MIGRAGED TO NETCORE AS EXTENSION ON HTTPCONTEXT internal static object GetDataTokenInViewContextHierarchy(this ActionContext actionContext, string dataTokenName) { - - var controllerActionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor; - while (!(controllerActionDescriptor is null)) - { - object token; - if (actionContext.RouteData.DataTokens.TryGetValue(dataTokenName, out token)) - return token; - controllerActionDescriptor = controllerActionDescriptor.ParentActionViewContext; - } - return null; + return actionContext.RouteData.DataTokens.TryGetValue(dataTokenName, out var token) ? token : null; } } } diff --git a/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs b/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs index bdc9ee840c..b47679a1bf 100644 --- a/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Website/Extensions/UmbracoWebstiteServiceCollectionExtensions.cs @@ -1,5 +1,11 @@ -using Microsoft.AspNetCore.Mvc.Controllers; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Umbraco.Web.Website.ViewEngines; namespace Umbraco.Extensions { @@ -8,6 +14,18 @@ namespace Umbraco.Extensions public static void AddUmbracoWebsite(this IServiceCollection services) { services.AddSingleton(); + + // Set the render & plugin view engines (Super complicated, but this allows us to use the IServiceCollection + // to inject dependencies into the viewEngines) + services.AddTransient, RenderMvcViewOptionsSetup>(); + services.AddSingleton(); + services.AddTransient, PluginMvcViewOptionsSetup>(); + services.AddSingleton(); + + // Wraps all existing view engines in a ProfilerViewEngine + services.AddTransient, ProfilingViewEngineWrapperMvcViewOptionsSetup>(); + } + } } diff --git a/src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs new file mode 100644 index 0000000000..4bcb7b5dc2 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Mvc.Razor; + +namespace Umbraco.Web.Website.ViewEngines +{ + public interface IPluginViewEngine : IRazorViewEngine + { + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/IRenderViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/IRenderViewEngine.cs new file mode 100644 index 0000000000..6f21d21494 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/IRenderViewEngine.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Mvc.Razor; + +namespace Umbraco.Web.Website.ViewEngines +{ + public interface IRenderViewEngine : IRazorViewEngine + { + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/PluginMvcViewOptionsSetup.cs b/src/Umbraco.Web.Website/ViewEngines/PluginMvcViewOptionsSetup.cs new file mode 100644 index 0000000000..cf703ddaca --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/PluginMvcViewOptionsSetup.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +namespace Umbraco.Web.Website.ViewEngines +{ + public class PluginMvcViewOptionsSetup : IConfigureOptions + { + private readonly IPluginViewEngine _pluginViewEngine; + + public PluginMvcViewOptionsSetup(IPluginViewEngine pluginViewEngine) + { + _pluginViewEngine = pluginViewEngine ?? throw new ArgumentNullException(nameof(pluginViewEngine)); + } + + public void Configure(MvcViewOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + options.ViewEngines.Add(_pluginViewEngine); + } + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs index 7c8deb9dd7..ac16be417a 100644 --- a/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs +++ b/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs @@ -9,7 +9,7 @@ namespace Umbraco.Web.Website.ViewEngines /// /// A view engine to look into the App_Plugins folder for views for packaged controllers /// - public class PluginViewEngine : RazorViewEngine + public class PluginViewEngine : RazorViewEngine, IPluginViewEngine { public PluginViewEngine( IRazorPageFactoryProvider pageFactory, diff --git a/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs b/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs new file mode 100644 index 0000000000..b8fbe1e527 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Microsoft.Extensions.Options; +using Umbraco.Core.Logging; + +namespace Umbraco.Web.Website.ViewEngines +{ + public class ProfilingViewEngineWrapperMvcViewOptionsSetup : IConfigureOptions + { + private readonly IProfiler _profiler; + + public ProfilingViewEngineWrapperMvcViewOptionsSetup(IProfiler profiler) + { + _profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); + } + + public void Configure(MvcViewOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + WrapViewEngines(options.ViewEngines); + } + + private void WrapViewEngines(IList viewEngines) + { + if (viewEngines == null || viewEngines.Count == 0) return; + + var originalEngines = viewEngines.ToList(); + viewEngines.Clear(); + foreach (var engine in originalEngines) + { + var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine, _profiler); + viewEngines.Add(wrappedEngine); + } + } + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/RenderMvcViewOptionsSetup.cs b/src/Umbraco.Web.Website/ViewEngines/RenderMvcViewOptionsSetup.cs new file mode 100644 index 0000000000..d8ad58cc91 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/RenderMvcViewOptionsSetup.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; + +namespace Umbraco.Web.Website.ViewEngines +{ + public class RenderMvcViewOptionsSetup : IConfigureOptions + { + private readonly IRenderViewEngine _renderViewEngine; + + public RenderMvcViewOptionsSetup(IRenderViewEngine renderViewEngine) + { + _renderViewEngine = renderViewEngine ?? throw new ArgumentNullException(nameof(renderViewEngine)); + } + + public void Configure(MvcViewOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + options.ViewEngines.Add(_renderViewEngine); + } + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs index 50e70b68a7..ea727a07f1 100644 --- a/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs +++ b/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Website.ViewEngines /// 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 : RazorViewEngine + public class RenderViewEngine : RazorViewEngine, IRenderViewEngine { public RenderViewEngine( @@ -46,50 +46,22 @@ namespace Umbraco.Web.Website.ViewEngines "/MacroPartials/{0}.cshtml", "/{0}.cshtml" }, + AreaViewLocationFormats = + { + "/Partials/{0}.cshtml", + "/MacroPartials/{0}.cshtml", + "/{0}.cshtml" + } }); } public new ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage) { - return ShouldFindView(context, viewName, isMainPage) + return ShouldFindView(context, isMainPage) ? base.FindView(context, viewName, isMainPage) : ViewEngineResult.NotFound(viewName, Array.Empty()); } - // /// - // /// Constructor - // /// - // public RenderViewEngine(IHostingEnvironment hostingEnvironment) - // { - // _hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); - // - // 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(); - // AreaViewLocationFormats = Array.Empty(); - // - // EnsureFoldersAndFiles(); - // } - - - // 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[] { }); - // } - /// /// 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 @@ -98,14 +70,14 @@ namespace Umbraco.Web.Website.ViewEngines /// /// /// - private static bool ShouldFindView(ActionContext context, string viewName, bool isMainPage) + private static bool ShouldFindView(ActionContext context, bool isMainPage) { - var umbracoToken = context.GetDataTokenInViewContextHierarchy(Core.Constants.Web.UmbracoDataToken); - - context.ActionDescriptor. + //In v8, this was testing recursively into if it was a child action, but child action do not exist anymore, + //And my best guess is that it + context.RouteData.DataTokens.TryGetValue(Core.Constants.Web.UmbracoDataToken, out var umbracoToken); // first check if we're rendering a partial view for the back office, or surface controller, etc... // anything that is not ContentModel as this should only pertain to Umbraco views. - if (isPartial && !(umbracoToken is ContentModel)) + if (!isMainPage && !(umbracoToken is ContentModel)) return true; // only find views if we're rendering the umbraco front end diff --git a/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs b/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs index 7179f54c87..6442e1eb2a 100644 --- a/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs +++ b/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Mvc /// The controller context. /// The name of the data token. /// The data token, or null. - /// MIGRAGED TO NETCORE AS EXTENSION ON ActionContext + /// MIGRATED TO NETCORE AS EXTENSION ON ActionContext internal static object GetDataTokenInViewContextHierarchy(this ControllerContext controllerContext, string dataTokenName) { var context = controllerContext; diff --git a/src/Umbraco.Web/Runtime/WebInitialComponent.cs b/src/Umbraco.Web/Runtime/WebInitialComponent.cs index 10623a1ed8..cddb04eac3 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComponent.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComponent.cs @@ -93,8 +93,8 @@ namespace Umbraco.Web.Runtime // ControllerBuilder.Current.SetControllerFactory(controllerFactory); // set the render & plugin view engines - ViewEngines.Engines.Add(new RenderViewEngine(_hostingEnvironment)); - ViewEngines.Engines.Add(new PluginViewEngine()); + // ViewEngines.Engines.Add(new RenderViewEngine(_hostingEnvironment)); + // ViewEngines.Engines.Add(new PluginViewEngine()); ////add the profiling action filter //GlobalFilters.Filters.Add(new ProfilingActionFilter()); From 1a4a72ee21267f489044b912761ef6e41e2874b5 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 3 Nov 2020 14:34:27 +0100 Subject: [PATCH 3/3] Cleanup Signed-off-by: Bjarke Berg --- .../Extensions/ActionContextExtensions.cs | 19 ------------------- .../Mvc/ControllerContextExtensions.cs | 1 - 2 files changed, 20 deletions(-) delete mode 100644 src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs diff --git a/src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs b/src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs deleted file mode 100644 index acb2ccbffa..0000000000 --- a/src/Umbraco.Web.Website/Extensions/ActionContextExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Controllers; - -namespace Umbraco.Extensions -{ - public static class ActionContextExtensions - { - /// - /// Recursively gets a data token from a controller context hierarchy. - /// - /// The action context. - /// The name of the data token. - /// The data token, or null. - internal static object GetDataTokenInViewContextHierarchy(this ActionContext actionContext, string dataTokenName) - { - return actionContext.RouteData.DataTokens.TryGetValue(dataTokenName, out var token) ? token : null; - } - } -} diff --git a/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs b/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs index 6442e1eb2a..4baaaac4fc 100644 --- a/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs +++ b/src/Umbraco.Web/Mvc/ControllerContextExtensions.cs @@ -22,7 +22,6 @@ namespace Umbraco.Web.Mvc /// The controller context. /// The name of the data token. /// The data token, or null. - /// MIGRATED TO NETCORE AS EXTENSION ON ActionContext internal static object GetDataTokenInViewContextHierarchy(this ControllerContext controllerContext, string dataTokenName) { var context = controllerContext;