From 77c48fb42a6a12405e14bd32903eabfc6a933c35 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Sun, 5 Sep 2010 04:06:18 -0700 Subject: [PATCH] More theming work Continued work towards making zone classes obsolete Shape table construction per theme Adding display helper to webforms view classes --HG-- branch : theming --- .hgsubstate | 2 +- .../Orchard.Framework.Tests.csproj | 6 +- src/Orchard.Tests/UI/ShapeTests.cs | 136 ++++++++++++++++++ .../Core/Feeds/Services/FeedFilter.cs | 4 +- .../Modules/Orchard.Setup/SetupMode.cs | 14 +- src/Orchard.Web/Orchard.Web.csproj | 1 + .../Themes/SafeMode/Views/Layout.ascx | 14 +- .../Themes/SafeMode/Views/Zone.ascx | 4 + .../Descriptors/DefaultShapeTableManager.cs | 46 +++--- .../Descriptors/Interfaces.cs | 107 +------------- .../Descriptors/ShapeDescriptor.cs | 22 +++ .../Descriptors/ShapeDescriptorAlteration.cs | 23 +++ .../ShapeDescriptorAlterationBuilder.cs | 64 +++++++++ .../Descriptors/ShapeTable.cs | 7 + .../Descriptors/ShapeTableBuilder.cs | 26 ++++ .../Implementation/DefaultDisplayManager.cs | 13 +- .../Implementation/DefaultShapeFactory.cs | 49 +++++-- .../PageWorkContextStateProvider.cs | 36 ----- .../Environment/DefaultWorkContextAccessor.cs | 7 +- .../Mvc/ViewEngines/Razor/RazorViewEngine.cs | 16 +++ .../Razor/RazorViewEngineProvider.cs | 4 +- .../Mvc/ViewEngines/Razor/WebViewPage.cs | 12 +- .../ThemeAwareness/LayoutAwareViewEngine.cs | 2 +- .../WebForms/WebFormViewEngineProvider.cs | 22 ++- src/Orchard/Mvc/ViewPage.cs | 27 ++-- src/Orchard/Mvc/ViewUserControl.cs | 28 ++-- src/Orchard/Orchard.Framework.csproj | 36 +++-- src/Orchard/UI/IPage.cs | 12 +- .../ContentItemDisplayZoneItem.cs | 0 .../ContentPartDisplayZoneItem.cs | 0 .../ContentPartEditorZoneItem.cs | 0 .../Zones/{ => Obsolete}/DelegateZoneItem.cs | 0 .../UI/Zones/{ => Obsolete}/IZoneManager.cs | 0 .../{ => Obsolete}/IZoneManagerEvents.cs | 0 .../{ => Obsolete}/RenderActionZoneItem.cs | 0 .../{ => Obsolete}/RenderPartialZoneItem.cs | 0 .../{ => Obsolete}/RenderStaticZoneItem.cs | 0 .../UI/Zones/{ => Obsolete}/ZoneCollection.cs | 0 .../UI/Zones/{ => Obsolete}/ZoneEntry.cs | 0 .../UI/Zones/{ => Obsolete}/ZoneItem.cs | 0 .../UI/Zones/{ => Obsolete}/ZoneManager.cs | 0 src/Orchard/UI/Zones/PageWorkContext.cs | 39 +++++ .../Zones/ZoneHoldingBehavior.cs | 39 +++-- src/Orchard/WorkContext.cs | 4 +- 44 files changed, 572 insertions(+), 250 deletions(-) create mode 100644 src/Orchard.Tests/UI/ShapeTests.cs create mode 100644 src/Orchard.Web/Themes/SafeMode/Views/Zone.ascx create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlteration.cs create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlterationBuilder.cs create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapeTableBuilder.cs delete mode 100644 src/Orchard/DisplayManagement/Implementation/PageWorkContextStateProvider.cs create mode 100644 src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngine.cs rename src/Orchard/UI/Zones/{ => Obsolete}/ContentItemDisplayZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/ContentPartDisplayZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/ContentPartEditorZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/DelegateZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/IZoneManager.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/IZoneManagerEvents.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/RenderActionZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/RenderPartialZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/RenderStaticZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/ZoneCollection.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/ZoneEntry.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/ZoneItem.cs (100%) rename src/Orchard/UI/Zones/{ => Obsolete}/ZoneManager.cs (100%) create mode 100644 src/Orchard/UI/Zones/PageWorkContext.cs rename src/Orchard/{DisplayManagement => UI}/Zones/ZoneHoldingBehavior.cs (73%) diff --git a/.hgsubstate b/.hgsubstate index d2167433f..a0575ff4a 100644 --- a/.hgsubstate +++ b/.hgsubstate @@ -1 +1 @@ -ccaf72b5e2d5fea43a6f15b3a912b631e3c0302d Clay +3bc513c8049ad19173130a0e0e6a5486ee8247d2 Clay diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 322104fc5..2f18e5e50 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -75,7 +75,6 @@ False ..\..\lib\Castle Windsor 2.0\bin\Castle.DynamicProxy2.dll - False ..\..\lib\fluentnhibernate\FluentNHibernate.dll @@ -233,6 +232,7 @@ + @@ -272,6 +272,10 @@ + + {76BCD43B-7BA5-4B63-B1E1-861641CA2686} + ClaySharp + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} Orchard.Framework diff --git a/src/Orchard.Tests/UI/ShapeTests.cs b/src/Orchard.Tests/UI/ShapeTests.cs new file mode 100644 index 000000000..cacd829a8 --- /dev/null +++ b/src/Orchard.Tests/UI/ShapeTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Autofac; +using ClaySharp; +using NUnit.Framework; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Implementation; +using Orchard.Environment; +using Orchard.Mvc; +using Orchard.UI.Zones; + +namespace Orchard.Tests.UI { + [TestFixture] + public class ShapeTests : ContainerTestBase { + private WorkContext _workContext; + + protected override void Register(ContainerBuilder builder) { + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + } + + protected override void Resolve(IContainer container) { + _workContext = container.Resolve().CreateWorkContextScope().WorkContext; + } + + [Test] + public void WorkContextPageIsLayoutShape() { + var page = _workContext.Page; + IShapeMetadata pageMetadata = page.Metadata; + Assert.That(pageMetadata.Type, Is.EqualTo("Layout")); + Assert.That(page.Metadata.Type, Is.EqualTo("Layout")); + } + + [Test] + public void PagePropertiesAreNil() { + var page = _workContext.Page; + var pageFoo = page.Foo; + Assert.That(pageFoo == null); + } + + [Test] + public void PageZonesPropertyIsNotNil() { + var page = _workContext.Page; + var pageZones = page.Zones; + Assert.That(pageZones != null); + Assert.That(pageZones.Foo == null); + } + + [Test] + public void AddingToZonePropertyMakesItExist() { + var page = _workContext.Page; + Assert.That(page.Zones.Foo == null); + + var pageZonesFoo = page.Zones.Foo; + pageZonesFoo.Add("hello"); + + Assert.That(page.Zones.Foo != null); + Assert.That(page.Foo != null); + Assert.That(page.Foo.Metadata.Type, Is.EqualTo("Zone")); + } + + [Test] + public void AddingToZoneIndexedMakesItExist() { + var page = _workContext.Page; + Assert.That(page.Zones["Foo"] == null); + + var pageZonesFoo = page.Zones["Foo"]; + pageZonesFoo.Add("hello"); + + Assert.That(page.Zones["Foo"] != null); + Assert.That(page["Foo"] != null); + Assert.That(page["Foo"].Metadata.Type, Is.EqualTo("Zone")); + } + + + [Test] + public void CallingAddOnNilPropertyMakesItBecomeZone() { + var page = _workContext.Page; + Assert.That(page.Foo == null); + + page.Foo.Add("hello"); + + Assert.That(page.Foo != null); + Assert.That(page.Foo.Metadata.Type, Is.EqualTo("Zone")); + } + + + [Test] + public void ZoneContentsAreEnumerable() { + var page = _workContext.Page; + Assert.That(page.Foo == null); + + page.Foo.Add("hello"); + page.Foo.Add("world"); + + var list = new List(); + foreach (var item in page.Foo) { + list.Add(item); + } + + Assert.That(list.Count(), Is.EqualTo(2)); + Assert.That(list.First(), Is.EqualTo("hello")); + Assert.That(list.Last(), Is.EqualTo("world")); + } + + + class NumberIsAlwaysFortyTwo : ShapeFactoryEvents { + public override void Creating(ShapeCreatingContext context) { + context.Behaviors.Add(new Behavior()); + } + + class Behavior : ClayBehavior { + public override object GetMember(Func proceed, object self, string name) { + return name == "Number" ? 42 : proceed(); + } + } + } + + [Test] + public void NumberIsFortyTwo() { + var page = _workContext.Page; + Assert.That(page.Number, Is.EqualTo(42)); + Assert.That(page.Foo.Number == null); + page.Foo.Add("yarg"); + Assert.That(page.Foo.Number, Is.EqualTo(42)); + } + } + +} diff --git a/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs b/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs index 16d6cc37a..2a34860aa 100644 --- a/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs +++ b/src/Orchard.Web/Core/Feeds/Services/FeedFilter.cs @@ -1,6 +1,7 @@ using System.Web.Mvc; using JetBrains.Annotations; using Orchard.Mvc.Filters; +using Orchard.UI; namespace Orchard.Core.Feeds.Services { [UsedImplicitly] @@ -14,7 +15,8 @@ namespace Orchard.Core.Feeds.Services { } public void OnResultExecuting(ResultExecutingContext filterContext) { - _workContextAccessor.GetContext(filterContext).Page.Zones["Head"].Add(html => html.ViewContext.Writer.Write(_feedManager.GetRegisteredLinks(html)), ":after"); + IPage page =_workContextAccessor.GetContext(filterContext).Page; + page.Zones["Head"].Add((HtmlHelper html) => html.ViewContext.Writer.Write(_feedManager.GetRegisteredLinks(html)), ":after"); } public void OnResultExecuted(ResultExecutedContext filterContext) {} diff --git a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs index d287b24f8..0feb72e09 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs @@ -13,6 +13,7 @@ using Orchard.Data.Providers; using Orchard.Data.Migration; using Orchard.DisplayManagement; using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy; using Orchard.DisplayManagement.Implementation; using Orchard.DisplayManagement.Shapes; using Orchard.Environment; @@ -31,6 +32,7 @@ using Orchard.Themes; using Orchard.UI.Notify; using Orchard.UI.PageClass; using Orchard.UI.PageTitle; +using Orchard.UI.Zones; namespace Orchard.Setup { public class SetupMode : Module { @@ -41,8 +43,8 @@ namespace Orchard.Setup { builder.RegisterModule(new CommandModule()); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().As().InstancePerLifetimeScope(); + builder.RegisterType().As().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); @@ -52,7 +54,7 @@ namespace Orchard.Setup { builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As(); + builder.RegisterType().As().InstancePerMatchingLifetimeScope("shell"); // setup mode specific implementations of needed service interfaces builder.RegisterType().As().InstancePerLifetimeScope(); @@ -72,7 +74,11 @@ namespace Orchard.Setup { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As().As(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); } diff --git a/src/Orchard.Web/Orchard.Web.csproj b/src/Orchard.Web/Orchard.Web.csproj index 0e8ad10ef..61f28d619 100644 --- a/src/Orchard.Web/Orchard.Web.csproj +++ b/src/Orchard.Web/Orchard.Web.csproj @@ -105,6 +105,7 @@ + diff --git a/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx b/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx index 2a457a94e..a7e5f555d 100644 --- a/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx +++ b/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx @@ -1,6 +1,6 @@ -<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> -<%@ Import Namespace="Orchard.Mvc.ViewModels" %> -<% //todo: (heskew) this should really be using the IResourceManager if it's to be a theme especially for the jquery dep (w/out needing to copy into this theme...) +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> + +<%-- //todo: (heskew) this should really be using the IResourceManager if it's to be a theme especially for the jquery dep (w/out needing to copy into this theme...) var jquery = ResolveUrl("~/Modules/Orchard.Themes/Scripts/jquery-1.4.2.js"); Model.Zones.AddAction("head:scripts", html => html.ViewContext.Writer.Write(@"")); @@ -15,10 +15,12 @@ html.ViewContext.Writer.Write(@"")); var ie6Css = ResolveUrl("../Styles/ie6.css"); Model.Zones.AddAction("head:styles", html => - html.ViewContext.Writer.Write(@"")); %> + html.ViewContext.Writer.Write(@"")); + --%>
- <%Html.ZoneBody("content"); %> -
\ No newline at end of file + + <%: Display(Model.Content) %> + diff --git a/src/Orchard.Web/Themes/SafeMode/Views/Zone.ascx b/src/Orchard.Web/Themes/SafeMode/Views/Zone.ascx new file mode 100644 index 000000000..bbe85bf58 --- /dev/null +++ b/src/Orchard.Web/Themes/SafeMode/Views/Zone.ascx @@ -0,0 +1,4 @@ +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +
+ <%foreach (var item in Model.Items) {%><%:Display(item)%><%}%> +
diff --git a/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs b/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs index c3dc68272..1b9852300 100644 --- a/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs +++ b/src/Orchard/DisplayManagement/Descriptors/DefaultShapeTableManager.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; namespace Orchard.DisplayManagement.Descriptors { @@ -9,30 +10,41 @@ namespace Orchard.DisplayManagement.Descriptors { _bindingStrategies = bindingStrategies; } - private ShapeTable _shapeTable; + ConcurrentDictionary _tables = new ConcurrentDictionary(); public ShapeTable GetShapeTable(string themeName) { - if (_shapeTable == null) { + return _tables.GetOrAdd(themeName ?? "", x => { var builder = new ShapeTableBuilder(); foreach (var bindingStrategy in _bindingStrategies) { bindingStrategy.Discover(builder); } - // placeholder - alterations will need to be selective and in a particular order - - // GroupBy has been determined to preserve the order of items in original series - _shapeTable = new ShapeTable { - Descriptors = builder.Build() - .GroupBy(alteration => alteration.ShapeType) - .Select(group => group.Aggregate( - new ShapeDescriptor { ShapeType = group.Key }, - (d, a) => { - a.Alter(d); - return d; - })) - .ToDictionary(sd => sd.ShapeType) + + var alterations = builder.Build() + .Where(alteration => IsModuleOrRequestedTheme(alteration, themeName)); + + var descriptors = alterations.GroupBy(alteration => alteration.ShapeType) + .Select(group => group.Aggregate( + new ShapeDescriptor { ShapeType = group.Key }, + (descriptor, alteration) => { + alteration.Alter(descriptor); + return descriptor; + })); + + return new ShapeTable { + Descriptors = descriptors.ToDictionary(sd => sd.ShapeType) }; + }); + } + + static bool IsModuleOrRequestedTheme(ShapeDescriptorAlteration alteration, string themeName) { + if (alteration == null || + alteration.Feature == null || + alteration.Feature.Extension == null) { + return false; } - return _shapeTable; + + return alteration.Feature.Extension.ExtensionType == "Module" || + (alteration.Feature.Extension.ExtensionType == "Theme" && alteration.Feature.Name == themeName); } } } diff --git a/src/Orchard/DisplayManagement/Descriptors/Interfaces.cs b/src/Orchard/DisplayManagement/Descriptors/Interfaces.cs index 7dc5a8898..934fd6276 100644 --- a/src/Orchard/DisplayManagement/Descriptors/Interfaces.cs +++ b/src/Orchard/DisplayManagement/Descriptors/Interfaces.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using Orchard.DisplayManagement.Implementation; -using Orchard.Environment.Extensions.Models; +using System.Collections.Generic; namespace Orchard.DisplayManagement.Descriptors { @@ -18,104 +13,4 @@ namespace Orchard.DisplayManagement.Descriptors { public interface IShapeDescriptorBindingStrategy : IDependency { void Discover(ShapeTableBuilder builder); } - - public class ShapeTable { - public IDictionary Descriptors { get; set; } - } - - public class ShapeDescriptor { - public string ShapeType { get; set; } - - /// - /// The BindingSource is informational text about the source of the Binding delegate. Not used except for - /// troubleshooting. - /// - public string BindingSource { get; set; } - - public Func Binding { get; set; } - } - - - public class ShapeTableBuilder { - readonly IList _descriptorBuilders = new List(); - - public ShapeDescriptorAlterationBuilder Describe { - get { - var db = new ShapeDescriptorAlterationBuilderImpl(); - _descriptorBuilders.Add(db); - return db; - } - } - - public IEnumerable Build() { - return _descriptorBuilders.Select(b => b.Build()); - } - - class ShapeDescriptorAlterationBuilderImpl : ShapeDescriptorAlterationBuilder { - public ShapeDescriptorAlteration Build() { - return new ShapeDescriptorAlteration(_shapeType, _feature, _configurations.ToArray()); - } - } - } - - public class ShapeDescriptorAlteration { - private readonly IList> _configurations; - - public ShapeDescriptorAlteration(string shapeType, FeatureDescriptor feature, IList> configurations) { - _configurations = configurations; - ShapeType = shapeType; - Feature = feature; - } - - public string ShapeType { get; private set; } - public FeatureDescriptor Feature { get; private set; } - public void Alter(ShapeDescriptor descriptor) { - foreach (var configuration in _configurations) { - configuration(descriptor); - } - } - } - - public class ShapeDescriptorAlterationBuilder { - protected FeatureDescriptor _feature; - protected string _shapeType; - protected readonly IList> _configurations = new List>(); - - public ShapeDescriptorAlterationBuilder Named(string shapeType) { - _shapeType = shapeType; - return this; - } - - public ShapeDescriptorAlterationBuilder From(FeatureDescriptor feature) { - _feature = feature; - return this; - } - - public ShapeDescriptorAlterationBuilder Configure(Action action) { - _configurations.Add(action); - return this; - } - - public ShapeDescriptorAlterationBuilder BoundAs(string bindingSource, Func> binder) { - // schedule the configuration - return Configure(descriptor => { - - Func target = null; - - descriptor.BindingSource = bindingSource; - - // announce the binding, which may be reconfigured before it's used - descriptor.Binding = displayContext => { - - // when used, first realize the actual target once - if (target == null) - target = binder(descriptor); - - // and execute the re - return target(displayContext); - }; - }); - } - } - } diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs new file mode 100644 index 000000000..838ee9a40 --- /dev/null +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Web; +using Orchard.DisplayManagement.Implementation; + +namespace Orchard.DisplayManagement.Descriptors { + public class ShapeDescriptor { + public string ShapeType { get; set; } + + /// + /// The BindingSource is informational text about the source of the Binding delegate. Not used except for + /// troubleshooting. + /// + public string BindingSource { get; set; } + + public Func Binding { get; set; } + + public IEnumerable> Creating {get;set;} + + public IEnumerable> Created {get;set;} + } +} \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlteration.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlteration.cs new file mode 100644 index 000000000..9fae8822d --- /dev/null +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlteration.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using Orchard.Environment.Extensions.Models; + +namespace Orchard.DisplayManagement.Descriptors { + public class ShapeDescriptorAlteration { + private readonly IList> _configurations; + + public ShapeDescriptorAlteration(string shapeType, FeatureDescriptor feature, IList> configurations) { + _configurations = configurations; + ShapeType = shapeType; + Feature = feature; + } + + public string ShapeType { get; private set; } + public FeatureDescriptor Feature { get; private set; } + public void Alter(ShapeDescriptor descriptor) { + foreach (var configuration in _configurations) { + configuration(descriptor); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlterationBuilder.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlterationBuilder.cs new file mode 100644 index 000000000..ca614908e --- /dev/null +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptorAlterationBuilder.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Orchard.DisplayManagement.Implementation; +using Orchard.Environment.Extensions.Models; + +namespace Orchard.DisplayManagement.Descriptors { + public class ShapeDescriptorAlterationBuilder { + protected FeatureDescriptor _feature; + protected string _shapeType; + protected readonly IList> _configurations = new List>(); + + public ShapeDescriptorAlterationBuilder Named(string shapeType) { + _shapeType = shapeType; + return this; + } + + public ShapeDescriptorAlterationBuilder From(FeatureDescriptor feature) { + _feature = feature; + return this; + } + + public ShapeDescriptorAlterationBuilder Configure(Action action) { + _configurations.Add(action); + return this; + } + + public ShapeDescriptorAlterationBuilder BoundAs(string bindingSource, Func> binder) { + // schedule the configuration + return Configure(descriptor => { + + Func target = null; + + descriptor.BindingSource = bindingSource; + + // announce the binding, which may be reconfigured before it's used + descriptor.Binding = displayContext => { + + // when used, first realize the actual target once + if (target == null) + target = binder(descriptor); + + // and execute the re + return target(displayContext); + }; + }); + } + + public ShapeDescriptorAlterationBuilder OnCreating(Action action) { + return Configure(descriptor => { + var existing = descriptor.Creating ?? Enumerable.Empty>(); + descriptor.Creating = existing.Concat(new[] { action }); + }); + } + + public ShapeDescriptorAlterationBuilder OnCreated(Action action) { + return Configure(descriptor => { + var existing = descriptor.Created ?? Enumerable.Empty>(); + descriptor.Created = existing.Concat(new[] { action }); + }); + } + } +} \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs new file mode 100644 index 000000000..ab1816449 --- /dev/null +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeTable.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +namespace Orchard.DisplayManagement.Descriptors { + public class ShapeTable { + public IDictionary Descriptors { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeTableBuilder.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeTableBuilder.cs new file mode 100644 index 000000000..6cc89dd0d --- /dev/null +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeTableBuilder.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Orchard.DisplayManagement.Descriptors { + public class ShapeTableBuilder { + readonly IList _descriptorBuilders = new List(); + + public ShapeDescriptorAlterationBuilder Describe { + get { + var db = new ShapeDescriptorAlterationBuilderImpl(); + _descriptorBuilders.Add(db); + return db; + } + } + + public IEnumerable Build() { + return _descriptorBuilders.Select(b => b.Build()); + } + + class ShapeDescriptorAlterationBuilderImpl : ShapeDescriptorAlterationBuilder { + public ShapeDescriptorAlteration Build() { + return new ShapeDescriptorAlteration(_shapeType, _feature, _configurations.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs index 987efda19..5e0272625 100644 --- a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs +++ b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs @@ -11,6 +11,7 @@ using Orchard.Localization; namespace Orchard.DisplayManagement.Implementation { public class DefaultDisplayManager : IDisplayManager { private readonly IShapeTableManager _shapeTableManager; + private readonly IWorkContextAccessor _workContextAccessor; // this need to be Shape instead of IShape - cast to interface throws error on clr types like HtmlString private static readonly CallSite> _convertAsShapeCallsite = CallSite>.Create( @@ -20,8 +21,11 @@ namespace Orchard.DisplayManagement.Implementation { typeof(Shape), null/*typeof(DefaultDisplayManager)*/))); - public DefaultDisplayManager(IShapeTableManager shapeTableManager) { + public DefaultDisplayManager( + IShapeTableManager shapeTableManager, + IWorkContextAccessor workContextAccessor) { _shapeTableManager = shapeTableManager; + _workContextAccessor = workContextAccessor; T = NullLocalizer.Instance; } @@ -32,7 +36,7 @@ namespace Orchard.DisplayManagement.Implementation { var shape = _convertAsShapeCallsite.Target(_convertAsShapeCallsite, context.Value); - // non-shape arguements are returned as a no-op + // non-shape arguments are returned as a no-op if (shape == null) return CoerceHtmlString(context.Value); @@ -41,7 +45,8 @@ namespace Orchard.DisplayManagement.Implementation { if (shapeMetadata == null || string.IsNullOrEmpty(shapeMetadata.Type)) return CoerceHtmlString(context.Value); - var shapeTable = _shapeTableManager.GetShapeTable(null); + var workContext = _workContextAccessor.GetContext(context.ViewContext); + var shapeTable = _shapeTableManager.GetShapeTable(workContext.CurrentTheme.ThemeName); //preproc loop / event (alter shape, swapping type) ShapeDescriptor shapeDescriptor; @@ -63,7 +68,7 @@ namespace Orchard.DisplayManagement.Implementation { return new HtmlString(HttpUtility.HtmlEncode(value)); } - private IHtmlString Process(ShapeDescriptor shapeDescriptor, IShape shape, DisplayContext context) { + private IHtmlString Process(ShapeDescriptor shapeDescriptor, IShape shape, DisplayContext context) { return CoerceHtmlString(shapeDescriptor.Binding(context)); } diff --git a/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs b/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs index d4a605c70..23708b22b 100644 --- a/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs +++ b/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs @@ -2,14 +2,20 @@ using System.Collections.Generic; using System.Linq; using ClaySharp; +using Orchard.DisplayManagement.Descriptors; using Orchard.DisplayManagement.Shapes; namespace Orchard.DisplayManagement.Implementation { - public interface IShapeEvents : IDependency { + public interface IShapeFactoryEvents : IDependency { void Creating(ShapeCreatingContext context); void Created(ShapeCreatedContext context); } + public abstract class ShapeFactoryEvents : IShapeFactoryEvents{ + public virtual void Creating(ShapeCreatingContext context) {} + public virtual void Created(ShapeCreatedContext context) {} + } + public class ShapeCreatingContext { public IShapeFactory ShapeFactory { get; set; } public string ShapeType { get; set; } @@ -27,13 +33,19 @@ namespace Orchard.DisplayManagement.Implementation { public class DefaultShapeFactory : IShapeFactory { - private readonly IEnumerable> _events; + private readonly IEnumerable> _events; + private readonly IShapeTableManager _shapeTableManager; - public DefaultShapeFactory(IEnumerable> events) { + public DefaultShapeFactory(IEnumerable> events, IShapeTableManager shapeTableManager) { _events = events; + _shapeTableManager = shapeTableManager; } public IShape Create(string shapeType, INamedEnumerable parameters) { + var defaultShapeTable = _shapeTableManager.GetShapeTable(null); + ShapeDescriptor shapeDescriptor; + defaultShapeTable.Descriptors.TryGetValue(shapeType, out shapeDescriptor); + var creatingContext = new ShapeCreatingContext { ShapeFactory = this, ShapeType = shapeType, @@ -50,7 +62,6 @@ namespace Orchard.DisplayManagement.Implementation { // consume the first argument positional = positional.Skip(1); } - IClayBehavior[] behaviors; if (creatingContext.BaseType == typeof(Array)) { // array is a hint - not an intended base class @@ -70,10 +81,17 @@ namespace Orchard.DisplayManagement.Implementation { }; } + // "creating" events may add behaviors and alter base type foreach (var ev in _events) { ev.Value.Creating(creatingContext); } + if (shapeDescriptor != null && shapeDescriptor.Creating != null) { + foreach (var ev in shapeDescriptor.Creating) { + ev(creatingContext); + } + } + // create the new instance var createdContext = new ShapeCreatedContext { ShapeFactory = this, ShapeType = creatingContext.ShapeType, @@ -81,6 +99,22 @@ namespace Orchard.DisplayManagement.Implementation { }; createdContext.Shape.Metadata = new ShapeMetadata { Type = shapeType }; + // "created" events provides default values and new object initialization + foreach (var ev in _events) { + ev.Value.Created(createdContext); + } + if (shapeDescriptor != null && shapeDescriptor.Created != null) { + foreach (var ev in shapeDescriptor.Created) { + ev(createdContext); + } + } + foreach (var ev in creatingContext.OnCreated) { + ev(createdContext); + } + + + // other properties passed with call overlay any defaults, so are after the created events + // only one non-Type, non-named argument is allowed var initializer = positional.SingleOrDefault(); if (initializer != null) { @@ -93,13 +127,6 @@ namespace Orchard.DisplayManagement.Implementation { createdContext.Shape[kv.Key] = kv.Value; } - foreach (var ev in creatingContext.OnCreated) { - ev(createdContext); - } - foreach (var ev in _events) { - ev.Value.Created(createdContext); - } - return createdContext.Shape; } } diff --git a/src/Orchard/DisplayManagement/Implementation/PageWorkContextStateProvider.cs b/src/Orchard/DisplayManagement/Implementation/PageWorkContextStateProvider.cs deleted file mode 100644 index a28918229..000000000 --- a/src/Orchard/DisplayManagement/Implementation/PageWorkContextStateProvider.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Linq; -using ClaySharp.Implementation; -using Orchard.DisplayManagement.Zones; -using Orchard.UI; - -namespace Orchard.DisplayManagement.Implementation { - public class PageWorkContextStateProvider : IWorkContextStateProvider, IShapeEvents { - private readonly IShapeFactory _shapeFactory; - - public PageWorkContextStateProvider(IShapeFactory shapeFactory) { - _shapeFactory = shapeFactory; - } - - public T Get(string name) { - if (name == "Page") { - var page = (dynamic)_shapeFactory.Create("Layout", Arguments.From(Enumerable.Empty(), Enumerable.Empty())); - return page; - } - return default(T); - } - - public void Creating(ShapeCreatingContext context) { - if (context.ShapeType == "Layout") { - context.Behaviors.Add(new ZoneHoldingBehavior(context.ShapeFactory)); - } - else if (context.ShapeType == "Zone") { - context.BaseType = typeof(Zone); - } - } - - public void Created(ShapeCreatedContext context) { - } - } - - -} diff --git a/src/Orchard/Environment/DefaultWorkContextAccessor.cs b/src/Orchard/Environment/DefaultWorkContextAccessor.cs index f32ca7aa5..8f8391ffe 100644 --- a/src/Orchard/Environment/DefaultWorkContextAccessor.cs +++ b/src/Orchard/Environment/DefaultWorkContextAccessor.cs @@ -100,7 +100,12 @@ namespace Orchard.Environment { } public override T GetState(string name) { - return (T)_state.GetOrAdd(name, x => _workContextStateProviders.Select(wcsp => wcsp.Get(x)).Where(t => !Equals(t, default(T))).FirstOrDefault()); + return (T)_state.GetOrAdd(name, x => GetStateInternal(x)); + } + + private T GetStateInternal(string name) { + return _workContextStateProviders.Select(wcsp => wcsp.Get(name)) + .FirstOrDefault(value => !Equals(value, default(T))); } public override void SetState(string name, T value) { diff --git a/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngine.cs b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngine.cs new file mode 100644 index 000000000..009644b75 --- /dev/null +++ b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngine.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Mvc; + +namespace Orchard.Mvc.ViewEngines.Razor { + public class RazorViewEngine : CshtmlViewEngine { + protected override bool FileExists(ControllerContext controllerContext, string virtualPath) { + if (!virtualPath.EndsWith(".cshtml", StringComparison.InvariantCultureIgnoreCase)) + return false; + + return base.FileExists(controllerContext, virtualPath); + } + } +} diff --git a/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs index ea512052c..4273a93bb 100644 --- a/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/Razor/RazorViewEngineProvider.cs @@ -79,7 +79,7 @@ namespace Orchard.Mvc.ViewEngines.Razor { //Logger.Debug("UniversalFormats (module): \r\n\t-{0}", string.Join("\r\n\t-", universalFormats)); - var viewEngine = new CshtmlViewEngine { + var viewEngine = new RazorViewEngine { MasterLocationFormats = DisabledFormats, ViewLocationFormats = universalFormats, PartialViewLocationFormats = universalFormats, @@ -92,7 +92,7 @@ namespace Orchard.Mvc.ViewEngines.Razor { } public IViewEngine CreateBareViewEngine() { - return new CshtmlViewEngine { + return new RazorViewEngine { MasterLocationFormats = DisabledFormats, ViewLocationFormats = DisabledFormats, PartialViewLocationFormats = DisabledFormats, diff --git a/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs b/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs index 9b9c975e4..8124adf08 100644 --- a/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs +++ b/src/Orchard/Mvc/ViewEngines/Razor/WebViewPage.cs @@ -1,16 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Mvc; -using Autofac; -using ClaySharp; -using Orchard.ContentManagement; -using Orchard.Data.Migration; +using Autofac; using Orchard.DisplayManagement; -using Orchard.Environment; using Orchard.Localization; -using Orchard.Mvc.Html; using Orchard.Security; using Orchard.Security.Permissions; diff --git a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs index 8484e5c88..0ff3242a2 100644 --- a/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs +++ b/src/Orchard/Mvc/ViewEngines/ThemeAwareness/LayoutAwareViewEngine.cs @@ -37,7 +37,7 @@ namespace Orchard.Mvc.ViewEngines.ThemeAwareness { var buffer = new StringWriter(); findBody.View.Render(viewContext, buffer); - _workContext.Page.Zones["Content"].Add(new HtmlString(buffer.ToString()), "5"); + _workContext.Page.Zones.Content.Add(new HtmlString(buffer.ToString()), "5"); var display = _displayHelperFactory.CreateHelper(viewContext, viewDataContainer); IHtmlString result = display(_workContext.Page); diff --git a/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs index 704d9fa94..99af90535 100644 --- a/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/WebForms/WebFormViewEngineProvider.cs @@ -1,13 +1,21 @@ using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Web.Mvc; +using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy; +using Orchard.FileSystems.VirtualPath; using Orchard.Logging; namespace Orchard.Mvc.ViewEngines.WebForms { - public class WebFormViewEngineProvider : IViewEngineProvider { - public WebFormViewEngineProvider() { + public class WebFormViewEngineProvider : IViewEngineProvider, IShapeTemplateViewEngine { + private readonly IVirtualPathProvider _virtualPathProvider; + + public WebFormViewEngineProvider(IVirtualPathProvider virtualPathProvider) { + _virtualPathProvider = virtualPathProvider; Logger = NullLogger.Instance; } + static string[] DisabledFormats = new[] { "~/Disabled" }; public ILogger Logger { get; set; } @@ -98,5 +106,15 @@ namespace Orchard.Mvc.ViewEngines.WebForms { AreaPartialViewLocationFormats = DisabledFormats, }; } + + public IEnumerable DetectTemplateFileNames(string virtualPath) { + var fileNames = _virtualPathProvider.ListFiles(virtualPath).Select(Path.GetFileName); + foreach (var fileName in fileNames) { + if (fileName.EndsWith(".aspx", StringComparison.OrdinalIgnoreCase) || + fileName.EndsWith(".ascx", StringComparison.OrdinalIgnoreCase)) { + yield return fileName; + } + } + } } } diff --git a/src/Orchard/Mvc/ViewPage.cs b/src/Orchard/Mvc/ViewPage.cs index 78c285ca8..b4cbb8bed 100644 --- a/src/Orchard/Mvc/ViewPage.cs +++ b/src/Orchard/Mvc/ViewPage.cs @@ -1,4 +1,7 @@ using System.Web.Mvc; +using Autofac; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Implementation; using Orchard.Localization; using Orchard.Mvc.Html; using Orchard.Security; @@ -6,15 +9,23 @@ using Orchard.Security.Permissions; namespace Orchard.Mvc { public class ViewPage : System.Web.Mvc.ViewPage { - public ViewPage() { - T = NullLocalizer.Instance; - } + private object _display; + private Localizer _localizer = NullLocalizer.Instance; - public Localizer T { get; set; } + public Localizer T { get { return _localizer; } } + public dynamic Display { get { return _display; } } + public IDisplayHelperFactory DisplayHelperFactory { get; set; } - public override void RenderView(ViewContext viewContext) { - T = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath); - base.RenderView(viewContext); + public IAuthorizer Authorizer { get; set; } + + public override void InitHelpers() { + base.InitHelpers(); + + var workContext = ViewContext.GetWorkContext(); + workContext.Resolve().InjectUnsetProperties(this); + + _localizer = LocalizationUtilities.Resolve(ViewContext, AppRelativeVirtualPath); + _display = DisplayHelperFactory.CreateHelper(ViewContext, this); } public MvcHtmlString H(string value) { @@ -22,7 +33,7 @@ namespace Orchard.Mvc { } public bool AuthorizedFor(Permission permission) { - return Html.Resolve().Authorize(permission); + return Authorizer.Authorize(permission); } } } \ No newline at end of file diff --git a/src/Orchard/Mvc/ViewUserControl.cs b/src/Orchard/Mvc/ViewUserControl.cs index 24f41aee6..51e9ea406 100644 --- a/src/Orchard/Mvc/ViewUserControl.cs +++ b/src/Orchard/Mvc/ViewUserControl.cs @@ -1,19 +1,28 @@ using System.Web.Mvc; +using Autofac; +using Orchard.DisplayManagement; using Orchard.Localization; -using Orchard.Mvc.Html; using Orchard.Security; using Orchard.Security.Permissions; namespace Orchard.Mvc { public class ViewUserControl : System.Web.Mvc.ViewUserControl { - public ViewUserControl() { - T = NullLocalizer.Instance; - } + private object _display; + private Localizer _localizer = NullLocalizer.Instance; - public Localizer T { get; set; } + public Localizer T { get { return _localizer; } } + public dynamic Display { get { return _display; } } + public IDisplayHelperFactory DisplayHelperFactory { get; set; } + + public IAuthorizer Authorizer { get; set; } public override void RenderView(ViewContext viewContext) { - T = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath); + var workContext = viewContext.GetWorkContext(); + workContext.Resolve().InjectUnsetProperties(this); + + _localizer = LocalizationUtilities.Resolve(viewContext, AppRelativeVirtualPath); + _display = DisplayHelperFactory.CreateHelper(viewContext, this); + base.RenderView(viewContext); } @@ -22,7 +31,10 @@ namespace Orchard.Mvc { } public bool AuthorizedFor(Permission permission) { - return Html.Resolve().Authorize(permission); + return Authorizer.Authorize(permission); } } -} \ No newline at end of file + + public class ViewUserControl : ViewUserControl { + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 8351214ff..166c993e7 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -139,8 +139,14 @@ - - + + + + + + + + @@ -731,18 +737,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + ASPXCodeBehind @@ -784,7 +790,7 @@ - + diff --git a/src/Orchard/UI/IPage.cs b/src/Orchard/UI/IPage.cs index 109e4ab08..e1ac2bc7e 100644 --- a/src/Orchard/UI/IPage.cs +++ b/src/Orchard/UI/IPage.cs @@ -1,6 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Web.Mvc; +using Orchard.DisplayManagement.Shapes; namespace Orchard.UI { public interface IPage { @@ -31,7 +33,7 @@ namespace Orchard.UI { IZone Add(Action action, string position); } - public class Zone : IZone { + public class Zone : Shape, IZone { private readonly IList _items = new List(); public virtual string ZoneName { get; set; } @@ -55,5 +57,11 @@ namespace Orchard.UI { //throw new NotImplementedException(); return this; } + + public virtual IList Items { + get { + return _items; + } + } } -} \ No newline at end of file +} diff --git a/src/Orchard/UI/Zones/ContentItemDisplayZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/ContentItemDisplayZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/ContentItemDisplayZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/ContentItemDisplayZoneItem.cs diff --git a/src/Orchard/UI/Zones/ContentPartDisplayZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/ContentPartDisplayZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/ContentPartDisplayZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/ContentPartDisplayZoneItem.cs diff --git a/src/Orchard/UI/Zones/ContentPartEditorZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/ContentPartEditorZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/ContentPartEditorZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/ContentPartEditorZoneItem.cs diff --git a/src/Orchard/UI/Zones/DelegateZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/DelegateZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/DelegateZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/DelegateZoneItem.cs diff --git a/src/Orchard/UI/Zones/IZoneManager.cs b/src/Orchard/UI/Zones/Obsolete/IZoneManager.cs similarity index 100% rename from src/Orchard/UI/Zones/IZoneManager.cs rename to src/Orchard/UI/Zones/Obsolete/IZoneManager.cs diff --git a/src/Orchard/UI/Zones/IZoneManagerEvents.cs b/src/Orchard/UI/Zones/Obsolete/IZoneManagerEvents.cs similarity index 100% rename from src/Orchard/UI/Zones/IZoneManagerEvents.cs rename to src/Orchard/UI/Zones/Obsolete/IZoneManagerEvents.cs diff --git a/src/Orchard/UI/Zones/RenderActionZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/RenderActionZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/RenderActionZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/RenderActionZoneItem.cs diff --git a/src/Orchard/UI/Zones/RenderPartialZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/RenderPartialZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/RenderPartialZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/RenderPartialZoneItem.cs diff --git a/src/Orchard/UI/Zones/RenderStaticZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/RenderStaticZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/RenderStaticZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/RenderStaticZoneItem.cs diff --git a/src/Orchard/UI/Zones/ZoneCollection.cs b/src/Orchard/UI/Zones/Obsolete/ZoneCollection.cs similarity index 100% rename from src/Orchard/UI/Zones/ZoneCollection.cs rename to src/Orchard/UI/Zones/Obsolete/ZoneCollection.cs diff --git a/src/Orchard/UI/Zones/ZoneEntry.cs b/src/Orchard/UI/Zones/Obsolete/ZoneEntry.cs similarity index 100% rename from src/Orchard/UI/Zones/ZoneEntry.cs rename to src/Orchard/UI/Zones/Obsolete/ZoneEntry.cs diff --git a/src/Orchard/UI/Zones/ZoneItem.cs b/src/Orchard/UI/Zones/Obsolete/ZoneItem.cs similarity index 100% rename from src/Orchard/UI/Zones/ZoneItem.cs rename to src/Orchard/UI/Zones/Obsolete/ZoneItem.cs diff --git a/src/Orchard/UI/Zones/ZoneManager.cs b/src/Orchard/UI/Zones/Obsolete/ZoneManager.cs similarity index 100% rename from src/Orchard/UI/Zones/ZoneManager.cs rename to src/Orchard/UI/Zones/Obsolete/ZoneManager.cs diff --git a/src/Orchard/UI/Zones/PageWorkContext.cs b/src/Orchard/UI/Zones/PageWorkContext.cs new file mode 100644 index 000000000..9e5f9fc63 --- /dev/null +++ b/src/Orchard/UI/Zones/PageWorkContext.cs @@ -0,0 +1,39 @@ +using System.Linq; +using ClaySharp.Implementation; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Descriptors; +using Orchard.Environment.Extensions.Models; + +namespace Orchard.UI.Zones { + public class PageWorkContext : IWorkContextStateProvider { + private readonly IShapeFactory _shapeFactory; + + public PageWorkContext(IShapeFactory shapeFactory) { + _shapeFactory = shapeFactory; + } + + public T Get(string name) { + if (name == "Page") { + return (dynamic)_shapeFactory.Create("Layout", Arguments.Empty()); + } + return default(T); + } + } + + public class CoreShapes : IShapeDescriptorBindingStrategy { + public void Discover(ShapeTableBuilder builder) { + var feature = new FeatureDescriptor { + Name = "Orchard.Framework", + Extension = new ExtensionDescriptor { + Name = "Orchard.Framework", + ExtensionType = "Module", + } + }; + builder.Describe.Named("Layout").From(feature) + .OnCreating(context => context.Behaviors.Add(new ZoneHoldingBehavior(context.ShapeFactory))); + + builder.Describe.Named("Zone").From(feature) + .OnCreating(context => context.BaseType = typeof(Zone)); + } + } +} diff --git a/src/Orchard/DisplayManagement/Zones/ZoneHoldingBehavior.cs b/src/Orchard/UI/Zones/ZoneHoldingBehavior.cs similarity index 73% rename from src/Orchard/DisplayManagement/Zones/ZoneHoldingBehavior.cs rename to src/Orchard/UI/Zones/ZoneHoldingBehavior.cs index 0377f8702..1029ffdd5 100644 --- a/src/Orchard/DisplayManagement/Zones/ZoneHoldingBehavior.cs +++ b/src/Orchard/UI/Zones/ZoneHoldingBehavior.cs @@ -3,9 +3,22 @@ using System.Linq; using ClaySharp; using ClaySharp.Behaviors; using ClaySharp.Implementation; -using Orchard.UI; +using Orchard.DisplayManagement; -namespace Orchard.DisplayManagement.Zones { +namespace Orchard.UI.Zones { + /// + /// Provides the behavior of shapes that have a Zones property. + /// Examples include Layout and Item + /// + /// * Returns a fake parent object for zones + /// Foo.Zones + /// + /// * + /// Foo.Zones.Alpha : + /// Foo.Zones["Alpha"] + /// Foo.Alpha :same + /// + /// public class ZoneHoldingBehavior : ClayBehavior { private readonly IShapeFactory _shapeFactory; @@ -15,18 +28,21 @@ namespace Orchard.DisplayManagement.Zones { public override object GetMember(Func proceed, object self, string name) { if (name == "Zones") { + // provide a robot for zone manipulation on parent object return ClayActivator.CreateInstance(new IClayBehavior[] { new InterfaceProxyBehavior(), new ZonesBehavior(_shapeFactory, self) }); } - //Page.Zones.Sidebar.Add(x,"below") - //Page.Sidebar.Add(x,"below") - + var result = proceed(); if (((dynamic)result) == null) { + + // substitute nil results with a robot that turns adds a zone on + // the parent when .Add is invoked return ClayActivator.CreateInstance(new IClayBehavior[] { - new NilResultBehavior(), + new InterfaceProxyBehavior(), + new NilBehavior(), new ZoneOnDemandBehavior(_shapeFactory, self, name) }); } @@ -47,6 +63,7 @@ namespace Orchard.DisplayManagement.Zones { if (parentMember == null) { return ClayActivator.CreateInstance(new IClayBehavior[] { new InterfaceProxyBehavior(), + new NilBehavior(), new ZoneOnDemandBehavior(_shapeFactory, _parent, name) }); } @@ -63,12 +80,12 @@ namespace Orchard.DisplayManagement.Zones { public class ZoneOnDemandBehavior : ClayBehavior { private readonly IShapeFactory _shapeFactory; private readonly object _parent; - private readonly string _name; + private readonly string _potentialZoneName; - public ZoneOnDemandBehavior(IShapeFactory shapeFactory, object parent, string name) { + public ZoneOnDemandBehavior(IShapeFactory shapeFactory, object parent, string potentialZoneName) { _shapeFactory = shapeFactory; _parent = parent; - _name = name; + _potentialZoneName = potentialZoneName; } public override object InvokeMember(Func proceed, object self, string name, INamedEnumerable args) { @@ -77,8 +94,8 @@ namespace Orchard.DisplayManagement.Zones { dynamic parent = _parent; dynamic zone = _shapeFactory.Create("Zone", Arguments.Empty()); - zone.ZoneName = name; - parent[name] = zone; + zone.ZoneName = _potentialZoneName; + parent[_potentialZoneName] = zone; if (argsCount == 1) return zone.Add(args.Single()); diff --git a/src/Orchard/WorkContext.cs b/src/Orchard/WorkContext.cs index 187ba0a40..080f75f68 100644 --- a/src/Orchard/WorkContext.cs +++ b/src/Orchard/WorkContext.cs @@ -18,8 +18,8 @@ namespace Orchard { set { SetState("HttpContext", value); } } - public IPage Page { - get { return GetState("Page"); } + public dynamic Page { + get { return GetState("Page"); } set { SetState("Page", value); } }