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 08b1578d88..5d6d4222e4 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -216,7 +216,6 @@ - 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 new file mode 100644 index 0000000000..ac16be417a --- /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, IPluginViewEngine + { + 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.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 new file mode 100644 index 0000000000..ea727a07f1 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.Encodings.Web; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Core.Hosting; +using Umbraco.Extensions; +using Umbraco.Web.Models; + +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, IRenderViewEngine + { + + public RenderViewEngine( + 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() + { + //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 + ViewLocationFormats = + { + "/Partials/{0}.cshtml", + "/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, isMainPage) + ? base.FindView(context, viewName, isMainPage) + : ViewEngineResult.NotFound(viewName, Array.Empty()); + } + + /// + /// 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. + /// + /// + /// + /// + private static bool ShouldFindView(ActionContext context, bool isMainPage) + { + //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 (!isMainPage && !(umbracoToken is ContentModel)) + return true; + + // only find views if we're rendering the umbraco front end + return umbracoToken is ContentModel; + } + + + } +} 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/Mvc/RenderViewEngine.cs b/src/Umbraco.Web/Mvc/RenderViewEngine.cs deleted file mode 100644 index 5f508e8d61..0000000000 --- a/src/Umbraco.Web/Mvc/RenderViewEngine.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Web.Mvc; -using Umbraco.Core.Composing; -using Umbraco.Core.Hosting; -using Umbraco.Core.IO; -using Umbraco.Web.Models; - -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 : RazorViewEngine - { - private readonly IHostingEnvironment _hostingEnvironment; - - private readonly IEnumerable _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 _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(); - } - - /// - /// Ensures that the correct web.config for razor exists in the /Views folder, the partials folder exist and the ViewStartPage exists. - /// - private void EnsureFoldersAndFiles() - { - var viewFolder = _hostingEnvironment.MapPathContentRoot(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[] { }); - } - - /// - /// 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. - /// - /// - /// - /// - 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 ContentModel 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; - } - } -} diff --git a/src/Umbraco.Web/Runtime/WebInitialComponent.cs b/src/Umbraco.Web/Runtime/WebInitialComponent.cs index 780855afc9..f07914f2d2 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); } } @@ -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()); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cd2d326c14..e331d4c5f4 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -228,7 +228,6 @@ - @@ -293,7 +292,6 @@ - @@ -314,7 +312,6 @@ -