Implementing shim and cache for theme awareness

Configured arrays of view engines cached for use in appropriate contexts
Adding current theme as dedicated property on work context
Current theme assigned by selecters, if null, as result begins executing
Distinction is made between deep and shallow view path locations

--HG--
branch : theming
This commit is contained in:
Louis DeJardin
2010-09-03 17:41:14 -07:00
parent 30fb9660a4
commit 2de2f685e9
20 changed files with 268 additions and 33 deletions

View File

@@ -24,6 +24,7 @@ using Orchard.Mvc.ModelBinders;
using Orchard.Mvc.Routes;
using Orchard.Mvc.ViewEngines;
using Orchard.Mvc.ViewEngines.Razor;
using Orchard.Mvc.ViewEngines.ThemeAwareness;
using Orchard.Mvc.ViewEngines.WebForms;
using Orchard.Settings;
using Orchard.Setup.Commands;
@@ -44,7 +45,7 @@ namespace Orchard.Setup {
builder.RegisterType<ModelBinderPublisher>().As<IModelBinderPublisher>().InstancePerLifetimeScope();
builder.RegisterType<WebFormViewEngineProvider>().As<IViewEngineProvider>().InstancePerLifetimeScope();
builder.RegisterType<RazorViewEngineProvider>().As<IViewEngineProvider>().InstancePerLifetimeScope();
builder.RegisterType<ViewEngineFilter>().As<IFilterProvider>().InstancePerLifetimeScope();
builder.RegisterType<CurrentThemeFilter>().As<IFilterProvider>().InstancePerLifetimeScope();
builder.RegisterType<ThemeFilter>().As<IFilterProvider>().InstancePerLifetimeScope();
builder.RegisterType<PageTitleBuilder>().As<IPageTitleBuilder>().InstancePerLifetimeScope();
builder.RegisterType<ZoneManager>().As<IZoneManager>().InstancePerLifetimeScope();
@@ -69,7 +70,11 @@ namespace Orchard.Setup {
builder.RegisterType<DefaultDisplayManager>().As<IDisplayManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<ThemeAwareViewEngine>().As<IThemeAwareViewEngine>();
builder.RegisterType<ConfiguredEnginesCache>().As<IConfiguredEnginesCache>();
}

View File

@@ -58,9 +58,6 @@ namespace Orchard.Environment {
void IOrchardHost.Initialize() {
Logger.Information("Initializing");
ViewEngines.Engines.Insert(0, LayoutViewEngine.CreateShim());
BuildCurrent();
}

View File

@@ -23,6 +23,7 @@ using Orchard.FileSystems.VirtualPath;
using Orchard.FileSystems.WebSite;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Mvc.ViewEngines.ThemeAwareness;
using Orchard.Services;
namespace Orchard.Environment {
@@ -117,6 +118,8 @@ namespace Orchard.Environment {
}
ControllerBuilder.Current.SetControllerFactory(new OrchardControllerFactory());
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ThemeArareViewEngineShim());
var hostContainer = new DefaultOrchardHostContainer(container);
//MvcServiceLocator.SetCurrent(hostContainer);

View File

@@ -10,8 +10,13 @@ namespace Orchard.Mvc.ViewEngines {
public IEnumerable<string> VirtualPaths { get; set; }
}
public interface IViewEngineProvider : IDependency {
public interface IViewEngineProvider : ISingletonDependency {
IViewEngine CreateThemeViewEngine(CreateThemeViewEngineParams parameters);
IViewEngine CreateModulesViewEngine(CreateModulesViewEngineParams parameters);
/// <summary>
/// Produce a view engine configured to resolve only fully qualified {viewName} parameters
/// </summary>
IViewEngine CreateBareViewEngine();
}
}

View File

@@ -91,6 +91,17 @@ namespace Orchard.Mvc.ViewEngines.Razor {
return viewEngine;
}
public IViewEngine CreateBareViewEngine() {
return new CshtmlViewEngine {
MasterLocationFormats = DisabledFormats,
ViewLocationFormats = DisabledFormats,
PartialViewLocationFormats = DisabledFormats,
AreaMasterLocationFormats = DisabledFormats,
AreaViewLocationFormats = DisabledFormats,
AreaPartialViewLocationFormats = DisabledFormats,
};
}
public IEnumerable<string> DetectTemplateFileNames(string virtualPath) {
var fileNames = _virtualPathProvider.ListFiles(virtualPath).Select(Path.GetFileName);
foreach (var fileName in fileNames) {

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Concurrent;
using System.Web.Mvc;
namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
public interface IConfiguredEnginesCache : ISingletonDependency {
IViewEngine BindBareEngines(Func<IViewEngine> factory);
IViewEngine BindShallowEngines(string themeName, Func<IViewEngine> factory);
IViewEngine BindDeepEngines(string themeName, Func<IViewEngine> factory);
}
public class ConfiguredEnginesCache : IConfiguredEnginesCache {
IViewEngine _bare = null;
readonly ConcurrentDictionary<string, IViewEngine> _shallow = new ConcurrentDictionary<string, IViewEngine>();
readonly ConcurrentDictionary<string, IViewEngine> _deep = new ConcurrentDictionary<string, IViewEngine>();
public ConfiguredEnginesCache() {
_shallow = new ConcurrentDictionary<string, IViewEngine>();
}
public IViewEngine BindBareEngines(Func<IViewEngine> factory) {
return _bare ?? (_bare = factory());
}
public IViewEngine BindShallowEngines(string themeName, Func<IViewEngine> factory) {
return _shallow.GetOrAdd(themeName, key => factory());
}
public IViewEngine BindDeepEngines(string themeName, Func<IViewEngine> factory) {
return _deep.GetOrAdd(themeName, key => factory());
}
}
}

View File

@@ -1,35 +1,27 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Orchard.Environment.Extensions;
using Orchard.Logging;
using Orchard.Mvc.Filters;
using Orchard.Themes;
namespace Orchard.Mvc.ViewEngines {
namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
public class ViewEngineFilter : FilterProvider, IResultFilter {
private readonly ViewEngineCollection _viewEngines;
public class CurrentThemeFilter : FilterProvider, IResultFilter {
private readonly IThemeService _themeService;
private readonly IExtensionManager _extensionManager;
private readonly IEnumerable<IViewEngineProvider> _viewEngineProviders;
private readonly WorkContext _workContext;
public ViewEngineFilter(
ViewEngineCollection viewEngines,
IThemeService themeService,
IExtensionManager extensionManager,
IEnumerable<IViewEngineProvider> viewEngineProviders) {
_viewEngines = viewEngines;
public CurrentThemeFilter(IThemeService themeService, WorkContext workContext) {
_themeService = themeService;
_extensionManager = extensionManager;
_viewEngineProviders = viewEngineProviders;
_workContext = workContext;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void OnResultExecuting(ResultExecutingContext filterContext) {
if (_workContext.CurrentTheme == null) {
_workContext.CurrentTheme = _themeService.GetRequestTheme(filterContext.RequestContext);
}
#if REFACTORING
var viewResultBase = filterContext.Result as ViewResultBase;
if (viewResultBase == null) {
return;
@@ -70,14 +62,7 @@ namespace Orchard.Mvc.ViewEngines {
viewResultBase.ViewEngineCollection = new ViewEngineCollection(_viewEngines.ToList());
viewResultBase.ViewEngineCollection.Insert(0, layoutViewEngine);
}
static bool ViewEngineIsForwarded(IViewEngine x) {
// default view engine, and layout view engine, are not forwarded to
// be used for resolving partials
return
x.GetType().Assembly != typeof(LayoutViewEngine).Assembly &&
x.GetType() != typeof(WebFormViewEngine);
#endif
}
public void OnResultExecuted(ResultExecutedContext filterContext) {

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions;
using Orchard.Themes;
namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
public interface IThemeAwareViewEngine : IDependency, IViewEngine {
}
public class ThemeAwareViewEngine : IThemeAwareViewEngine {
private readonly WorkContext _workContext;
private readonly IEnumerable<IViewEngineProvider> _viewEngineProviders;
private readonly IConfiguredEnginesCache _configuredEnginesCache;
private readonly IExtensionManager _extensionManager;
private readonly ShellDescriptor _shellDescriptor;
private readonly IViewEngine _nullEngines = new ViewEngineCollectionWrapper(Enumerable.Empty<IViewEngine>());
public ThemeAwareViewEngine(
WorkContext workContext,
IEnumerable<IViewEngineProvider> viewEngineProviders,
IConfiguredEnginesCache configuredEnginesCache,
IExtensionManager extensionManager,
ShellDescriptor shellDescriptor) {
_workContext = workContext;
_viewEngineProviders = viewEngineProviders;
_configuredEnginesCache = configuredEnginesCache;
_extensionManager = extensionManager;
_shellDescriptor = shellDescriptor;
}
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) {
IViewEngine engines = _nullEngines;
if (partialViewName.StartsWith("/") || partialViewName.StartsWith("~")) {
engines = BareEngines();
}
else if (_workContext.CurrentTheme != null) {
if (controllerContext is ViewContext) {
engines = ShallowEngines(_workContext.CurrentTheme);
}
else {
engines = DeepEngines(_workContext.CurrentTheme);
}
}
return engines.FindPartialView(controllerContext, partialViewName, useCache);
}
private IViewEngine BareEngines() {
return _configuredEnginesCache.BindBareEngines(() => new ViewEngineCollectionWrapper(_viewEngineProviders.Select(vep => vep.CreateBareViewEngine())));
}
private IViewEngine ShallowEngines(ITheme theme) {
//return _configuredEnginesCache.BindShallowEngines(theme.ThemeName, () => new ViewEngineCollectionWrapper(_viewEngineProviders.Select(vep => vep.CreateBareViewEngine())));
return DeepEngines(theme);
}
private IViewEngine DeepEngines(ITheme theme) {
return _configuredEnginesCache.BindDeepEngines(theme.ThemeName, () => {
var engines = Enumerable.Empty<IViewEngine>();
var themeLocation = _extensionManager.GetThemeLocation(theme);
var themeParams = new CreateThemeViewEngineParams { VirtualPath = themeLocation };
engines = engines.Concat(_viewEngineProviders.Select(vep => vep.CreateThemeViewEngine(themeParams)));
var activeFeatures = _extensionManager.AvailableFeatures().Where(fd => _shellDescriptor.Features.Any(sdf => sdf.Name == fd.Name));
var activeModuleLocations = activeFeatures.Select(fd => fd.Extension.Location + "/" + fd.Extension.Name).Distinct();
var moduleParams = new CreateModulesViewEngineParams { VirtualPaths = activeModuleLocations };
engines = engines.Concat(_viewEngineProviders.Select(vep => vep.CreateModulesViewEngine(moduleParams)));
return new ViewEngineCollectionWrapper(engines);
});
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
return FindPartialView(controllerContext, viewName, useCache);
}
public void ReleaseView(ControllerContext controllerContext, IView view) {
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Linq;
using System.Web.Mvc;
using Orchard.Environment;
namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
public class ThemeArareViewEngineShim : IViewEngine, IShim {
public ThemeArareViewEngineShim() {
OrchardHostContainerRegistry.RegisterShim(this);
}
public IOrchardHostContainer HostContainer { get; set; }
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) {
return Forward(
controllerContext,
dve => dve.FindPartialView(controllerContext, partialViewName, useCache),
EmptyViewEngineResult);
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
return Forward(
controllerContext,
dve => dve.FindView(controllerContext, viewName, masterName, useCache),
EmptyViewEngineResult);
}
public void ReleaseView(ControllerContext controllerContext, IView view) {
throw new NotImplementedException();
}
static TResult Forward<TResult>(ControllerContext controllerContext, Func<IThemeAwareViewEngine, TResult> forwardAction, Func<TResult> defaultAction) {
var workContext = controllerContext.GetWorkContext();
if (workContext != null) {
var displayViewEngine = workContext.Resolve<IThemeAwareViewEngine>();
if (displayViewEngine != null) {
return forwardAction(displayViewEngine);
}
}
return defaultAction();
}
static ViewEngineResult EmptyViewEngineResult() {
return new ViewEngineResult(Enumerable.Empty<string>());
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace Orchard.Mvc.ViewEngines.ThemeAwareness {
public class ViewEngineCollectionWrapper : IViewEngine {
private readonly IEnumerable<IViewEngine> _engines;
public ViewEngineCollectionWrapper(IEnumerable<IViewEngine> engines) {
_engines = engines.ToArray();
}
public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) {
var searchedLocations = Enumerable.Empty<string>();
foreach (var engine in _engines) {
var result = engine.FindPartialView(controllerContext, partialViewName, useCache);
if (result.View != null)
return result;
if (!useCache)
searchedLocations = searchedLocations.Concat(result.SearchedLocations);
}
return new ViewEngineResult(searchedLocations.Distinct());
}
public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
var searchedLocations = Enumerable.Empty<string>();
foreach (var engine in _engines) {
var result = engine.FindView(controllerContext, viewName, masterName, useCache);
if (result.View != null)
return result;
if (!useCache)
searchedLocations = searchedLocations.Concat(result.SearchedLocations);
}
return new ViewEngineResult(searchedLocations.Distinct());
}
public void ReleaseView(ControllerContext controllerContext, IView view) {
throw new NotImplementedException();
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using System.Web.Mvc;
using Orchard.Logging;
@@ -85,5 +86,17 @@ namespace Orchard.Mvc.ViewEngines.WebForms {
return viewEngine;
}
public IViewEngine CreateBareViewEngine() {
return new WebFormViewEngineForAspNet4 {
MasterLocationFormats = DisabledFormats,
ViewLocationFormats = DisabledFormats,
PartialViewLocationFormats = DisabledFormats,
AreaMasterLocationFormats = DisabledFormats,
AreaViewLocationFormats = DisabledFormats,
AreaPartialViewLocationFormats = DisabledFormats,
};
}
}
}

View File

@@ -139,6 +139,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ConfiguredEnginesCache.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ThemeAwareViewEngine.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ThemeAwareViewEngineShim.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ViewEngineCollectionWrapper.cs" />
<Compile Include="Mvc\ViewModels\BaseViewModel.cs" />
<Compile Include="Caching\Signals.cs" />
<Compile Include="Collections\IPageOfItems.cs" />
@@ -716,7 +720,7 @@
<Compile Include="Mvc\ViewEngines\LayoutView.cs" />
<Compile Include="Mvc\ViewEngines\LayoutViewEngine.cs" />
<Compile Include="Mvc\ViewEngines\LayoutViewContext.cs" />
<Compile Include="Mvc\ViewEngines\ViewEngineFilter.cs" />
<Compile Include="Mvc\ViewEngines\ThemeAwareness\CurrentTheme.cs" />
<Compile Include="Mvc\ViewEngines\WebForms\WebFormViewEngineProvider.cs" />
<Compile Include="UI\Admin\AdminThemeSelector.cs" />
<Compile Include="UI\Navigation\INavigationManager.cs" />

View File

@@ -3,6 +3,7 @@ using System.Web;
using Autofac;
using Orchard.Security;
using Orchard.Settings;
using Orchard.Themes;
using Orchard.UI;
namespace Orchard {
@@ -20,6 +21,8 @@ namespace Orchard {
get { return State<IUser>(); }
}
public ITheme CurrentTheme { get; set; }
public abstract T Resolve<T>();
public abstract T State<T>();
}