diff --git a/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs b/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs index dee0f004e..031a042f9 100644 --- a/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs +++ b/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs @@ -20,7 +20,10 @@ namespace Orchard.Tests.DisplayManagement { WorkContext _workContext; protected override void Register(Autofac.ContainerBuilder builder) { - _defaultShapeTable = new ShapeTable { Descriptors = new Dictionary() }; + _defaultShapeTable = new ShapeTable { + Descriptors = new Dictionary(), + Bindings = new Dictionary() + }; _workContext = new TestWorkContext { CurrentTheme = new Theme { ThemeName = "Hello" } }; @@ -85,11 +88,10 @@ namespace Orchard.Tests.DisplayManagement { public IContainerProvider ContainerProvider { get; set; } public override T Resolve() { - if (typeof(T) == typeof(ILifetimeScope)) - { - return (T) ContainerProvider.RequestLifetime; + if (typeof(T) == typeof(ILifetimeScope)) { + return (T)ContainerProvider.RequestLifetime; } - + throw new NotImplementedException(); } @@ -104,8 +106,10 @@ namespace Orchard.Tests.DisplayManagement { } void AddShapeDescriptor(ShapeDescriptor shapeDescriptor) { + _defaultShapeTable.Descriptors[shapeDescriptor.ShapeType] = shapeDescriptor; foreach (var binding in shapeDescriptor.Bindings) { - _defaultShapeTable.Descriptors[binding.Key] = shapeDescriptor; + binding.Value.ShapeDescriptor = shapeDescriptor; + _defaultShapeTable.Bindings[binding.Key] = binding.Value; } } @@ -163,7 +167,7 @@ namespace Orchard.Tests.DisplayManagement { } [Test] - public void RenderAlternateShape() { + public void RenderAlternateShapeExplicitly() { var displayManager = _container.Resolve(); var shape = new Shape { @@ -188,5 +192,39 @@ namespace Orchard.Tests.DisplayManagement { var result = displayManager.Execute(CreateDisplayContext(shape)); Assert.That(result.ToString(), Is.EqualTo("Hello again!")); } + + [Test] + public void RenderAlternateShapeByMostRecentlyAddedMatchingAlternate() { + var displayManager = _container.Resolve(); + + var shape = new Shape { + Metadata = new ShapeMetadata { + Type = "Foo" + } + }; + shape.Metadata.Alternates.Add("Foo__1"); + shape.Metadata.Alternates.Add("Foo__2"); + shape.Metadata.Alternates.Add("Foo__3"); + + var descriptor = new ShapeDescriptor { + ShapeType = "Foo", + }; + descriptor.Bindings["Foo"] = new ShapeBinding { + BindingName = "Foo", + Binding = ctx => new HtmlString("Hi there!"), + }; + descriptor.Bindings["Foo__1"] = new ShapeBinding { + BindingName = "Foo__1", + Binding = ctx => new HtmlString("Hello (1)!"), + }; + descriptor.Bindings["Foo__2"] = new ShapeBinding { + BindingName = "Foo__2", + Binding = ctx => new HtmlString("Hello (2)!"), + }; + AddShapeDescriptor(descriptor); + + var result = displayManager.Execute(CreateDisplayContext(shape)); + Assert.That(result.ToString(), Is.EqualTo("Hello (2)!")); + } } } diff --git a/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs b/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs index f8c97eee8..35dc34811 100644 --- a/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs +++ b/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs @@ -41,12 +41,13 @@ namespace Orchard.DisplayManagement.Descriptors { })); return new ShapeTable { - Descriptors = descriptors.ToDictionary(sd => sd.ShapeType) + Descriptors = descriptors.ToDictionary(sd => sd.ShapeType), + Bindings = descriptors.SelectMany(sd => sd.Bindings).ToDictionary(kv => kv.Key, kv => kv.Value), }; }); } - + static bool IsModuleOrRequestedTheme(ShapeAlteration alteration, string themeName) { if (alteration == null || alteration.Feature == null || diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs index ab1816449..476d0a9be 100644 --- a/src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs @@ -3,5 +3,6 @@ namespace Orchard.DisplayManagement.Descriptors { public class ShapeTable { public IDictionary Descriptors { get; set; } + public IDictionary Bindings { get; set; } } } \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs index f182cd965..257f14bb1 100644 --- a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs +++ b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Dynamic; +using System.Linq; using System.Linq.Expressions; using System.Runtime.CompilerServices; using System.Web; @@ -50,7 +52,7 @@ namespace Orchard.DisplayManagement.Implementation { ShapeDescriptor shapeDescriptor; ShapeBinding shapeBinding; - if (TryGetDescriptor(shapeMetadata.Type, shapeTable, out shapeDescriptor, out shapeBinding)) { + if (TryGetDescriptorBinding(shapeMetadata.Type, shapeMetadata.Alternates, shapeTable, out shapeDescriptor, out shapeBinding)) { shape.Metadata.ChildContent = Process(shapeDescriptor, shapeBinding, shape, context); } else { @@ -60,7 +62,7 @@ namespace Orchard.DisplayManagement.Implementation { foreach (var frameType in shape.Metadata.Wrappers) { ShapeDescriptor frameDescriptor; ShapeBinding frameBinding; - if (TryGetDescriptor(frameType, shapeTable, out frameDescriptor, out frameBinding)) { + if (TryGetDescriptorBinding(frameType, Enumerable.Empty(), shapeTable, out frameDescriptor, out frameBinding)) { shape.Metadata.ChildContent = Process(frameDescriptor, frameBinding, shape, context); } } @@ -68,18 +70,32 @@ namespace Orchard.DisplayManagement.Implementation { return shape.Metadata.ChildContent; } - static bool TryGetDescriptor(string shapeType, ShapeTable shapeTable, out ShapeDescriptor shapeDescriptor, out ShapeBinding shapeBinding) { + static bool TryGetDescriptorBinding(string shapeType, IEnumerable shapeAlternates, ShapeTable shapeTable, out ShapeDescriptor shapeDescriptor, out ShapeBinding shapeBinding) { + // shape alternates are optional, fully qualified binding names + // the earliest added alternates have the lowest priority + // the descriptor returned is based on the binding that is matched, so it may be an entirely + // different descriptor if the alternate has a different base name + foreach (var shapeAlternate in shapeAlternates.Reverse()) { + if (shapeTable.Bindings.TryGetValue(shapeAlternate, out shapeBinding)) { + shapeDescriptor = shapeBinding.ShapeDescriptor; + return true; + } + } + // when no alternates match, the shapeType is used to find the longest matching binding + // the shapetype name can break itself into shorter fallbacks at double-underscore marks + // so the shapetype itself may contain a longer alternate forms that falls back to a shorter one var shapeTypeScan = shapeType; for (; ; ) { - if (shapeTable.Descriptors.TryGetValue(shapeTypeScan, out shapeDescriptor) && - shapeDescriptor.Bindings.TryGetValue(shapeTypeScan, out shapeBinding)) { + if (shapeTable.Bindings.TryGetValue(shapeTypeScan, out shapeBinding)) { + shapeDescriptor = shapeBinding.ShapeDescriptor; return true; } var delimiterIndex = shapeTypeScan.LastIndexOf("__"); if (delimiterIndex < 0) { shapeBinding = null; + shapeDescriptor = null; return false; } diff --git a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs index 92d0b7642..a1a6c9712 100644 --- a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs +++ b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs @@ -6,11 +6,13 @@ namespace Orchard.DisplayManagement.Shapes { public class ShapeMetadata { public ShapeMetadata() { Wrappers = new List(); + Alternates = new List(); } public string Type { get; set; } public string Position { get; set; } public IList Wrappers { get; set; } + public IList Alternates { get; set; } public bool WasExecuted { get; set; } public IHtmlString ChildContent { get; set; }