From a126977a4e0b6296afa33b782272103b2b983f87 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 4 Jan 2021 15:43:30 +1100 Subject: [PATCH] Simplifies how view locations are determined AB#9720 --- .../UmbracoBuilderExtensions.cs | 10 +- .../ViewEngines/IPluginViewEngine.cs | 8 -- .../ViewEngines/IRenderViewEngine.cs | 8 -- .../ViewEngines/PluginMvcViewOptionsSetup.cs | 26 ----- .../PluginRazorViewEngineOptionsSetup.cs | 52 ++++++++++ .../ViewEngines/PluginViewEngine.cs | 52 ---------- ...ingViewEngineWrapperMvcViewOptionsSetup.cs | 24 +++-- .../ViewEngines/RenderMvcViewOptionsSetup.cs | 26 ----- .../RenderRazorViewEngineOptionsSetup.cs | 48 ++++++++++ .../ViewEngines/RenderViewEngine.cs | 94 ------------------- 10 files changed, 120 insertions(+), 228 deletions(-) delete mode 100644 src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs delete mode 100644 src/Umbraco.Web.Website/ViewEngines/IRenderViewEngine.cs delete mode 100644 src/Umbraco.Web.Website/ViewEngines/PluginMvcViewOptionsSetup.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/PluginRazorViewEngineOptionsSetup.cs delete mode 100644 src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs delete mode 100644 src/Umbraco.Web.Website/ViewEngines/RenderMvcViewOptionsSetup.cs create mode 100644 src/Umbraco.Web.Website/ViewEngines/RenderRazorViewEngineOptionsSetup.cs delete mode 100644 src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs diff --git a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs index 52f6d5df11..64d7cd0205 100644 --- a/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Website/DependencyInjection/UmbracoBuilderExtensions.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Core.DependencyInjection; @@ -27,12 +28,9 @@ namespace Umbraco.Web.Website.DependencyInjection builder.WithCollectionBuilder() .Add(builder.TypeLoader.GetSurfaceControllers()); - // Set the render & plugin view engines (Super complicated, but this allows us to use the IServiceCollection - // to inject dependencies into the viewEngines) - builder.Services.AddTransient, RenderMvcViewOptionsSetup>(); - builder.Services.AddSingleton(); - builder.Services.AddTransient, PluginMvcViewOptionsSetup>(); - builder.Services.AddSingleton(); + // Configure MVC startup options for custom view locations + builder.Services.AddTransient, RenderRazorViewEngineOptionsSetup>(); + builder.Services.AddTransient, PluginRazorViewEngineOptionsSetup>(); // Wraps all existing view engines in a ProfilerViewEngine builder.Services.AddTransient, ProfilingViewEngineWrapperMvcViewOptionsSetup>(); diff --git a/src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs deleted file mode 100644 index 4bcb7b5dc2..0000000000 --- a/src/Umbraco.Web.Website/ViewEngines/IPluginViewEngine.cs +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 6f21d21494..0000000000 --- a/src/Umbraco.Web.Website/ViewEngines/IRenderViewEngine.cs +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index cf703ddaca..0000000000 --- a/src/Umbraco.Web.Website/ViewEngines/PluginMvcViewOptionsSetup.cs +++ /dev/null @@ -1,26 +0,0 @@ -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/PluginRazorViewEngineOptionsSetup.cs b/src/Umbraco.Web.Website/ViewEngines/PluginRazorViewEngineOptionsSetup.cs new file mode 100644 index 0000000000..3cb6d78113 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/PluginRazorViewEngineOptionsSetup.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.Extensions.Options; + +namespace Umbraco.Web.Website.ViewEngines +{ + /// + /// Configure view engine locations for front-end rendering based on App_Plugins views + /// + public class PluginRazorViewEngineOptionsSetup : IConfigureOptions + { + /// + public void Configure(RazorViewEngineOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + options.ViewLocationExpanders.Add(new ViewLocationExpander()); + } + + /// + /// Expands the default view locations + /// + private class ViewLocationExpander : IViewLocationExpander + { + public IEnumerable ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable viewLocations) + { + string[] umbViewLocations = new string[] + { + // area view locations for 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") + }; + + viewLocations = umbViewLocations.Concat(viewLocations); + + return viewLocations; + } + + // not a dynamic expander + public void PopulateValues(ViewLocationExpanderContext context) { } + } + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs deleted file mode 100644 index e0b16a351e..0000000000 --- a/src/Umbraco.Web.Website/ViewEngines/PluginViewEngine.cs +++ /dev/null @@ -1,52 +0,0 @@ -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 -{ - // TODO: We don't really need to have different view engines simply to search additional places, - // we can just do ConfigureOptions on startup to add more to the - // default list so this can be totally removed/replaced with configure options logic. - - /// - /// 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() => Options.Create(new RazorViewEngineOptions() - { - // This is definitely not doing what it used to do :P see: - // https://github.com/umbraco/Umbraco-CMS/blob/v8/contrib/src/Umbraco.Web/Mvc/PluginViewEngine.cs#L23 - - 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/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs b/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs index b8fbe1e527..1510975f14 100644 --- a/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs +++ b/src/Umbraco.Web.Website/ViewEngines/ProfilingViewEngineWrapperMvcViewOptionsSetup.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Mvc; @@ -8,15 +8,20 @@ using Umbraco.Core.Logging; namespace Umbraco.Web.Website.ViewEngines { + /// + /// Wraps all view engines with a + /// public class ProfilingViewEngineWrapperMvcViewOptionsSetup : IConfigureOptions { private readonly IProfiler _profiler; - public ProfilingViewEngineWrapperMvcViewOptionsSetup(IProfiler profiler) - { - _profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); - } + /// + /// Initializes a new instance of the class. + /// + /// The + public ProfilingViewEngineWrapperMvcViewOptionsSetup(IProfiler profiler) => _profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); + /// public void Configure(MvcViewOptions options) { if (options == null) @@ -29,13 +34,16 @@ namespace Umbraco.Web.Website.ViewEngines private void WrapViewEngines(IList viewEngines) { - if (viewEngines == null || viewEngines.Count == 0) return; + if (viewEngines == null || viewEngines.Count == 0) + { + return; + } var originalEngines = viewEngines.ToList(); viewEngines.Clear(); - foreach (var engine in originalEngines) + foreach (IViewEngine engine in originalEngines) { - var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine(engine, _profiler); + IViewEngine 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 deleted file mode 100644 index d8ad58cc91..0000000000 --- a/src/Umbraco.Web.Website/ViewEngines/RenderMvcViewOptionsSetup.cs +++ /dev/null @@ -1,26 +0,0 @@ -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/RenderRazorViewEngineOptionsSetup.cs b/src/Umbraco.Web.Website/ViewEngines/RenderRazorViewEngineOptionsSetup.cs new file mode 100644 index 0000000000..39009d44a1 --- /dev/null +++ b/src/Umbraco.Web.Website/ViewEngines/RenderRazorViewEngineOptionsSetup.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc.Razor; +using Microsoft.Extensions.Options; + +namespace Umbraco.Web.Website.ViewEngines +{ + /// + /// Configure view engine locations for front-end rendering + /// + public class RenderRazorViewEngineOptionsSetup : IConfigureOptions + { + /// + public void Configure(RazorViewEngineOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + options.ViewLocationExpanders.Add(new ViewLocationExpander()); + } + + /// + /// Expands the default view locations + /// + private class ViewLocationExpander : IViewLocationExpander + { + public IEnumerable ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable viewLocations) + { + string[] umbViewLocations = new string[] + { + "/Views/Partials/{0}.cshtml", + "/Views/MacroPartials/{0}.cshtml", + "/Views/{0}.cshtml" + }; + + viewLocations = umbViewLocations.Concat(viewLocations); + + return viewLocations; + } + + // not a dynamic expander + public void PopulateValues(ViewLocationExpanderContext context) { } + } + } +} diff --git a/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs b/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs deleted file mode 100644 index 8c53255928..0000000000 --- a/src/Umbraco.Web.Website/ViewEngines/RenderViewEngine.cs +++ /dev/null @@ -1,94 +0,0 @@ -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 -{ - // TODO: We don't really need to have different view engines simply to search additional places, - // we can just do ConfigureOptions on startup to add more to the - // default list so this can be totally removed/replaced with configure options logic. - - /// - /// 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 - { - - /// - /// Initializes a new instance of the class. - /// - public RenderViewEngine( - IRazorPageFactoryProvider pageFactory, - IRazorPageActivator pageActivator, - HtmlEncoder htmlEncoder, - ILoggerFactory loggerFactory, - DiagnosticListener diagnosticListener) - : base(pageFactory, pageActivator, htmlEncoder, OverrideViewLocations(), loggerFactory, diagnosticListener) - { - } - - private static IOptions OverrideViewLocations() => 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 = - { - "/Views/Partials/{0}.cshtml", - "/Views/MacroPartials/{0}.cshtml", - "/Views/{0}.cshtml" - }, - AreaViewLocationFormats = - { - "/Views/Partials/{0}.cshtml", - "/Views/MacroPartials/{0}.cshtml", - "/Views/{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) - { - return true; - // TODO: Determine if this is required, i don't think it is - ////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; - } - - - } -}