Simplifies how view locations are determined
AB#9720
This commit is contained in:
@@ -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<SurfaceControllerTypeCollectionBuilder>()
|
||||
.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<IConfigureOptions<MvcViewOptions>, RenderMvcViewOptionsSetup>();
|
||||
builder.Services.AddSingleton<IRenderViewEngine, RenderViewEngine>();
|
||||
builder.Services.AddTransient<IConfigureOptions<MvcViewOptions>, PluginMvcViewOptionsSetup>();
|
||||
builder.Services.AddSingleton<IPluginViewEngine, PluginViewEngine>();
|
||||
// Configure MVC startup options for custom view locations
|
||||
builder.Services.AddTransient<IConfigureOptions<RazorViewEngineOptions>, RenderRazorViewEngineOptionsSetup>();
|
||||
builder.Services.AddTransient<IConfigureOptions<RazorViewEngineOptions>, PluginRazorViewEngineOptionsSetup>();
|
||||
|
||||
// Wraps all existing view engines in a ProfilerViewEngine
|
||||
builder.Services.AddTransient<IConfigureOptions<MvcViewOptions>, ProfilingViewEngineWrapperMvcViewOptionsSetup>();
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
|
||||
namespace Umbraco.Web.Website.ViewEngines
|
||||
{
|
||||
public interface IPluginViewEngine : IRazorViewEngine
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
|
||||
namespace Umbraco.Web.Website.ViewEngines
|
||||
{
|
||||
public interface IRenderViewEngine : IRazorViewEngine
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Umbraco.Web.Website.ViewEngines
|
||||
{
|
||||
public class PluginMvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure view engine locations for front-end rendering based on App_Plugins views
|
||||
/// </summary>
|
||||
public class PluginRazorViewEngineOptionsSetup : IConfigureOptions<RazorViewEngineOptions>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void Configure(RazorViewEngineOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
options.ViewLocationExpanders.Add(new ViewLocationExpander());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands the default view locations
|
||||
/// </summary>
|
||||
private class ViewLocationExpander : IViewLocationExpander
|
||||
{
|
||||
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> 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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<RazorViewEngineOptions> on startup to add more to the
|
||||
// default list so this can be totally removed/replaced with configure options logic.
|
||||
|
||||
/// <summary>
|
||||
/// A view engine to look into the App_Plugins folder for views for packaged controllers
|
||||
/// </summary>
|
||||
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<RazorViewEngineOptions> 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"),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps all view engines with a <see cref="ProfilingViewEngine"/>
|
||||
/// </summary>
|
||||
public class ProfilingViewEngineWrapperMvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
|
||||
{
|
||||
private readonly IProfiler _profiler;
|
||||
|
||||
public ProfilingViewEngineWrapperMvcViewOptionsSetup(IProfiler profiler)
|
||||
{
|
||||
_profiler = profiler ?? throw new ArgumentNullException(nameof(profiler));
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProfilingViewEngineWrapperMvcViewOptionsSetup"/> class.
|
||||
/// </summary>
|
||||
/// <param name="profiler">The <see cref="IProfiler"/></param>
|
||||
public ProfilingViewEngineWrapperMvcViewOptionsSetup(IProfiler profiler) => _profiler = profiler ?? throw new ArgumentNullException(nameof(profiler));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Configure(MvcViewOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
@@ -29,13 +34,16 @@ namespace Umbraco.Web.Website.ViewEngines
|
||||
|
||||
private void WrapViewEngines(IList<IViewEngine> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Umbraco.Web.Website.ViewEngines
|
||||
{
|
||||
public class RenderMvcViewOptionsSetup : IConfigureOptions<MvcViewOptions>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure view engine locations for front-end rendering
|
||||
/// </summary>
|
||||
public class RenderRazorViewEngineOptionsSetup : IConfigureOptions<RazorViewEngineOptions>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void Configure(RazorViewEngineOptions options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
options.ViewLocationExpanders.Add(new ViewLocationExpander());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands the default view locations
|
||||
/// </summary>
|
||||
private class ViewLocationExpander : IViewLocationExpander
|
||||
{
|
||||
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> 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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<RazorViewEngineOptions> on startup to add more to the
|
||||
// default list so this can be totally removed/replaced with configure options logic.
|
||||
|
||||
/// <summary>
|
||||
/// A view engine to look into the template location specified in the config for the front-end/Rendering part of the cms,
|
||||
/// this includes paths to render partial macros and media item templates.
|
||||
/// </summary>
|
||||
public class RenderViewEngine : RazorViewEngine, IRenderViewEngine
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderViewEngine"/> class.
|
||||
/// </summary>
|
||||
public RenderViewEngine(
|
||||
IRazorPageFactoryProvider pageFactory,
|
||||
IRazorPageActivator pageActivator,
|
||||
HtmlEncoder htmlEncoder,
|
||||
ILoggerFactory loggerFactory,
|
||||
DiagnosticListener diagnosticListener)
|
||||
: base(pageFactory, pageActivator, htmlEncoder, OverrideViewLocations(), loggerFactory, diagnosticListener)
|
||||
{
|
||||
}
|
||||
|
||||
private static IOptions<RazorViewEngineOptions> 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<string>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the view should be found, this is used for view lookup performance and also to ensure
|
||||
/// less overlap with other user's view engines. This will return true if the Umbraco back office is rendering
|
||||
/// and its a partial view or if the umbraco front-end is rendering but nothing else.
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
/// <param name="isPartial"></param>
|
||||
/// <returns></returns>
|
||||
private 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user