From ab3d0a2636a2339c6f0127fd1af09f5c411a81ce Mon Sep 17 00:00:00 2001 From: Renaud Paquay Date: Wed, 8 Dec 2010 14:27:50 -0800 Subject: [PATCH] Fix computed set of views paths to allow themes in the admin When looking for ViewPath to activate, the order should be: 1. Current active theme 2. Base themes of the current theme (in "base" order). Note that "base themes" for "TheAdmin" theme means all enabled themes 3. Modules which have at least one active feature, in feature dependency order Work Item: 16654 --HG-- branch : dev --- .../ThemeAwareness/ThemeAwareViewEngine.cs | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs index c7484e827..f2c14c544 100644 --- a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs @@ -5,6 +5,7 @@ using System.Web.Mvc; using Orchard.Environment.Descriptor.Models; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; +using Orchard.Logging; namespace Orchard.Mvc.ViewEngines.ThemeAwareness { public interface IThemeAwareViewEngine : IDependency { @@ -31,8 +32,12 @@ namespace Orchard.Mvc.ViewEngines.ThemeAwareness { _configuredEnginesCache = configuredEnginesCache; _extensionManager = extensionManager; _shellDescriptor = shellDescriptor; + + Logger = NullLogger.Instance; } + public ILogger Logger { get; set; } + public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache, bool useDeepPaths) { var engines = _nullEngines; @@ -71,21 +76,78 @@ namespace Orchard.Mvc.ViewEngines.ThemeAwareness { private IViewEngine DeepEngines(ExtensionDescriptor theme) { return _configuredEnginesCache.BindDeepEngines(theme.Id, () => { + // The order for searching for views is: + // 1. Current "theme" + // 2. Base themes of the current theme (in "base" order) + // 3. Active features from modules in dependency order + var engines = Enumerable.Empty(); var themeLocation = theme.Location + "/" + theme.Id; + // 1. current 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.Id)); - var activeModuleLocations = activeFeatures.Select(fd => fd.Extension.Location + "/" + fd.Extension.Id).Distinct(); - var moduleParams = new CreateModulesViewEngineParams { VirtualPaths = activeModuleLocations }; + // 2. Base themes of the current theme (in "base" order) + var baseThemes = GetBaseThemes(theme); + + // 3. Active features from modules in dependency order + var enabledModules = _extensionManager.EnabledFeatures(_shellDescriptor) + .Reverse() // reverse from (C <= B <= A) to (A => B => C) + .Where(fd => !DefaultExtensionTypes.IsTheme(fd.Extension.ExtensionType)/*core + modules*/); + + var allLocations = baseThemes.Concat(enabledModules) + .Select(fd => fd.Extension.Location + "/" + fd.Extension.Id) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + var moduleParams = new CreateModulesViewEngineParams { VirtualPaths = allLocations }; engines = engines.Concat(_viewEngineProviders.Select(vep => vep.CreateModulesViewEngine(moduleParams))); return new ViewEngineCollectionWrapper(engines); }); } + private IEnumerable GetBaseThemes(ExtensionDescriptor themeExtension) { + if (themeExtension.Id.Equals("TheAdmin", StringComparison.OrdinalIgnoreCase)) { + // Special case: conceptually, the base themes of "TheAdmin" is the list of all + // enabled themes. This is so that any enabled theme can have controller/action/views + // in the Admin of the site. + return _extensionManager + .EnabledFeatures(_shellDescriptor) + .Reverse() // reverse from (C <= B <= A) to (A => B => C) + .Where(fd => DefaultExtensionTypes.IsTheme(fd.Extension.ExtensionType)); + } + else { + var availableFeatures = _extensionManager.AvailableFeatures(); + var list = new List(); + while(true) { + if (themeExtension == null) + break; + + if (String.IsNullOrEmpty(themeExtension.BaseTheme)) + break; + + var baseFeature = availableFeatures.FirstOrDefault(fd => fd.Id == themeExtension.BaseTheme); + if (baseFeature == null) { + Logger.Error("Base theme '{0}' of theme '{1}' not found in list of features", themeExtension.BaseTheme, themeExtension.Id); + break; + } + + // Protect against potential infinite loop + if (list.Contains(baseFeature)) { + Logger.Error("Base theme '{0}' of theme '{1}' ignored, as it seems there is recursion in base themes", themeExtension.BaseTheme, themeExtension.Id); + break; + } + + list.Add(baseFeature); + + themeExtension = baseFeature.Extension; + } + return list; + } + } + public void ReleaseView(ControllerContext controllerContext, IView view) { throw new NotImplementedException(); }