diff --git a/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs b/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs
index a702b1d20..889a47292 100644
--- a/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs
+++ b/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs
@@ -21,9 +21,9 @@ namespace Orchard.Specs.Bindings {
webApp.GivenIHaveACleanSiteWith(
virtualDirectory,
TableData(
- new { extension = DefaultExtensionTypes.Module, names = "Orchard.Setup, Orchard.Pages, Orchard.Blogs, Orchard.Messaging, Orchard.Modules, Orchard.Packaging, Orchard.PublishLater, Orchard.Themes, Orchard.Scripting, Orchard.Widgets, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.jQuery, Orchard.Tags, TinyMce" },
- new { extension = DefaultExtensionTypes.Core, names = "Common, Dashboard, Feeds, HomePage, Navigation, Contents, Routable, Scheduling, Settings, Shapes, XmlRpc" },
- new { extension = DefaultExtensionTypes.Theme, names = "SafeMode, TheAdmin, TheThemeMachine" }));
+ new { extension = "Module", names = "Orchard.Setup, Orchard.Pages, Orchard.Blogs, Orchard.Messaging, Orchard.Modules, Orchard.Packaging, Orchard.PublishLater, Orchard.Themes, Orchard.Scripting, Orchard.Widgets, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.jQuery, Orchard.Tags, TinyMce" },
+ new { extension = "Core", names = "Common, Dashboard, Feeds, HomePage, Navigation, Contents, Routable, Scheduling, Settings, Shapes, XmlRpc" },
+ new { extension = "Theme", names = "SafeMode, TheAdmin, TheThemeMachine" }));
webApp.WhenIGoTo("Setup");
diff --git a/src/Orchard.Specs/Bindings/WebAppHosting.cs b/src/Orchard.Specs/Bindings/WebAppHosting.cs
index 7d3d839fc..7648b2e9d 100644
--- a/src/Orchard.Specs/Bindings/WebAppHosting.cs
+++ b/src/Orchard.Specs/Bindings/WebAppHosting.cs
@@ -140,13 +140,13 @@ namespace Orchard.Specs.Bindings {
foreach (var row in table.Rows) {
foreach (var name in row["names"].Split(',').Select(x => x.Trim())) {
switch (row["extension"]) {
- case DefaultExtensionTypes.Core:
+ case "Core":
GivenIHaveCore(name);
break;
- case DefaultExtensionTypes.Module:
+ case "Module":
GivenIHaveModule(name);
break;
- case DefaultExtensionTypes.Theme:
+ case "Theme":
GivenIHaveTheme(name);
break;
default:
diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.cshtml b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.cshtml
index 0b5ff95c8..3f252a303 100644
--- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.cshtml
+++ b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.cshtml
@@ -25,7 +25,7 @@
}
//temporarily "disable" actions on core features
- var showActions = categoryName.ToString() != "Core";
+ bool showActions;
@categoryName
@{
@@ -46,9 +46,7 @@
select (from f in Model.Features where f.Descriptor.Id == d select f).SingleOrDefault()).Where(f => f != null).OrderBy(f => f.Descriptor.Name);
var missingDependencies = feature.Descriptor.Dependencies
.Where(d => !Model.Features.Any(f => f.Descriptor.Id == d));
- if (showActions) {
- showActions = missingDependencies.Count() == 0;
- }
+ showActions = categoryName.ToString() != "Core" && missingDependencies.Count() == 0;
-
diff --git a/src/Orchard.Web/Web.config b/src/Orchard.Web/Web.config
index 653221a2a..68c8c0377 100644
--- a/src/Orchard.Web/Web.config
+++ b/src/Orchard.Web/Web.config
@@ -16,7 +16,7 @@
-
+
@@ -143,7 +143,8 @@
-
+
+
diff --git a/src/Orchard/Environment/Extensions/IExtensionManager.cs b/src/Orchard/Environment/Extensions/IExtensionManager.cs
index 723a82cd0..b79f0b95c 100644
--- a/src/Orchard/Environment/Extensions/IExtensionManager.cs
+++ b/src/Orchard/Environment/Extensions/IExtensionManager.cs
@@ -15,13 +15,7 @@ namespace Orchard.Environment.Extensions {
public static class ExtensionManagerExtensions {
public static IEnumerable EnabledFeatures(this IExtensionManager extensionManager, ShellDescriptor descriptor) {
- return extensionManager.AvailableExtensions()
- .SelectMany(extensionDescriptor => extensionDescriptor.Features)
- .Where(featureDescriptor => IsFeatureEnabledInDescriptor(featureDescriptor, descriptor));
- }
-
- private static bool IsFeatureEnabledInDescriptor(FeatureDescriptor featureDescriptor, ShellDescriptor shellDescriptor) {
- return shellDescriptor.Features.Any(shellDescriptorFeature => shellDescriptorFeature.Name == featureDescriptor.Id);
+ return extensionManager.AvailableFeatures().Where(fd => descriptor.Features.Any(sf => sf.Name == fd.Id));
}
}
}
diff --git a/src/Orchard/Environment/Extensions/Models/DefaultExtensionTypes.cs b/src/Orchard/Environment/Extensions/Models/DefaultExtensionTypes.cs
index 607fe8fcf..372cd2d77 100644
--- a/src/Orchard/Environment/Extensions/Models/DefaultExtensionTypes.cs
+++ b/src/Orchard/Environment/Extensions/Models/DefaultExtensionTypes.cs
@@ -4,7 +4,6 @@ namespace Orchard.Environment.Extensions.Models {
public static class DefaultExtensionTypes {
public const string Module = "Module";
public const string Theme = "Theme";
- public const string Core = "Core";
public static bool IsModule(string extensionType) {
return string.Equals(extensionType, Module, StringComparison.OrdinalIgnoreCase);
diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/ThemeAwareViewEngine.cs
index c7484e827..55ae0988a 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,82 @@ 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
+ engines = engines.Concat(CreateThemeViewEngines(theme));
- var themeParams = new CreateThemeViewEngineParams { VirtualPath = themeLocation };
- engines = engines.Concat(_viewEngineProviders.Select(vep => vep.CreateThemeViewEngine(themeParams)));
+ // 2. Base themes of the current theme (in "base" order)
+ engines = GetBaseThemes(theme).Aggregate(engines, (current, baseTheme) => current.Concat(CreateThemeViewEngines(baseTheme)));
- 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 };
+ // 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.IsModule(fd.Extension.ExtensionType));
+
+ var allLocations = enabledModules.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 CreateThemeViewEngines(ExtensionDescriptor theme) {
+ var themeLocation = theme.Location + "/" + theme.Id;
+ var themeParams = new CreateThemeViewEngineParams {VirtualPath = themeLocation};
+ return _viewEngineProviders.Select(vep => vep.CreateThemeViewEngine(themeParams));
+ }
+
+ 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)
+ .Select(fd => fd.Extension)
+ .Where(fd => DefaultExtensionTypes.IsTheme(fd.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.Extension)) {
+ 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.Extension);
+
+ themeExtension = baseFeature.Extension;
+ }
+ return list;
+ }
+ }
+
public void ReleaseView(ControllerContext controllerContext, IView view) {
throw new NotImplementedException();
}