diff --git a/src/Orchard.Web/Modules/Futures.Modules.Packaging/Futures.Modules.Packaging.csproj b/src/Orchard.Web/Modules/Futures.Modules.Packaging/Futures.Modules.Packaging.csproj_ similarity index 100% rename from src/Orchard.Web/Modules/Futures.Modules.Packaging/Futures.Modules.Packaging.csproj rename to src/Orchard.Web/Modules/Futures.Modules.Packaging/Futures.Modules.Packaging.csproj_ diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj b/src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj_ similarity index 100% rename from src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj rename to src/Orchard.Web/Modules/Futures.Widgets/Futures.Widgets.csproj_ diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj b/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj_ similarity index 100% rename from src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj rename to src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj_ diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj b/src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj_ similarity index 100% rename from src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj rename to src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj_ diff --git a/src/Orchard.Web/Modules/Orchard.Sandbox/Orchard.Sandbox.csproj b/src/Orchard.Web/Modules/Orchard.Sandbox/Orchard.Sandbox.csproj_ similarity index 100% rename from src/Orchard.Web/Modules/Orchard.Sandbox/Orchard.Sandbox.csproj rename to src/Orchard.Web/Modules/Orchard.Sandbox/Orchard.Sandbox.csproj_ diff --git a/src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj b/src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj_ similarity index 100% rename from src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj rename to src/Orchard.Web/Modules/Orchard.Search/Orchard.Search.csproj_ diff --git a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs index d34ace551..e5b10da3a 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs @@ -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().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); @@ -69,7 +70,11 @@ namespace Orchard.Setup { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.RegisterType().As(); + } diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj b/src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj_ similarity index 100% rename from src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj rename to src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj_ diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index 6173b0e4d..07fbcd8e7 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -58,9 +58,6 @@ namespace Orchard.Environment { void IOrchardHost.Initialize() { Logger.Information("Initializing"); - ViewEngines.Engines.Insert(0, LayoutViewEngine.CreateShim()); - - BuildCurrent(); } diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index 7e22b098d..a11035f13 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -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); diff --git a/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs index 49690aa87..c091b03c7 100644 --- a/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/IViewEngineProvider.cs @@ -10,8 +10,13 @@ namespace Orchard.Mvc.ViewEngines { public IEnumerable VirtualPaths { get; set; } } - public interface IViewEngineProvider : IDependency { + public interface IViewEngineProvider : ISingletonDependency { IViewEngine CreateThemeViewEngine(CreateThemeViewEngineParams parameters); IViewEngine CreateModulesViewEngine(CreateModulesViewEngineParams parameters); + + /// + /// Produce a view engine configured to resolve only fully qualified {viewName} parameters + /// + IViewEngine CreateBareViewEngine(); } } diff --git a/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs index 7bbe0d567..ea512052c 100644 --- a/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs @@ -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 DetectTemplateFileNames(string virtualPath) { var fileNames = _virtualPathProvider.ListFiles(virtualPath).Select(Path.GetFileName); foreach (var fileName in fileNames) { diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ConfiguredEnginesCache.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ConfiguredEnginesCache.cs new file mode 100644 index 000000000..7d71a886c --- /dev/null +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ConfiguredEnginesCache.cs @@ -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 factory); + IViewEngine BindShallowEngines(string themeName, Func factory); + IViewEngine BindDeepEngines(string themeName, Func factory); + } + + public class ConfiguredEnginesCache : IConfiguredEnginesCache { + IViewEngine _bare = null; + readonly ConcurrentDictionary _shallow = new ConcurrentDictionary(); + readonly ConcurrentDictionary _deep = new ConcurrentDictionary(); + + public ConfiguredEnginesCache() { + _shallow = new ConcurrentDictionary(); + } + + public IViewEngine BindBareEngines(Func factory) { + return _bare ?? (_bare = factory()); + } + + public IViewEngine BindShallowEngines(string themeName, Func factory) { + return _shallow.GetOrAdd(themeName, key => factory()); + } + + public IViewEngine BindDeepEngines(string themeName, Func factory) { + return _deep.GetOrAdd(themeName, key => factory()); + } + } +} diff --git a/src/Orchard/Mvc/ViewEngines/ViewEngineFilter.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/CurrentTheme.cs similarity index 67% rename from src/Orchard/Mvc/ViewEngines/ViewEngineFilter.cs rename to src/Orchard/Mvc/ViewEngines/ThemeAwareness/CurrentTheme.cs index c1fd4b19b..56e42d27b 100644 --- a/src/Orchard/Mvc/ViewEngines/ViewEngineFilter.cs +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/CurrentTheme.cs @@ -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 _viewEngineProviders; + private readonly WorkContext _workContext; - public ViewEngineFilter( - ViewEngineCollection viewEngines, - IThemeService themeService, - IExtensionManager extensionManager, - IEnumerable 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) { diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs new file mode 100644 index 000000000..b9e1825bb --- /dev/null +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs @@ -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 _viewEngineProviders; + private readonly IConfiguredEnginesCache _configuredEnginesCache; + private readonly IExtensionManager _extensionManager; + private readonly ShellDescriptor _shellDescriptor; + private readonly IViewEngine _nullEngines = new ViewEngineCollectionWrapper(Enumerable.Empty()); + + public ThemeAwareViewEngine( + WorkContext workContext, + IEnumerable 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(); + 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(); + } + } + +} diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngineShim.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngineShim.cs new file mode 100644 index 000000000..f43b56f01 --- /dev/null +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngineShim.cs @@ -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(ControllerContext controllerContext, Func forwardAction, Func defaultAction) { + var workContext = controllerContext.GetWorkContext(); + if (workContext != null) { + var displayViewEngine = workContext.Resolve(); + if (displayViewEngine != null) { + return forwardAction(displayViewEngine); + } + } + return defaultAction(); + } + + static ViewEngineResult EmptyViewEngineResult() { + return new ViewEngineResult(Enumerable.Empty()); + } + } +} diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ViewEngineCollectionWrapper.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ViewEngineCollectionWrapper.cs new file mode 100644 index 000000000..16d20d890 --- /dev/null +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ViewEngineCollectionWrapper.cs @@ -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 _engines; + + public ViewEngineCollectionWrapper(IEnumerable engines) { + _engines = engines.ToArray(); + } + + public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { + var searchedLocations = Enumerable.Empty(); + 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(); + 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(); + } + } +} diff --git a/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs index 28c69eb0c..704d9fa94 100644 --- a/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs @@ -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, + }; + } } } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 6cbb4df38..7ddfb94e3 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -139,6 +139,10 @@ + + + + @@ -716,7 +720,7 @@ - + diff --git a/src/Orchard/WorkContext.cs b/src/Orchard/WorkContext.cs index 87c5a6e41..2b297612b 100644 --- a/src/Orchard/WorkContext.cs +++ b/src/Orchard/WorkContext.cs @@ -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(); } } + public ITheme CurrentTheme { get; set; } + public abstract T Resolve(); public abstract T State(); }