From 097d936a8312c4ec73bb6333f4c771155e5a934c Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Mon, 11 Oct 2010 16:13:22 -0700 Subject: [PATCH 1/6] Starting to transition onto different displaytype/location architecture Introducing branch to avoid pushing a known broken state onto dev --HG-- branch : composition --- src/Orchard.Web/Core/Contents/Shapes.cs | 26 ++++++++++++++----- .../Core/Routable/Drivers/RoutePartDriver.cs | 2 +- ....RoutePart.cshtml => RoutableTitle.cshtml} | 0 .../Modules/Orchard.Widgets/Migrations.cs | 2 +- .../Services/WidgetsService.cs | 2 +- .../DefaultContentManager.cs | 25 +++++++++++------- .../ContentManagement/IContentManager.cs | 6 ++--- .../DisplayManagement/Shapes/ShapeMetadata.cs | 1 + 8 files changed, 41 insertions(+), 23 deletions(-) rename src/Orchard.Web/Core/Routable/Views/Parts/{Routable.RoutePart.cshtml => RoutableTitle.cshtml} (100%) diff --git a/src/Orchard.Web/Core/Contents/Shapes.cs b/src/Orchard.Web/Core/Contents/Shapes.cs index e116dc6a7..fddd989a0 100644 --- a/src/Orchard.Web/Core/Contents/Shapes.cs +++ b/src/Orchard.Web/Core/Contents/Shapes.cs @@ -1,15 +1,27 @@ -using Orchard.DisplayManagement.Implementation; +using System; +using Orchard.ContentManagement; +using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Implementation; using Orchard.UI.Zones; namespace Orchard.Core.Contents { - public class Shapes : IShapeFactoryEvents { - - public void Creating(ShapeCreatingContext creating) { - if (creating.ShapeType.StartsWith("Items_Content")) - creating.Behaviors.Add(new ZoneHoldingBehavior(name => creating.New.ContentZone())); + public class Shapes : IShapeTableProvider { + public void Discover(ShapeTableBuilder builder) { + builder.Describe("Items_Content") + .OnCreating(creating => creating.Behaviors.Add(new ZoneHoldingBehavior(name => ContentZone(creating, name)))) + .OnDisplaying(displaying => { + ContentItem contentItem = displaying.Shape.ContentItem; + if (contentItem != null) { + displaying.ShapeMetadata.Alternates.Add("Items_Content__" + contentItem.ContentType); + displaying.ShapeMetadata.Alternates.Add("Items_Content__" + contentItem.Id); + } + }); } - public void Created(ShapeCreatedContext created) { + private static object ContentZone(ShapeCreatingContext creating, string name) { + var zone = creating.New.ContentZone(); + zone.ZoneName = name; + return zone; } } } diff --git a/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs b/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs index 9bdd6b0d7..fb6c81c00 100644 --- a/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs +++ b/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs @@ -45,7 +45,7 @@ namespace Orchard.Core.Routable.Drivers { } protected override DriverResult Display(RoutePart part, string displayType, dynamic shapeHelper) { - var routePart = shapeHelper.Parts_Routable_RoutePart(ContentPart: part, Title: part.Title); + var routePart = shapeHelper.Parts_RoutableTitle(ContentPart: part, Title: part.Title); if (!string.IsNullOrWhiteSpace(displayType)) routePart.Metadata.Type = string.Format("{0}.{1}", routePart.Metadata.Type, displayType); var location = part.GetLocation(displayType, "Header", "5"); diff --git a/src/Orchard.Web/Core/Routable/Views/Parts/Routable.RoutePart.cshtml b/src/Orchard.Web/Core/Routable/Views/Parts/RoutableTitle.cshtml similarity index 100% rename from src/Orchard.Web/Core/Routable/Views/Parts/Routable.RoutePart.cshtml rename to src/Orchard.Web/Core/Routable/Views/Parts/RoutableTitle.cshtml diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Migrations.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Migrations.cs index 19ac7520b..b122b4633 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Migrations.cs @@ -70,7 +70,7 @@ namespace Orchard.Widgets { .WithPart("WidgetPart") .WithPart("BodyPart") .WithPart("CommonPart") - .WithSetting("stereotype", "widget") + .WithSetting("Stereotype", "Widget") ); CreateDefaultLayers(); diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Services/WidgetsService.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Services/WidgetsService.cs index d1972b618..fd8bcb8b7 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Services/WidgetsService.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Services/WidgetsService.cs @@ -23,7 +23,7 @@ namespace Orchard.Widgets.Services { public IEnumerable GetWidgetTypes() { return _contentManager.GetContentTypeDefinitions() - .Where(contentTypeDefinition => contentTypeDefinition.Settings.ContainsKey("stereotype") && contentTypeDefinition.Settings["stereotype"] == "widget") + .Where(contentTypeDefinition => contentTypeDefinition.Settings.ContainsKey("Stereotype") && contentTypeDefinition.Settings["Stereotype"] == "Widget") .Select(contentTypeDefinition => contentTypeDefinition.Name); } diff --git a/src/Orchard/ContentManagement/DefaultContentManager.cs b/src/Orchard/ContentManagement/DefaultContentManager.cs index 4f368644b..679753224 100644 --- a/src/Orchard/ContentManagement/DefaultContentManager.cs +++ b/src/Orchard/ContentManagement/DefaultContentManager.cs @@ -378,23 +378,28 @@ namespace Orchard.ContentManagement { new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); - public dynamic BuildDisplay(dynamic content, string displayType = "") { + public dynamic BuildDisplay(IContent content, string displayType = "") { + var contentTypeDefinition = content.ContentItem.TypeDefinition; + string stereotype; + if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype)) + stereotype = "Content"; + + var shapeTypeName = "Items_" + stereotype; + var shapeDisplayType = string.IsNullOrWhiteSpace(displayType) ? "Detail" : displayType; + var shapeHelper = _shapeHelperFactory.CreateHelper(); - - var shapeTypeName = string.IsNullOrEmpty(displayType) ? "Items_Content" : ("Items_Content_" + displayType); var itemShape = _shapeHelperCalls.Invoke(shapeHelper, shapeTypeName); + + itemShape.ContentItem = content.ContentItem; + itemShape.Metadata.DisplayType = shapeDisplayType; - IContent iContent = content; - if (iContent != null) - itemShape.ContentItem = iContent.ContentItem; - - var context = new BuildDisplayContext(itemShape, content, displayType, _shapeHelperFactory); + var context = new BuildDisplayContext(itemShape, content, shapeDisplayType, _shapeHelperFactory); Handlers.Invoke(handler => handler.BuildDisplay(context), Logger); return context.Shape; } - public dynamic BuildEditor(dynamic content) { + public dynamic BuildEditor(IContent content) { var shapeHelper = _shapeHelperFactory.CreateHelper(); var itemShape = shapeHelper.Items_Content_Edit(); @@ -407,7 +412,7 @@ namespace Orchard.ContentManagement { return context.Shape; } - public dynamic UpdateEditor(dynamic content, IUpdateModel updater) { + public dynamic UpdateEditor(IContent content, IUpdateModel updater) { var shapeHelper = _shapeHelperFactory.CreateHelper(); var itemShape = shapeHelper.Items_Content_Edit(); diff --git a/src/Orchard/ContentManagement/IContentManager.cs b/src/Orchard/ContentManagement/IContentManager.cs index 0eb88a2ef..571ed0325 100644 --- a/src/Orchard/ContentManagement/IContentManager.cs +++ b/src/Orchard/ContentManagement/IContentManager.cs @@ -26,9 +26,9 @@ namespace Orchard.ContentManagement { ContentItemMetadata GetItemMetadata(IContent contentItem); - dynamic BuildDisplay(dynamic content, string displayType = ""); - dynamic BuildEditor(dynamic content); - dynamic UpdateEditor(dynamic content, IUpdateModel updater); + dynamic BuildDisplay(IContent content, string displayType = ""); + dynamic BuildEditor(IContent content); + dynamic UpdateEditor(IContent content, IUpdateModel updater); } public class VersionOptions { diff --git a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs index 4c046b490..7ee11a62e 100644 --- a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs +++ b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs @@ -9,6 +9,7 @@ namespace Orchard.DisplayManagement.Shapes { } public string Type { get; set; } + public string DisplayType { get; set; } public string Position { get; set; } public string Prefix { get; set; } public IList Wrappers { get; set; } From 9536b74fa214549c6089ebe777eb3d49c5642895 Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Tue, 12 Oct 2010 00:57:52 -0700 Subject: [PATCH 2/6] Adding an html widget to the homepage @ setup (w/ content from previous homepage page) --HG-- branch : dev --- .../Orchard.Setup/Orchard.Setup.csproj | 4 +++ .../Orchard.Setup/Services/SetupService.cs | 18 ++++++++++++- .../Orchard.Widgets/Filters/WidgetFilter.cs | 1 + .../RuleEngine/UrlRuleProvider.cs | 26 ++++++++++++++----- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj b/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj index 75c7ea197..bf0578413 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj +++ b/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj @@ -97,6 +97,10 @@ {9916839C-39FC-4CEB-A5AF-89CA7E87119F} Orchard.Core + + {194D3CCC-1153-474D-8176-FDE8D7D0D0BD} + Orchard.Widgets + diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index a444f745f..4261700a2 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -27,6 +27,7 @@ using Orchard.Settings; using Orchard.Themes; using Orchard.Environment.State; using Orchard.Data.Migration; +using Orchard.Widgets.Models; namespace Orchard.Setup.Services { public class SetupService : ISetupService { @@ -224,7 +225,22 @@ namespace Orchard.Setup.Services { //contentManager.Publish(page); //siteSettings.Record.HomePage = "RoutableHomePageProvider;" + page.Id; //scratch that - //create the home page as a WidgetPage + + // add a layer for the homepage + var layer = contentManager.Create("Layer"); + layer.As().Name = "TheHomepage"; + layer.As().LayerRule = "url \"~/\""; + contentManager.Publish(layer); + + // create an html widget with the homepage layer as its container + var htmlWidget = contentManager.Create("HtmlWidget"); + htmlWidget.As().LayerPart = layer.As(); + htmlWidget.As().Title = T("Welcome to Orchard!").Text; + htmlWidget.As().Zone = "Content"; + htmlWidget.As().Text = "

Welcome to Orchard!

Congratulations, you've successfully set-up your Orchard site.

This is the home page of your new site. We've taken the liberty to write here about a few things you could look at next in order to get familiar with the application. Once you feel confident you don't need this anymore, just click Edit to go into edit mode and replace this with whatever you want on your home page to make it your own.

One thing you could do (but you don't have to) is go into Manage Settings (follow the Admin link and then look for it under \"Settings\" in the menu on the left) and check that everything is configured the way you want.

You probably want to make the site your own. One of the ways you can do that is by clicking Manage Themes in the admin menu. A theme is a packaged look and feel that affects the whole site.

Next, you can start playing with the content types that we installed. For example, go ahead and click Add New Page in the admin menu and create an \"about\" page. Then, add it to the navigation menu by going to Manage Menu. You can also click Add New Blog and start posting by clicking \"Add New Post\".

Finally, Orchard has been designed to be extended. It comes with a few built-in modules such as pages and blogs or themes. You can install new themes by going to Manage Themes and clicking Install a new Theme. Like for themes, modules are created by other users of Orchard just like you so if you feel up to it, please consider participating.

--The Orchard Crew

"; + contentManager.Publish(htmlWidget); + + // create the home page as a WidgetPage var page = contentManager.Create("WidgetPage", VersionOptions.Draft); page.As().Title = T("Home").ToString(); contentManager.Publish(page); diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs index 27a293744..9612327fd 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Filters/WidgetFilter.cs @@ -36,6 +36,7 @@ namespace Orchard.Widgets.Filters { List activeLayerIds = new List(); foreach (var activeLayer in activeLayers) { + var context = workContext.HttpContext; if (_ruleManager.Matches(activeLayer.Record.LayerRule)) { activeLayerIds.Add(activeLayer.ContentItem.Id); } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/UrlRuleProvider.cs b/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/UrlRuleProvider.cs index 46c127403..fd653ab9e 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/UrlRuleProvider.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/UrlRuleProvider.cs @@ -1,21 +1,33 @@ using System; +using Orchard.Mvc; using Orchard.UI.Widgets; namespace Orchard.Widgets.RuleEngine { public class UrlRuleProvider : IRuleProvider { - private readonly IWorkContextAccessor _workContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; - public UrlRuleProvider(IWorkContextAccessor workContextAccessor) { - _workContextAccessor = workContextAccessor; + public UrlRuleProvider(IHttpContextAccessor httpContextAccessor) { + _httpContextAccessor = httpContextAccessor; } public void Process(RuleContext ruleContext) { - if (!String.Equals(ruleContext.FunctionName, "url", StringComparison.OrdinalIgnoreCase)) { + if (!String.Equals(ruleContext.FunctionName, "url", StringComparison.OrdinalIgnoreCase)) return; - } - var context = _workContextAccessor.GetContext(); - ruleContext.Result = context.HttpContext.Request.RawUrl.StartsWith(Convert.ToString(ruleContext.Arguments[0])); + var context = _httpContextAccessor.Current(); + var url = Convert.ToString(ruleContext.Arguments[0]); + if (url.StartsWith("~/")) { + url = url.Substring(2); + var appPath = context.Request.ApplicationPath; + if (appPath == "/") + appPath = ""; + url = string.Format("{0}/{1}", appPath, url); + } + if (!url.Contains("?") && url.EndsWith("/")) + url = url.TrimEnd('/'); + ruleContext.Result = url.EndsWith("*") + ? context.Request.RawUrl.ToUpperInvariant().StartsWith(url.ToUpperInvariant()) + : context.Request.Path.ToUpperInvariant() == url.ToUpperInvariant(); } } } \ No newline at end of file From 0ce0dcb69b63e251ff2ee0251dd4bdee50d0a7b1 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Tue, 12 Oct 2010 01:02:18 -0700 Subject: [PATCH 3/6] Preparing to put a content part placement subsystem in effect ContentShapeResult.Apply calls through the context.FindPlacement to acquire placement location DefaultContentDisplay component wires the FindPlacement onto the ShapeDescriptor.Placement ShapeDescriptor.Placement func contains the chained per-theme rule stack for part shape placement Next up - simple Placement.txt parser to convert rules into Placement chain --HG-- branch : composition --- .../DefaultShapeTableManagerTests.cs | 62 +++++++++ .../Core/Common/Drivers/BodyPartDriver.cs | 22 ++-- .../Core/Contents/Views/Items/Content.cshtml | 2 +- .../Core/Routable/Drivers/RoutePartDriver.cs | 8 +- .../DefaultContentDisplay.cs | 123 ++++++++++++++++++ .../DefaultContentManager.cs | 56 +------- .../Drivers/ContentPartDriver.cs | 18 ++- .../Drivers/ContentShapeResult.cs | 52 +++++--- .../Handlers/BuildShapeContext.cs | 4 + .../ContentManagement/IContentManager.cs | 6 + .../Descriptors/ShapeAlterationBuilder.cs | 24 +++- .../Descriptors/ShapeDescriptor.cs | 4 + src/Orchard/Orchard.Framework.csproj | 1 + 13 files changed, 293 insertions(+), 89 deletions(-) create mode 100644 src/Orchard/ContentManagement/DefaultContentDisplay.cs diff --git a/src/Orchard.Tests/DisplayManagement/Descriptors/DefaultShapeTableManagerTests.cs b/src/Orchard.Tests/DisplayManagement/Descriptors/DefaultShapeTableManagerTests.cs index 00871a191..990da3959 100644 --- a/src/Orchard.Tests/DisplayManagement/Descriptors/DefaultShapeTableManagerTests.cs +++ b/src/Orchard.Tests/DisplayManagement/Descriptors/DefaultShapeTableManagerTests.cs @@ -79,5 +79,67 @@ namespace Orchard.Tests.DisplayManagement.Descriptors { Assert.That(foo.Displaying.Single(), Is.SameAs(cb3)); Assert.That(foo.Displayed.Single(), Is.SameAs(cb4)); } + + [Test] + public void DefaultPlacementIsReturnedByDefault() { + var manager = _container.Resolve(); + + var hello = manager.GetShapeTable(null).Descriptors["Hello"]; + hello.DefaultPlacement = "Header:5"; + var result = hello.Placement(null); + Assert.That(result, Is.EqualTo("Header:5")); + } + + [Test] + public void DescribedPlacementIsReturnedIfNotNull() { + + _container.Resolve().Discover = + builder => builder.Describe("Hello") + .Placement(ctx => ctx.DisplayType == "Detail" ? "Main" : null) + .Placement(ctx => ctx.DisplayType == "Summary" ? "" : null); + + var manager = _container.Resolve(); + var hello = manager.GetShapeTable(null).Descriptors["Hello"]; + var result1 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" }); + var result2 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" }); + var result3 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" }); + hello.DefaultPlacement = "Header:5"; + var result4 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" }); + var result5 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" }); + var result6 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" }); + + Assert.That(result1, Is.EqualTo("Main")); + Assert.That(result2, Is.EqualTo("")); + Assert.That(result3, Is.Null); + Assert.That(result4, Is.EqualTo("Main")); + Assert.That(result5, Is.EqualTo("")); + Assert.That(result6, Is.EqualTo("Header:5")); + } + + [Test] + public void TwoArgumentVariationDoesSameThing() { + + _container.Resolve().Discover = + builder => builder.Describe("Hello") + .Placement(ctx => ctx.DisplayType == "Detail", "Main") + .Placement(ctx => ctx.DisplayType == "Summary", ""); + + var manager = _container.Resolve(); + var hello = manager.GetShapeTable(null).Descriptors["Hello"]; + var result1 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" }); + var result2 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" }); + var result3 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" }); + hello.DefaultPlacement = "Header:5"; + var result4 = hello.Placement(new ShapePlacementContext { DisplayType = "Detail" }); + var result5 = hello.Placement(new ShapePlacementContext { DisplayType = "Summary" }); + var result6 = hello.Placement(new ShapePlacementContext { DisplayType = "Tile" }); + + Assert.That(result1, Is.EqualTo("Main")); + Assert.That(result2, Is.EqualTo("")); + Assert.That(result3, Is.Null); + Assert.That(result4, Is.EqualTo("Main")); + Assert.That(result5, Is.EqualTo("")); + Assert.That(result6, Is.EqualTo("Header:5")); + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs b/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs index 694d6b59b..a9020d471 100644 --- a/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs +++ b/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs @@ -34,19 +34,17 @@ namespace Orchard.Core.Common.Drivers { } protected override DriverResult Display(BodyPart part, string displayType, dynamic shapeHelper) { - var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text)); - var body = shapeHelper.Parts_Common_Body(ContentPart: part, Html: new HtmlString(bodyText)); - if (!string.IsNullOrWhiteSpace(displayType)) - body.Metadata.Type = string.Format("{0}.{1}", body.Metadata.Type, displayType); - var location = part.GetLocation(displayType); - //return Combined( - // Services.Authorizer.Authorize(Permissions.ChangeOwner) ? ContentPartTemplate(model, "Parts/Common.Body.ManageWrapperPre").LongestMatch(displayType, "SummaryAdmin").Location(location) : null, - // Services.Authorizer.Authorize(Permissions.ChangeOwner) ? ContentPartTemplate(model, "Parts/Common.Body.Manage").LongestMatch(displayType, "SummaryAdmin").Location(location) : null, - // ContentPartTemplate(model, TemplateName, Prefix).LongestMatch(displayType, "Summary", "SummaryAdmin").Location(location), - // Services.Authorizer.Authorize(Permissions.ChangeOwner) ? ContentPartTemplate(model, "Parts/Common.Body.ManageWrapperPost").LongestMatch(displayType, "SummaryAdmin").Location(location) : null); - - return ContentShape(body).Location(location); + return Combined( + ContentShape("Parts_Common_Body", displayType == "Detail" ? "Content" : null, () => { + var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text)); + return shapeHelper.Parts_Common_Body(ContentPart: part, Html: new HtmlString(bodyText)); + }), + ContentShape("Parts_Common_Body_Summary", displayType == "Summary" ? "Content" : null, () => { + var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text)); + return shapeHelper.Parts_Common_Body_Summary(ContentPart: part, Html: new HtmlString(bodyText)); + }) + ); } protected override DriverResult Editor(BodyPart part, dynamic shapeHelper) { diff --git a/src/Orchard.Web/Core/Contents/Views/Items/Content.cshtml b/src/Orchard.Web/Core/Contents/Views/Items/Content.cshtml index 9147b7ecb..46e4e50b9 100644 --- a/src/Orchard.Web/Core/Contents/Views/Items/Content.cshtml +++ b/src/Orchard.Web/Core/Contents/Views/Items/Content.cshtml @@ -3,6 +3,6 @@ @Display(Model.Header)
-@Display(Model.Primary) +@Display(Model.Content)
\ No newline at end of file diff --git a/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs b/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs index fb6c81c00..5a0b308cc 100644 --- a/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs +++ b/src/Orchard.Web/Core/Routable/Drivers/RoutePartDriver.cs @@ -45,11 +45,7 @@ namespace Orchard.Core.Routable.Drivers { } protected override DriverResult Display(RoutePart part, string displayType, dynamic shapeHelper) { - var routePart = shapeHelper.Parts_RoutableTitle(ContentPart: part, Title: part.Title); - if (!string.IsNullOrWhiteSpace(displayType)) - routePart.Metadata.Type = string.Format("{0}.{1}", routePart.Metadata.Type, displayType); - var location = part.GetLocation(displayType, "Header", "5"); - return ContentShape(routePart).Location(location); + return ContentShape("Parts_RoutableTitle", "Header:5", () => shapeHelper.Parts_RoutableTitle(ContentPart: part, Title: part.Title)); } protected override DriverResult Editor(RoutePart part, dynamic shapeHelper) { @@ -80,7 +76,7 @@ namespace Orchard.Core.Routable.Drivers { } protected override DriverResult Editor(RoutePart part, IUpdateModel updater, dynamic shapeHelper) { - + var model = new RoutableEditorViewModel(); updater.TryUpdateModel(model, Prefix, null, null); part.Title = model.Title; diff --git a/src/Orchard/ContentManagement/DefaultContentDisplay.cs b/src/Orchard/ContentManagement/DefaultContentDisplay.cs new file mode 100644 index 000000000..a90f29add --- /dev/null +++ b/src/Orchard/ContentManagement/DefaultContentDisplay.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Routing; +using Microsoft.CSharp.RuntimeBinder; +using Orchard.ContentManagement.Handlers; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Descriptors; +using Orchard.Logging; +using Orchard.Mvc; +using Orchard.Themes; + +namespace Orchard.ContentManagement { + public class DefaultContentDisplay : IContentDisplay { + private readonly Lazy> _handlers; + private readonly IShapeHelperFactory _shapeHelperFactory; + private readonly IShapeTableManager _shapeTableManager; + private readonly IWorkContextAccessor _workContextAccessor; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly Lazy _themeService; + private readonly RequestContext _requestContext; + + public DefaultContentDisplay( + Lazy> handlers, + IShapeHelperFactory shapeHelperFactory, + IShapeTableManager shapeTableManager, + IWorkContextAccessor workContextAccessor, + IHttpContextAccessor httpContextAccessor, + Lazy themeService, + RequestContext requestContext) { + _handlers = handlers; + _shapeHelperFactory = shapeHelperFactory; + _shapeTableManager = shapeTableManager; + _workContextAccessor = workContextAccessor; + _httpContextAccessor = httpContextAccessor; + _themeService = themeService; + _requestContext = requestContext; + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + static readonly CallSiteCollection _shapeHelperCalls = new CallSiteCollection(shapeTypeName => Binder.InvokeMember( + CSharpBinderFlags.None, + shapeTypeName, + Enumerable.Empty(), + null, + new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); + + + public dynamic BuildDisplay(IContent content, string displayType) { + var contentTypeDefinition = content.ContentItem.TypeDefinition; + string stereotype; + if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype)) + stereotype = "Content"; + + var shapeTypeName = "Items_" + stereotype; + var shapeDisplayType = string.IsNullOrWhiteSpace(displayType) ? "Detail" : displayType; + + var shapeHelper = _shapeHelperFactory.CreateHelper(); + var itemShape = _shapeHelperCalls.Invoke(shapeHelper, shapeTypeName); + + itemShape.ContentItem = content.ContentItem; + itemShape.Metadata.DisplayType = shapeDisplayType; + + var context = new BuildDisplayContext(itemShape, content, shapeDisplayType, _shapeHelperFactory); + BindPlacement(context, displayType); + + _handlers.Value.Invoke(handler => handler.BuildDisplay(context), Logger); + return context.Shape; + } + + public dynamic BuildEditor(IContent content) { + var shapeHelper = _shapeHelperFactory.CreateHelper(); + var itemShape = shapeHelper.Items_Content_Edit(); + + IContent iContent = content; + if (iContent != null) + itemShape.ContentItem = iContent.ContentItem; + + var context = new BuildEditorContext(itemShape, content, _shapeHelperFactory); + BindPlacement(context, null); + + _handlers.Value.Invoke(handler => handler.BuildEditor(context), Logger); + return context.Shape; + } + + public dynamic UpdateEditor(IContent content, IUpdateModel updater) { + var shapeHelper = _shapeHelperFactory.CreateHelper(); + var itemShape = shapeHelper.Items_Content_Edit(); + + IContent iContent = content; + if (iContent != null) + itemShape.ContentItem = iContent.ContentItem; + + var context = new UpdateEditorContext(itemShape, content, updater, _shapeHelperFactory); + BindPlacement(context, null); + + _handlers.Value.Invoke(handler => handler.UpdateEditor(context), Logger); + return context.Shape; + } + + private void BindPlacement(BuildShapeContext context, string displayType) { + context.FindPlacement = (partShapeType, defaultLocation) => { + //var workContext = _workContextAccessor.GetContext(); + //var theme = workContext.CurrentTheme; + var theme = _themeService.Value.GetRequestTheme(_requestContext); + var shapeTable = _shapeTableManager.GetShapeTable(theme.ThemeName); + ShapeDescriptor descriptor; + if (shapeTable.Descriptors.TryGetValue(partShapeType, out descriptor)) { + var placementContext = new ShapePlacementContext { + ContentType = context.ContentItem.ContentType, + DisplayType = displayType + }; + var location = descriptor.Placement(placementContext); + return location ?? defaultLocation; + } + return defaultLocation; + }; + } + } +} diff --git a/src/Orchard/ContentManagement/DefaultContentManager.cs b/src/Orchard/ContentManagement/DefaultContentManager.cs index 679753224..b6582c8be 100644 --- a/src/Orchard/ContentManagement/DefaultContentManager.cs +++ b/src/Orchard/ContentManagement/DefaultContentManager.cs @@ -4,14 +4,12 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using Autofac; -using Microsoft.CSharp.RuntimeBinder; using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.MetaData; using Orchard.ContentManagement.MetaData.Builders; using Orchard.ContentManagement.MetaData.Models; using Orchard.ContentManagement.Records; using Orchard.Data; -using Orchard.DisplayManagement; using Orchard.Indexing; using Orchard.Logging; @@ -23,7 +21,7 @@ namespace Orchard.ContentManagement { private readonly IRepository _contentItemVersionRepository; private readonly IContentDefinitionManager _contentDefinitionManager; private readonly Func _contentManagerSession; - private readonly IShapeHelperFactory _shapeHelperFactory; + private readonly IContentDisplay _contentDisplay; public DefaultContentManager( IComponentContext context, @@ -32,14 +30,14 @@ namespace Orchard.ContentManagement { IRepository contentItemVersionRepository, IContentDefinitionManager contentDefinitionManager, Func contentManagerSession, - IShapeHelperFactory shapeHelperFactory) { + IContentDisplay contentDisplay) { _context = context; _contentTypeRepository = contentTypeRepository; _contentItemRepository = contentItemRepository; _contentItemVersionRepository = contentItemVersionRepository; _contentDefinitionManager = contentDefinitionManager; _contentManagerSession = contentManagerSession; - _shapeHelperFactory = shapeHelperFactory; + _contentDisplay = contentDisplay; Logger = NullLogger.Instance; } @@ -370,59 +368,17 @@ namespace Orchard.ContentManagement { return context.Metadata; } - static readonly CallSiteCollection _shapeHelperCalls = new CallSiteCollection(shapeTypeName => Binder.InvokeMember( - CSharpBinderFlags.None, - shapeTypeName, - Enumerable.Empty(), - null, - new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })); - public dynamic BuildDisplay(IContent content, string displayType = "") { - var contentTypeDefinition = content.ContentItem.TypeDefinition; - string stereotype; - if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype)) - stereotype = "Content"; - - var shapeTypeName = "Items_" + stereotype; - var shapeDisplayType = string.IsNullOrWhiteSpace(displayType) ? "Detail" : displayType; - - var shapeHelper = _shapeHelperFactory.CreateHelper(); - var itemShape = _shapeHelperCalls.Invoke(shapeHelper, shapeTypeName); - - itemShape.ContentItem = content.ContentItem; - itemShape.Metadata.DisplayType = shapeDisplayType; - - var context = new BuildDisplayContext(itemShape, content, shapeDisplayType, _shapeHelperFactory); - Handlers.Invoke(handler => handler.BuildDisplay(context), Logger); - return context.Shape; + return _contentDisplay.BuildDisplay(content, displayType); } - public dynamic BuildEditor(IContent content) { - var shapeHelper = _shapeHelperFactory.CreateHelper(); - var itemShape = shapeHelper.Items_Content_Edit(); - - IContent iContent = content; - if (iContent != null) - itemShape.ContentItem = iContent.ContentItem; - - var context = new BuildEditorContext(itemShape, content, _shapeHelperFactory); - Handlers.Invoke(handler => handler.BuildEditor(context), Logger); - return context.Shape; + return _contentDisplay.BuildEditor(content); } public dynamic UpdateEditor(IContent content, IUpdateModel updater) { - var shapeHelper = _shapeHelperFactory.CreateHelper(); - var itemShape = shapeHelper.Items_Content_Edit(); - - IContent iContent = content; - if (iContent != null) - itemShape.ContentItem = iContent.ContentItem; - - var context = new UpdateEditorContext(itemShape, content, updater, _shapeHelperFactory); - Handlers.Invoke(handler => handler.UpdateEditor(context), Logger); - return context.Shape; + return _contentDisplay.UpdateEditor(content, updater); } public IContentQuery Query() { diff --git a/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs b/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs index bf88335ea..cfe937ef0 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.MetaData; +using Orchard.DisplayManagement; namespace Orchard.ContentManagement.Drivers { public abstract class ContentPartDriver : IContentPartDriver where TContent : ContentPart, new() { @@ -27,8 +28,21 @@ namespace Orchard.ContentManagement.Drivers { protected virtual DriverResult Editor(TContent part, dynamic shapeHelper) { return null; } protected virtual DriverResult Editor(TContent part, IUpdateModel updater, dynamic shapeHelper) { return null; } - public ContentShapeResult ContentShape(dynamic shape) { - return new ContentShapeResult(shape, Prefix).Location(Zone); + [Obsolete("Provided while transitioning to factory variations")] + public ContentShapeResult ContentShape(IShape shape) { + return ContentShapeImplementation(shape.Metadata.Type, Zone, () => shape); + } + + public ContentShapeResult ContentShape(string shapeType, Func factory) { + return ContentShapeImplementation(shapeType, null, factory); + } + + public ContentShapeResult ContentShape(string shapeType, string defaultLocation, Func factory) { + return ContentShapeImplementation(shapeType, defaultLocation, factory); + } + + private ContentShapeResult ContentShapeImplementation(string shapeType, string defaultLocation, Func factory) { + return new ContentShapeResult(shapeType, Prefix, factory).Location(defaultLocation); } [Obsolete] diff --git a/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs b/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs index 0c4328abf..3c4e6af78 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentShapeResult.cs @@ -1,37 +1,57 @@ -using Orchard.ContentManagement.Handlers; +using System; +using Orchard.ContentManagement.Handlers; using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Shapes; namespace Orchard.ContentManagement.Drivers { public class ContentShapeResult : DriverResult { - public dynamic Shape { get; set; } - public string Prefix { get; set; } - public string Zone { get; set; } - public string Position { get; set; } + private string _defaultLocation; + private readonly string _shapeType; + private readonly string _prefix; + private readonly Func _shapeBuilder; - public ContentShapeResult(dynamic shape, string prefix) { - Shape = shape; - Prefix = prefix; + public ContentShapeResult(string shapeType, string prefix, Func shapeBuilder) { + _shapeType = shapeType; + _prefix = prefix; + _shapeBuilder = shapeBuilder; } public override void Apply(BuildDisplayContext context) { - context.Shape.Zones[Zone].Add(Shape, Position); + ApplyImplementation(context, context.DisplayType); } public override void Apply(BuildEditorContext context) { - IShape iShape = Shape; - if (iShape != null ) - Shape.Metadata.Prefix = Prefix; - context.Shape.Zones[Zone].Add(Shape, Position); + ApplyImplementation(context, null); + } + + private void ApplyImplementation(BuildShapeContext context, string displayType) { + var location = context.FindPlacement(_shapeType, _defaultLocation); + if (string.IsNullOrEmpty(location) || location == "-") + return; + + dynamic parentShape = context.Shape; + IShape contentShape = _shapeBuilder(); + contentShape.Metadata.Prefix = _prefix; + contentShape.Metadata.DisplayType = displayType; + + var delimiterIndex = location.IndexOf(':'); + if (delimiterIndex < 0) { + parentShape.Zones[location].Add(contentShape); + } + else { + var zoneName = location.Substring(0, delimiterIndex); + var position = location.Substring(delimiterIndex + 1); + parentShape.Zones[zoneName].Add(contentShape, position); + } } public ContentShapeResult Location(string zone) { - Zone = zone; + _defaultLocation = zone; return this; } public ContentShapeResult Location(string zone, string position) { - Zone = zone; - Position = position; + _defaultLocation = zone + ":" + position; return this; } diff --git a/src/Orchard/ContentManagement/Handlers/BuildShapeContext.cs b/src/Orchard/ContentManagement/Handlers/BuildShapeContext.cs index e0a4b1cf8..216f27995 100644 --- a/src/Orchard/ContentManagement/Handlers/BuildShapeContext.cs +++ b/src/Orchard/ContentManagement/Handlers/BuildShapeContext.cs @@ -1,3 +1,4 @@ +using System; using Orchard.DisplayManagement; namespace Orchard.ContentManagement.Handlers { @@ -6,10 +7,13 @@ namespace Orchard.ContentManagement.Handlers { Shape = shape; ContentItem = content.ContentItem; New = shapeHelperFactory.CreateHelper(); + FindPlacement = (partType, defaultLocation) => defaultLocation; } public dynamic Shape { get; private set; } public ContentItem ContentItem { get; private set; } public dynamic New { get; private set; } + + public Func FindPlacement { get; set; } } } \ No newline at end of file diff --git a/src/Orchard/ContentManagement/IContentManager.cs b/src/Orchard/ContentManagement/IContentManager.cs index 571ed0325..127b07de3 100644 --- a/src/Orchard/ContentManagement/IContentManager.cs +++ b/src/Orchard/ContentManagement/IContentManager.cs @@ -31,6 +31,12 @@ namespace Orchard.ContentManagement { dynamic UpdateEditor(IContent content, IUpdateModel updater); } + public interface IContentDisplay : IDependency { + dynamic BuildDisplay(IContent content, string displayType = ""); + dynamic BuildEditor(IContent content); + dynamic UpdateEditor(IContent content, IUpdateModel updater); + } + public class VersionOptions { public static VersionOptions Latest { get { return new VersionOptions { IsLatest = true }; } } public static VersionOptions Published { get { return new VersionOptions { IsPublished = true }; } } diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs index 312d469d0..93e796a46 100644 --- a/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeAlterationBuilder.cs @@ -73,7 +73,7 @@ namespace Orchard.DisplayManagement.Descriptors { descriptor.Created = existing.Concat(new[] { action }); }); } - + public ShapeAlterationBuilder OnDisplaying(Action action) { return Configure(descriptor => { var existing = descriptor.Displaying ?? Enumerable.Empty>(); @@ -88,8 +88,28 @@ namespace Orchard.DisplayManagement.Descriptors { }); } + public ShapeAlterationBuilder Placement(Func action) { + return Configure(descriptor => { + var next = descriptor.Placement; + descriptor.Placement = ctx => action(ctx) ?? next(ctx); + }); + } + + public ShapeAlterationBuilder Placement(Func predicate, string location) { + return Configure(descriptor => { + var next = descriptor.Placement; + descriptor.Placement = ctx => predicate(ctx) ? location : next(ctx); + }); + } + public ShapeAlteration Build() { return new ShapeAlteration(_shapeType, _feature, _configurations.ToArray()); } } -} \ No newline at end of file + + public class ShapePlacementContext { + public string ContentType { get; set; } + public string DisplayType { get; set; } + + } +} diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs index 28ef54af0..1c7a64109 100644 --- a/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs @@ -13,6 +13,7 @@ namespace Orchard.DisplayManagement.Descriptors { Displayed = Enumerable.Empty>(); Wrappers = new List(); Bindings = new Dictionary(); + Placement = ctx => DefaultPlacement; } public string ShapeType { get; set; } @@ -41,6 +42,9 @@ namespace Orchard.DisplayManagement.Descriptors { public IEnumerable> Displaying { get; set; } public IEnumerable> Displayed { get; set; } + public Func Placement { get; set; } + public string DefaultPlacement { get; set; } + public IList Wrappers { get; set; } } diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index d5f2f1843..b1be6b24e 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -159,6 +159,7 @@ + From 1506a588db2bdb8babba2a7b348bc27818672f88 Mon Sep 17 00:00:00 2001 From: Nathan Heskew Date: Tue, 12 Oct 2010 10:18:19 -0700 Subject: [PATCH 4/6] pushing the composition branch --HG-- branch : composition --- src/Shapes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Shapes.txt b/src/Shapes.txt index 58e35f8f3..6882ce5a7 100644 --- a/src/Shapes.txt +++ b/src/Shapes.txt @@ -24,6 +24,7 @@ MenuItem -RouteValues -Item (clr object) + List: ul|ol + li* .Items (meta-property, bound to shape children or passed in) .Id From fd6f4822957d85479734f4fcba5cc17bf2eb1f14 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Tue, 12 Oct 2010 11:18:00 -0700 Subject: [PATCH 5/6] Adding a ShapePlacementParsingStrategy Built-in shape table provider to provide alterations based on placement files in modules and themes --HG-- branch : composition --- src/Orchard.Web/Core/Orchard.Core.csproj | 2 +- .../ShapePlacementParsingStrategy.cs | 56 +++++++++++++++++++ src/Orchard/Orchard.Framework.csproj | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapePlacements/ShapePlacementParsingStrategy.cs diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj index 31141c616..2beb761d3 100644 --- a/src/Orchard.Web/Core/Orchard.Core.csproj +++ b/src/Orchard.Web/Core/Orchard.Core.csproj @@ -392,7 +392,7 @@ - + diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapePlacements/ShapePlacementParsingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ShapePlacements/ShapePlacementParsingStrategy.cs new file mode 100644 index 000000000..d2ab225d3 --- /dev/null +++ b/src/Orchard/DisplayManagement/Descriptors/ShapePlacements/ShapePlacementParsingStrategy.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Orchard.Environment.Descriptor.Models; +using Orchard.Environment.Extensions; +using Orchard.Environment.Extensions.Models; + +namespace Orchard.DisplayManagement.Descriptors.ShapePlacements { + public class ShapePlacementParsingStrategy : IShapeTableProvider { + private readonly IExtensionManager _extensionManager; + private readonly ShellDescriptor _shellDescriptor; + + public ShapePlacementParsingStrategy( + IExtensionManager extensionManager, + ShellDescriptor shellDescriptor) { + _extensionManager = extensionManager; + _shellDescriptor = shellDescriptor; + } + + public void Discover(ShapeTableBuilder builder) { + + var availableFeatures = _extensionManager.AvailableFeatures(); + var activeFeatures = availableFeatures.Where(fd => FeatureIsTheme(fd) || FeatureIsEnabled(fd)); + var activeExtensions = Once(activeFeatures); + + foreach (var extensionDescriptor in activeExtensions) { + foreach (var featureDescriptor in extensionDescriptor.Features.Where(fd=>fd.Name == fd.Extension.Name)) { + builder.Describe("Parts_RoutableTitle") + .From(new Feature{Descriptor = featureDescriptor}) + .Placement(ctx => ctx.ContentType == "WidgetPage", "Content:after"); + } + //var featureDescriptors = extensionDescriptor.Where(fd => fd.Name == hit.extensionDescriptor.Name); + //foreach (var featureDescriptor in featureDescriptors) { + } + //builder.Describe("Parts_RoutableTitle") + //.Placement(ctx => ctx.ContentType == "Page", "Content:after"); + } + + + private bool FeatureIsTheme(FeatureDescriptor fd) { + return fd.Extension.ExtensionType == "Theme"; + } + + private bool FeatureIsEnabled(FeatureDescriptor fd) { + return _shellDescriptor.Features.Any(sf => sf.Name == fd.Name); + } + + private static IEnumerable Once(IEnumerable featureDescriptors) { + var once = new ConcurrentDictionary(); + return featureDescriptors.Select(fd => fd.Extension).Where(ed => once.TryAdd(ed.Name, null)).ToList(); + } + + } +} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index b1be6b24e..a94c28422 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -165,6 +165,7 @@ + From 7e84e73cae7a63f966c8e310f54ed7b119c01401 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Tue, 12 Oct 2010 17:33:11 -0700 Subject: [PATCH 6/6] Placement.info support enabled Enabled Module and Theme Placement.info files are parsed, and influence the location of certain shapes This only applies to part or field shapes that are added by returning ContentShape(...) from a display/editor driver --HG-- branch : composition extra : transplant_source : %CF%B0%17%E1%18%C1%06o%B2%91a%23%A1%3D%872%BE%F8%01%F3 --- .../Descriptors/InMemoryWebSiteFolder.cs | 52 +++++++++ .../Descriptors/PlacementFileParserTests.cs | 92 +++++++++++++++ .../Orchard.Framework.Tests.csproj | 2 + .../Orchard.Widgets/Orchard.Widgets.csproj | 1 + .../Orchard.Widgets/Views/Items_Widget.cshtml | 4 + src/Orchard.Web/Orchard.Web.csproj | 1 + src/Orchard.Web/Themes/Classic/Placement.info | 21 ++++ src/Orchard.Web/Themes/Web.config | 4 +- src/Orchard.Web/Web.config | 2 +- .../DefaultContentManager.cs | 10 +- .../ShapePlacementStrategy/PlacementFile.cs | 19 +++ .../PlacementFileParser.cs | 83 +++++++++++++ .../ShapePlacementParsingStrategy.cs | 110 ++++++++++++++++++ .../ShapePlacementParsingStrategy.cs | 56 --------- src/Orchard/Orchard.Framework.csproj | 3 +- 15 files changed, 396 insertions(+), 64 deletions(-) create mode 100644 src/Orchard.Tests/DisplayManagement/Descriptors/InMemoryWebSiteFolder.cs create mode 100644 src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Widgets/Views/Items_Widget.cshtml create mode 100644 src/Orchard.Web/Themes/Classic/Placement.info create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/PlacementFile.cs create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/PlacementFileParser.cs create mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs delete mode 100644 src/Orchard/DisplayManagement/Descriptors/ShapePlacements/ShapePlacementParsingStrategy.cs diff --git a/src/Orchard.Tests/DisplayManagement/Descriptors/InMemoryWebSiteFolder.cs b/src/Orchard.Tests/DisplayManagement/Descriptors/InMemoryWebSiteFolder.cs new file mode 100644 index 000000000..315ea32f5 --- /dev/null +++ b/src/Orchard.Tests/DisplayManagement/Descriptors/InMemoryWebSiteFolder.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Orchard.Caching; +using Orchard.FileSystems.WebSite; + +namespace Orchard.Tests.DisplayManagement.Descriptors { + public class InMemoryWebSiteFolder : IWebSiteFolder { + public InMemoryWebSiteFolder() { + Contents = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public IDictionary Contents { get; set; } + + public IEnumerable ListDirectories(string virtualPath) { + throw new NotImplementedException(); + } + + public IEnumerable ListFiles(string virtualPath, bool recursive) { + throw new NotImplementedException(); + } + + public bool FileExists(string virtualPath) { + throw new NotImplementedException(); + } + + public string ReadFile(string virtualPath) { + string result; + return Contents.TryGetValue(virtualPath, out result) ? result : null; + } + + public string ReadFile(string virtualPath, bool actualContent) { + throw new NotImplementedException(); + } + + public void CopyFileTo(string virtualPath, Stream destination) { + throw new NotImplementedException(); + } + + public void CopyFileTo(string virtualPath, Stream destination, bool actualContent) { + throw new NotImplementedException(); + } + + public IVolatileToken WhenPathChanges(string virtualPath) { + return new Token { IsCurrent = true }; + } + + public class Token : IVolatileToken { + public bool IsCurrent { get; set; } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs b/src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs new file mode 100644 index 000000000..899617170 --- /dev/null +++ b/src/Orchard.Tests/DisplayManagement/Descriptors/PlacementFileParserTests.cs @@ -0,0 +1,92 @@ +using System.Linq; +using System.Text; +using Autofac; +using NUnit.Framework; +using Orchard.Caching; +using Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy; +using Orchard.FileSystems.WebSite; +using Orchard.Tests.Stubs; + +namespace Orchard.Tests.DisplayManagement.Descriptors { + [TestFixture] + public class PlacementFileParserTests : ContainerTestBase { + private IPlacementFileParser _parser; + private InMemoryWebSiteFolder _folder; + + protected override void Register(Autofac.ContainerBuilder builder) { + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As() + .As().InstancePerLifetimeScope(); + } + + + protected override void Resolve(IContainer container) { + _parser = container.Resolve(); + _folder = container.Resolve(); + } + + [Test] + public void ParsingMissingFileIsNull() { + var result = _parser.Parse("~/hello.xml"); + Assert.That(result, Is.Null); + } + + [Test] + public void ParsingEmptyFileAsNothing() { + _folder.Contents["~/hello.xml"] = ""; + var result = _parser.Parse("~/hello.xml"); + Assert.That(result, Is.Not.Null); + Assert.That(result.Nodes, Is.Not.Null); + Assert.That(result.Nodes.Count(), Is.EqualTo(0)); + } + + [Test] + public void ItemsComeBackAsPlacementNodes() { + _folder.Contents["~/hello.xml"] = @" + + + + +"; + var result = _parser.Parse("~/hello.xml"); + Assert.That(result, Is.Not.Null); + Assert.That(result.Nodes, Is.Not.Null); + Assert.That(result.Nodes.Count(), Is.EqualTo(2)); + } + + + [Test] + public void NestedItemsComeBackAsNestedNodes() { + _folder.Contents["~/hello.xml"] = @" + + + + + + +"; + var result = _parser.Parse("~/hello.xml"); + Assert.That(result, Is.Not.Null); + Assert.That(result.Nodes, Is.Not.Null); + Assert.That(result.Nodes.Count(), Is.EqualTo(2)); + Assert.That(result.Nodes.First().Nodes.Count(), Is.EqualTo(1)); + Assert.That(result.Nodes.Last().Nodes.Count(), Is.EqualTo(0)); + } + + [Test] + public void EachPlaceAttributeIsShapeLocation() { + _folder.Contents["~/hello.xml"] = @" + +"; + var result = _parser.Parse("~/hello.xml"); + Assert.That(result, Is.Not.Null); + Assert.That(result.Nodes, Is.Not.Null); + Assert.That(result.Nodes.Count(), Is.EqualTo(2)); + var foo = result.Nodes.OfType().Single(x=>x.ShapeType == "Foo"); + var bar = result.Nodes.OfType().Single(x=>x.ShapeType == "Bar"); + Assert.That(foo.Location, Is.EqualTo("Header")); + Assert.That(bar.Location, Is.EqualTo("Content:after")); + } + } +} diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 635b194dd..cc65734bc 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -216,6 +216,8 @@ + + diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj b/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj index f71910ccf..299f79a33 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj @@ -115,6 +115,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Views/Items_Widget.cshtml b/src/Orchard.Web/Modules/Orchard.Widgets/Views/Items_Widget.cshtml new file mode 100644 index 000000000..0fa937dbb --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Views/Items_Widget.cshtml @@ -0,0 +1,4 @@ +TheWidget +@Display(Model.Content) +@Display(Model.Primary) +@Display(Model.Body) diff --git a/src/Orchard.Web/Orchard.Web.csproj b/src/Orchard.Web/Orchard.Web.csproj index 8062f4b86..9ba3e9241 100644 --- a/src/Orchard.Web/Orchard.Web.csproj +++ b/src/Orchard.Web/Orchard.Web.csproj @@ -125,6 +125,7 @@ + diff --git a/src/Orchard.Web/Themes/Classic/Placement.info b/src/Orchard.Web/Themes/Classic/Placement.info new file mode 100644 index 000000000..af3251204 --- /dev/null +++ b/src/Orchard.Web/Themes/Classic/Placement.info @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/src/Orchard.Web/Themes/Web.config b/src/Orchard.Web/Themes/Web.config index f2f307586..ae3d47d77 100644 --- a/src/Orchard.Web/Themes/Web.config +++ b/src/Orchard.Web/Themes/Web.config @@ -5,6 +5,8 @@ + +