Nested Themes. Also, enabling a theme now really enables its features.

--HG--
branch : dev
This commit is contained in:
Dave Reed
2010-10-12 12:21:46 -07:00
parent 62eb8101af
commit 7811d4fcdd
18 changed files with 205 additions and 22 deletions

View File

@@ -17,8 +17,10 @@ using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.Security;
using Orchard.Tests.Modules;
using Orchard.Tests.Stubs;
using Orchard.UI.Notify;
namespace Orchard.Core.Tests.Body {
@@ -41,6 +43,7 @@ namespace Orchard.Core.Tests.Body {
builder.RegisterType<DefaultContentQuery>().As<IContentQuery>();
builder.RegisterType<BodyPartHandler>().As<IContentHandler>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
}
[Test]

View File

@@ -20,12 +20,14 @@ using Orchard.Core.Scheduling.Services;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Security;
using Orchard.Tasks.Scheduling;
using Orchard.Tests.Modules;
using Orchard.Core.Common.ViewModels;
using System.Web.Mvc;
using Orchard.Tests.Stubs;
namespace Orchard.Core.Tests.Common.Providers {
[TestFixture]
@@ -46,6 +48,7 @@ namespace Orchard.Core.Tests.Common.Providers {
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
_authn = new Mock<IAuthenticationService>();
_authz = new Mock<IAuthorizationService>();

View File

@@ -20,6 +20,7 @@ using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.Security;
using Orchard.Tests.Modules;
using System.Web.Mvc;
@@ -56,6 +57,8 @@ namespace Orchard.Core.Tests.Routable.Services {
builder.RegisterType<DefaultContentQuery>().As<IContentQuery>();
builder.RegisterInstance(new UrlHelper(new RequestContext(new StubHttpContext("~/"), new RouteData()))).As<UrlHelper>();
builder.RegisterType<RoutePartHandler>().As<IContentHandler>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
}
private IRoutableService _routableService;

View File

@@ -12,9 +12,11 @@ using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions;
using Orchard.Tasks;
using Orchard.Tasks.Scheduling;
using Orchard.Tests.Modules;
using Orchard.Tests.Stubs;
namespace Orchard.Core.Tests.Scheduling {
[TestFixture]
@@ -40,6 +42,8 @@ namespace Orchard.Core.Tests.Scheduling {
builder.RegisterType<ScheduledTaskExecutor>().As<IBackgroundTask>().Named("ScheduledTaskExecutor", typeof(IBackgroundTask));
builder.RegisterInstance(_handler).As<IScheduledTaskHandler>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
}
protected override IEnumerable<Type> DatabaseTypes {

View File

@@ -13,8 +13,10 @@ using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions;
using Orchard.Tasks.Scheduling;
using Orchard.Tests.Modules;
using Orchard.Tests.Stubs;
namespace Orchard.Core.Tests.Scheduling {
[TestFixture]
@@ -43,6 +45,8 @@ namespace Orchard.Core.Tests.Scheduling {
builder.RegisterInstance(new Mock<IContentDefinitionManager>().Object);
builder.RegisterType<ScheduledTaskManager>().As<IScheduledTaskManager>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
}
protected override IEnumerable<Type> DatabaseTypes {

View File

@@ -20,11 +20,13 @@ using Orchard.Environment;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.Records;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Messaging.Events;
using Orchard.Messaging.Services;
using Orchard.Security;
using Orchard.Security.Permissions;
using Orchard.Tests.Stubs;
using Orchard.UI.Notify;
using Orchard.Users.Controllers;
using Orchard.Users.Handlers;
@@ -57,6 +59,7 @@ namespace Orchard.Tests.Modules.Users.Controllers {
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
builder.RegisterInstance(new Mock<INotifier>().Object);
_authorizer = new Mock<IAuthorizer>();
builder.RegisterInstance(_authorizer.Object);

View File

@@ -17,9 +17,11 @@ using Orchard.ContentManagement.Records;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions;
using Orchard.Messaging.Events;
using Orchard.Messaging.Services;
using Orchard.Security;
using Orchard.Tests.Stubs;
using Orchard.Users.Handlers;
using Orchard.Users.Models;
using Orchard.Users.Services;
@@ -80,6 +82,7 @@ namespace Orchard.Tests.Modules.Users.Services {
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
_session = _sessionFactory.OpenSession();
builder.RegisterInstance(new TestSessionLocator(_session)).As<ISessionLocator>();
_container = builder.Build();

View File

@@ -15,7 +15,9 @@ using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.Security;
using Orchard.Tests.Stubs;
using Orchard.Themes;
using Orchard.Themes.Models;
using Orchard.UI.Notify;
@@ -72,7 +74,7 @@ namespace Orchard.Tests.Modules.Widgets.Services {
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<WidgetsService>().As<IWidgetsService>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
Theme theme1 = new Theme { Zones = ThemeZoneName1 };
Theme theme2 = new Theme { Zones = ThemeZoneName2 };
Mock<IThemeService> themeServiceMock = new Mock<IThemeService>();

View File

@@ -10,9 +10,11 @@ using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.Records;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.Environment.Extensions;
using Orchard.Tests.ContentManagement.Records;
using Orchard.Tests.ContentManagement.Models;
using Orchard.DisplayManagement.Implementation;
using Orchard.Tests.Stubs;
namespace Orchard.Tests.ContentManagement {
[TestFixture]
@@ -64,6 +66,8 @@ namespace Orchard.Tests.ContentManagement {
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
_session = _sessionFactory.OpenSession();
builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();

View File

@@ -12,12 +12,14 @@ using Orchard.Data;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.Records;
using Orchard.Environment.Extensions;
using Orchard.Tests.ContentManagement.Records;
using Orchard.Tests.ContentManagement.Models;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement;
using System.Collections.Generic;
using Orchard.Tests.Stubs;
namespace Orchard.Tests.ContentManagement {
[TestFixture]
@@ -72,6 +74,8 @@ namespace Orchard.Tests.ContentManagement {
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
_session = _sessionFactory.OpenSession();

View File

@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Autofac;
using NUnit.Framework;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
namespace Orchard.Tests.DisplayManagement.Descriptors {
@@ -12,12 +15,51 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
protected override void Register(Autofac.ContainerBuilder builder) {
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
var features = new [] {
new FeatureDescriptor {
Name = "Theme1",
Extension = new ExtensionDescriptor {
Name = "Theme1",
ExtensionType = "Theme"
}
},
new FeatureDescriptor {
Name = "DerivedTheme",
Extension = new ExtensionDescriptor {
Name = "DerivedTheme",
ExtensionType = "Theme",
BaseTheme = "BaseTheme"
}
},
new FeatureDescriptor {
Name = "BaseTheme",
Extension = new ExtensionDescriptor {
Name = "BaseTheme",
ExtensionType = "Theme"
}
}
};
builder.RegisterInstance<IExtensionManager>(new TestExtensionManager(features));
TestShapeProvider.FeatureShapes = new Dictionary<Feature, IEnumerable<string>> {
{ TestFeature(), new [] {"Hello"} },
{ Feature(features[0]), new [] {"Theme1Shape"} },
{ Feature(features[1]), new [] {"DerivedShape", "OverriddenShape"} },
{ Feature(features[2]), new [] {"BaseShape", "OverriddenShape"} }
};
builder.RegisterType<TestShapeProvider>().As<IShapeTableProvider>()
.WithMetadata("Feature", TestFeature())
.WithMetadata("Features", TestFeature())
.As<TestShapeProvider>()
.InstancePerLifetimeScope();
}
static Feature Feature(FeatureDescriptor descriptor) {
return new Feature {
Descriptor = descriptor
};
}
static Feature TestFeature() {
return new Feature {
Descriptor = new FeatureDescriptor {
@@ -31,12 +73,45 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
};
}
public class TestExtensionManager : IExtensionManager {
private readonly IEnumerable<FeatureDescriptor> _availableFeautures;
public TestExtensionManager(IEnumerable<FeatureDescriptor> availableFeautures) {
_availableFeautures = availableFeautures;
}
public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
throw new NotSupportedException();
}
public IEnumerable<FeatureDescriptor> AvailableFeatures() {
return _availableFeautures;
}
public IEnumerable<Feature> LoadFeatures(IEnumerable<FeatureDescriptor> featureDescriptors) {
throw new NotSupportedException();
}
public void InstallExtension(string extensionType, HttpPostedFileBase extensionBundle) {
throw new NotSupportedException();
}
public void UninstallExtension(string extensionType, string extensionName) {
throw new NotSupportedException();
}
}
public class TestShapeProvider : IShapeTableProvider {
public static IDictionary<Feature, IEnumerable<string>> FeatureShapes;
public Action<ShapeTableBuilder> Discover = x => { };
void IShapeTableProvider.Discover(ShapeTableBuilder builder) {
builder.Describe("Hello");
foreach (var pair in FeatureShapes) {
foreach (var shape in pair.Value) {
builder.Describe(shape).From(pair.Key).BoundAs(pair.Key.Descriptor.Name, null);
}
}
Discover(builder);
}
}
@@ -64,7 +139,7 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
Action<ShapeDisplayedContext> cb4 = x => { };
_container.Resolve<TestShapeProvider>().Discover =
builder => builder.Describe("Foo")
builder => builder.Describe("Foo").From(TestFeature())
.OnCreating(cb1)
.OnCreated(cb2)
.OnDisplaying(cb3)
@@ -79,5 +154,34 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
Assert.That(foo.Displaying.Single(), Is.SameAs(cb3));
Assert.That(foo.Displayed.Single(), Is.SameAs(cb4));
}
[Test]
public void OnlyShapesFromTheGivenThemeAreProvided() {
_container.Resolve<TestShapeProvider>();
var manager = _container.Resolve<IShapeTableManager>();
var table = manager.GetShapeTable("Theme1");
Assert.IsTrue(table.Descriptors.ContainsKey("Theme1Shape"));
Assert.IsFalse(table.Descriptors.ContainsKey("DerivedShape"));
Assert.IsFalse(table.Descriptors.ContainsKey("BaseShape"));
}
[Test]
public void ShapesFromTheBaseThemeAreProvided() {
_container.Resolve<TestShapeProvider>();
var manager = _container.Resolve<IShapeTableManager>();
var table = manager.GetShapeTable("DerivedTheme");
Assert.IsFalse(table.Descriptors.ContainsKey("Theme1Shape"));
Assert.IsTrue(table.Descriptors.ContainsKey("DerivedShape"));
Assert.IsTrue(table.Descriptors.ContainsKey("BaseShape"));
}
[Test]
public void DerivedThemesCanOverrideBaseThemeShapeBindings() {
_container.Resolve<TestShapeProvider>();
var manager = _container.Resolve<IShapeTableManager>();
var table = manager.GetShapeTable("DerivedTheme");
Assert.IsTrue(table.Bindings.ContainsKey("OverriddenShape"));
Assert.AreEqual("DerivedTheme", table.Descriptors["OverriddenShape"].BindingSource);
}
}
}

View File

@@ -8,6 +8,8 @@ using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement.Shapes;
using Orchard.Environment.Extensions;
using Orchard.Tests.Stubs;
namespace Orchard.Tests.DisplayManagement {
[TestFixture]
@@ -20,6 +22,7 @@ namespace Orchard.Tests.DisplayManagement {
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
_container = builder.Build();
}

View File

@@ -8,6 +8,8 @@ using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement.Shapes;
using Orchard.Environment.Extensions;
using Orchard.Tests.Stubs;
namespace Orchard.Tests.DisplayManagement {
[TestFixture]
@@ -20,6 +22,7 @@ namespace Orchard.Tests.DisplayManagement {
builder.RegisterType<ShapeHelperFactory>().As<IShapeHelperFactory>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
_container = builder.Build();
}

View File

@@ -244,6 +244,7 @@
<Compile Include="Mvc\Routes\UrlPrefixTests.cs" />
<Compile Include="Records\BigRecord.cs" />
<Compile Include="Scripting\ScriptingTests.cs" />
<Compile Include="Stubs\StubExtensionManager.cs" />
<Compile Include="Stubs\StubReportsCoordinator.cs" />
<Compile Include="Stubs\StubVirtualPathProvider.cs" />
<Compile Include="Stubs\StubFileSystem.cs" />

View File

@@ -119,7 +119,7 @@ namespace Orchard.Themes.Services {
}
while (themes.Count > 0)
_moduleService.DisableFeatures(new[] {themes.Pop()});
_moduleService.EnableFeatures(new[] {themes.Pop()});
}
public ITheme GetRequestTheme(RequestContext requestContext) {

View File

@@ -3,7 +3,9 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Autofac.Features.Metadata;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Utility;
namespace Orchard.DisplayManagement.Descriptors {
@@ -13,8 +15,12 @@ namespace Orchard.DisplayManagement.Descriptors {
public class DefaultShapeTableManager : IShapeTableManager {
private readonly IEnumerable<Meta<IShapeTableProvider, IFeatureMetadata>> _bindingStrategies;
private readonly IExtensionManager _extensionManager;
public DefaultShapeTableManager(IEnumerable<Meta<IShapeTableProvider, IFeatureMetadata>> bindingStrategies) {
public DefaultShapeTableManager(
IEnumerable<Meta<IShapeTableProvider, IFeatureMetadata>> bindingStrategies,
IExtensionManager extensionManager) {
_extensionManager = extensionManager;
_bindingStrategies = bindingStrategies;
}
@@ -30,7 +36,8 @@ namespace Orchard.DisplayManagement.Descriptors {
}
var alterations = builderFactory.BuildAlterations()
.Where(alteration => IsModuleOrRequestedTheme(alteration, themeName));
.Where(alteration => IsModuleOrRequestedTheme(alteration, themeName))
.OrderByDependencies(AlterationHasDependency);
var descriptors = alterations.GroupBy(alteration => alteration.ShapeType, StringComparer.OrdinalIgnoreCase)
.Select(group => group.Aggregate(
@@ -47,8 +54,11 @@ namespace Orchard.DisplayManagement.Descriptors {
});
}
private static bool AlterationHasDependency(ShapeAlteration item, ShapeAlteration subject) {
return ExtensionManager.HasDependency(item.Feature.Descriptor, subject.Feature.Descriptor);
}
static bool IsModuleOrRequestedTheme(ShapeAlteration alteration, string themeName) {
private bool IsModuleOrRequestedTheme(ShapeAlteration alteration, string themeName) {
if (alteration == null ||
alteration.Feature == null ||
alteration.Feature.Descriptor == null ||
@@ -57,10 +67,36 @@ namespace Orchard.DisplayManagement.Descriptors {
}
var extensionType = alteration.Feature.Descriptor.Extension.ExtensionType;
var featureName = alteration.Feature.Descriptor.Name;
return extensionType == "Module" ||
(extensionType == "Theme" && featureName == themeName);
if (extensionType == "Module") {
return true;
}
if (extensionType == "Theme") {
// alterations from themes must be from the given theme or a base theme
var featureName = alteration.Feature.Descriptor.Name;
return featureName == themeName || IsBaseTheme(featureName, themeName);
}
return false;
}
private bool IsBaseTheme(string featureName, string themeName) {
// determine if the given feature is a base theme of the given theme
var availableFeatures = _extensionManager.AvailableFeatures();
var themeFeature = availableFeatures.SingleOrDefault(fd => fd.Name == themeName);
while(themeFeature != null) {
var baseTheme = themeFeature.Extension.BaseTheme;
if (String.IsNullOrEmpty(baseTheme)) {
return false;
}
if (featureName == baseTheme) {
return true;
}
themeFeature = availableFeatures.SingleOrDefault(fd => fd.Name == baseTheme);
}
return false;
}
class ShapeTableBuilderFactory {

View File

@@ -38,7 +38,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
var harvesterInfos = _harvesters.Select(harvester => new { harvester, subPaths = harvester.SubPaths() });
var availableFeatures = _extensionManager.AvailableFeatures();
var activeFeatures = availableFeatures.Where(fd => FeatureIsTheme(fd) || FeatureIsEnabled(fd));
var activeFeatures = availableFeatures.Where(FeatureIsEnabled);
var activeExtensions = Once(activeFeatures);
var hits = activeExtensions.SelectMany(extensionDescriptor => {
@@ -81,12 +81,9 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
}
}
private bool FeatureIsTheme(FeatureDescriptor fd) {
return fd.Extension.ExtensionType == "Theme";
}
private bool FeatureIsEnabled(FeatureDescriptor fd) {
return _shellDescriptor.Features.Any(sf => sf.Name == fd.Name);
return (fd.Extension.ExtensionType == "Theme" && fd.Name == "TheAdmin") ||
_shellDescriptor.Features.Any(sf => sf.Name == fd.Name);
}
private IHtmlString Render(ShapeDescriptor shapeDescriptor, DisplayContext displayContext, HarvestShapeInfo harvestShapeInfo, HarvestShapeHit harvestShapeHit) {

View File

@@ -48,17 +48,23 @@ namespace Orchard.Environment.Extensions {
/// <param name="item"></param>
/// <param name="subject"></param>
/// <returns></returns>
static bool HasDependency(FeatureDescriptor item, FeatureDescriptor subject) {
// Themes implicitly depend on modules to ensure build and override ordering
if (item.Extension.ExtensionType == "Theme" && subject.Extension.ExtensionType == "Module")
return true;
internal static bool HasDependency(FeatureDescriptor item, FeatureDescriptor subject) {
if (item.Extension.ExtensionType == "Theme") {
// Themes implicitly depend on modules to ensure build and override ordering
if (subject.Extension.ExtensionType == "Module") {
return true;
}
if (subject.Extension.ExtensionType == "Theme") {
// theme depends on another if it is its base theme
return item.Extension.BaseTheme == subject.Name;
}
}
// Return based on explicit dependencies
return item.Dependencies != null &&
item.Dependencies.Any(x => StringComparer.OrdinalIgnoreCase.Equals(x, subject.Name));
}
private IEnumerable<ExtensionEntry> LoadedExtensions() {
foreach ( var descriptor in AvailableExtensions() ) {
ExtensionEntry entry = null;