From 2365083307af0a61c2651ca3d168fb45622c0f70 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 17 Feb 2011 15:31:00 -0800 Subject: [PATCH 1/5] Adding Fields alternates --HG-- branch : dev --- .../Drivers/ContentFieldDriver.cs | 87 ++++++++++++++++--- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs b/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs index 84f2edba7..849a12dd1 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs @@ -71,24 +71,91 @@ namespace Orchard.ContentManagement.Drivers { private object AddAlternates(dynamic shape, string differentiator) { // automatically add shape alternates for shapes added by fields - // [ShapeType__FieldName] for ShapeType-FieldName.cshtml templates - // [ShapeType__PartName] for ShapeType-PartName.cshtml templates // [ShapeType__PartName__FieldName] for ShapeType-PartName-FieldName.cshtml templates // for fields on dynamic parts the part name is the same as the content type name - // ex. Fields/Common.Text-Something.FirstName ShapeMetadata metadata = shape.Metadata; - if (!string.IsNullOrEmpty(differentiator)) - metadata.Alternates.Add(metadata.Type + "__" + differentiator); - ContentPart part = shape.ContentPart; - if (part != null) { - metadata.Alternates.Add(metadata.Type + "__" + part.PartDefinition.Name); - if (!string.IsNullOrEmpty(differentiator)) - metadata.Alternates.Add(metadata.Type + "__" + part.PartDefinition.Name + "__" + differentiator); + var shapeType = metadata.Type; + var fieldName = differentiator ?? String.Empty; + var partName = part != null ? part.PartDefinition.Name : String.Empty; + var contentType = part != null ? part.ContentItem.ContentType : String.Empty; + var displayType = metadata.DisplayType ?? String.Empty; + var dynamicType = string.Equals(partName, contentType, StringComparison.Ordinal); + + var url = ""; + + // [ShapeType__FieldName] e.g. Fields/Common.Text-Teaser + if ( !string.IsNullOrEmpty(fieldName) ) + metadata.Alternates.Add(shapeType + "__" + fieldName); + + // [ShapeType__PartName] e.g. Fields/Common.Text-TeaserPart + if ( !string.IsNullOrEmpty(partName) ) { + metadata.Alternates.Add(shapeType + "__" + partName); } + // [ShapeType]__[ContentType]__[PartName] e.g. Fields/Common.Text-Blog-TeaserPart + if ( !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(contentType) && !dynamicType ) { + metadata.Alternates.Add(shapeType + "__" + contentType + "__" + partName); + } + + // [ShapeType]_[DisplayType]__[FieldName] e.g. Fields/Common.Text-Teaser.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(fieldName) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + fieldName); + } + + // [ShapeType]__[PartName]__[FieldName] e.g. Fields/Common.Text-TeaserPart-Teaser + if ( !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) ) { + metadata.Alternates.Add(shapeType + "__" + partName + "__" + fieldName); + } + + // [ShapeType]__[ContentType]__[FieldName] e.g. Fields/Common.Text-Blog-Teaser + if ( !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(fieldName) ) { + metadata.Alternates.Add(shapeType + "__" + contentType + "__" + fieldName); + } + + // [ShapeType]__[ContentType]__[PartName]__[FieldName] e.g. Fields/Common.Text-Blog-TeaserPart-Teaser + if ( !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) && !dynamicType ) { + metadata.Alternates.Add(shapeType + "__" + contentType + "__" + partName ); + } + + // [ShapeType]__[ContentType]__[PartName]__[FieldName]__url__[Url] e.g. Fields/Common.Text-Blog-TeaserPart-Teaser-url-myBlog + if ( !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) && !dynamicType ) { + metadata.Alternates.Add(shapeType + "__" + contentType + "__" + partName + "__" + fieldName + "__url__" + url); + } + + // [ShapeType]_[DisplayType]__[PartName] e.g. Fields/Common.Text-TeaserPart.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(partName) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + partName); + } + + // [ShapeType]_[DisplayType]__[ContentType]__[PartName] e.g. Fields/Common.Text-Blog-TeaserPart.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !dynamicType ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + contentType + "__" + partName); + } + + // [ShapeType]_[DisplayType]__[PartName]__[FieldName] e.g. Fields/Common.Text-TeaserPart-Teaser.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + partName + "__" + fieldName); + } + + // [ShapeType]_[DisplayType]__[ContentType]__[PartName]__[FieldName] e.g. Fields/Common.Text-Blog-TeaserPart-Teaser.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !dynamicType && !string.IsNullOrEmpty(fieldName) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + contentType + "__" + partName + "__" + fieldName); + } + + // [ShapeType]_[DisplayType]__[PartName]__[FieldName]__url__[Url] e.g. Fields/Common.Text-TeaserPart-Teaser-url-myBlog.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + partName + "__" + fieldName + "__url__" + url); + } + + // [ShapeType]_[DisplayType]__[ContentType]__[PartName]__[FieldName]__url__[Url] e.g. Fields/Common.Text-Blog-TeaserPart-Teaser-url-myBlog.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !dynamicType && !string.IsNullOrEmpty(fieldName) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + contentType + "__" + partName + "__" + fieldName + "__url__" + url); + } + + return shape; } From cdc7c86f3bcff1715db686a2c338eb4fb5e26cbf Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Thu, 17 Feb 2011 15:31:11 -0800 Subject: [PATCH 2/5] Adding Content alternates --HG-- branch : dev --- src/Orchard.Web/Core/Contents/Shapes.cs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Orchard.Web/Core/Contents/Shapes.cs b/src/Orchard.Web/Core/Contents/Shapes.cs index 8435a9806..18aa7e853 100644 --- a/src/Orchard.Web/Core/Contents/Shapes.cs +++ b/src/Orchard.Web/Core/Contents/Shapes.cs @@ -12,16 +12,28 @@ namespace Orchard.Core.Contents { .OnDisplaying(displaying => { ContentItem contentItem = displaying.Shape.ContentItem; if (contentItem != null) { - //Content-BlogPost + + var url = string.Empty; + + // Content__[ContentType] e.g. Content-BlogPost displaying.ShapeMetadata.Alternates.Add("Content__" + contentItem.ContentType); - //Content-42 - displaying.ShapeMetadata.Alternates.Add("Content__" + contentItem.Id); - //Content.Summary + + // Content__[ContentType]__url__[Url] e.g. Content-BlogPost-url-myBlog + displaying.ShapeMetadata.Alternates.Add("Content__" + contentItem.ContentType + "__url__" + url); + + // Content_[DisplayType] e.g. Content.Summary displaying.ShapeMetadata.Alternates.Add("Content_" + displaying.ShapeMetadata.DisplayType); - //Content-Page.Summary + + // Content_[DisplayType]__[ContentType] e.g. Content-BlogPost.Summary displaying.ShapeMetadata.Alternates.Add("Content_" + displaying.ShapeMetadata.DisplayType + "__" + contentItem.ContentType); - if (!displaying.ShapeMetadata.DisplayType.Contains("Admin")) + // Content__[Id] e.g. Content-42 + displaying.ShapeMetadata.Alternates.Add("Content__" + contentItem.Id); + + // Content_[DisplayType]__[Id] e.g. Content-42.Summary + displaying.ShapeMetadata.Alternates.Add("Content_" + displaying.ShapeMetadata.DisplayType + "__" + contentItem.Id); + + if ( !displaying.ShapeMetadata.DisplayType.Contains("Admin") ) displaying.ShapeMetadata.Wrappers.Add("Content_ControlWrapper"); } }); From 9fca99c51b7b2066c97257991e929b0232b8428f Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Mon, 21 Feb 2011 11:35:54 -0800 Subject: [PATCH 3/5] Adding new alternates - Injected alternates in curent Core/Framework - Created a new Feature in Orchard.DesignerTools to add Url based alternates (e.g. __url__homepage) - Added Work<> meta-dependency to inject dependencies in non-dependency enables classes --HG-- branch : dev --- src/Orchard.Web/Core/Contents/Shapes.cs | 14 +-- src/Orchard.Web/Core/Shapes/CoreShapes.cs | 43 ++++---- .../Modules/Orchard.DesignerTools/Module.txt | 4 + .../Orchard.DesignerTools.csproj | 1 + .../Services/UrlAlternatesFactory.cs | 50 +++++++++ .../Modules/Orchard.Widgets/Shapes.cs | 6 +- .../Drivers/ContentFieldDriver.cs | 27 +---- .../Drivers/ContentPartDriver.cs | 41 ++++++- .../Implementation/DefaultDisplayManager.cs | 8 +- .../DisplayManagement/Shapes/ShapeMetadata.cs | 20 +++- src/Orchard/Environment/WorkContextModule.cs | 100 +++++++++++++++++- 11 files changed, 253 insertions(+), 61 deletions(-) create mode 100644 src/Orchard.Web/Modules/Orchard.DesignerTools/Services/UrlAlternatesFactory.cs diff --git a/src/Orchard.Web/Core/Contents/Shapes.cs b/src/Orchard.Web/Core/Contents/Shapes.cs index 18aa7e853..0f51956bf 100644 --- a/src/Orchard.Web/Core/Contents/Shapes.cs +++ b/src/Orchard.Web/Core/Contents/Shapes.cs @@ -1,4 +1,7 @@ -using Orchard.ContentManagement; +using System; +using System.Linq; +using System.Web; +using Orchard.ContentManagement; using Orchard.DisplayManagement.Descriptors; namespace Orchard.Core.Contents { @@ -12,18 +15,9 @@ namespace Orchard.Core.Contents { .OnDisplaying(displaying => { ContentItem contentItem = displaying.Shape.ContentItem; if (contentItem != null) { - - var url = string.Empty; - // Content__[ContentType] e.g. Content-BlogPost displaying.ShapeMetadata.Alternates.Add("Content__" + contentItem.ContentType); - // Content__[ContentType]__url__[Url] e.g. Content-BlogPost-url-myBlog - displaying.ShapeMetadata.Alternates.Add("Content__" + contentItem.ContentType + "__url__" + url); - - // Content_[DisplayType] e.g. Content.Summary - displaying.ShapeMetadata.Alternates.Add("Content_" + displaying.ShapeMetadata.DisplayType); - // Content_[DisplayType]__[ContentType] e.g. Content-BlogPost.Summary displaying.ShapeMetadata.Alternates.Add("Content_" + displaying.ShapeMetadata.DisplayType + "__" + contentItem.ContentType); diff --git a/src/Orchard.Web/Core/Shapes/CoreShapes.cs b/src/Orchard.Web/Core/Shapes/CoreShapes.cs index ee10d12b3..0a20329d0 100644 --- a/src/Orchard.Web/Core/Shapes/CoreShapes.cs +++ b/src/Orchard.Web/Core/Shapes/CoreShapes.cs @@ -9,6 +9,7 @@ using System.Web.Mvc.Html; using Orchard.DisplayManagement; using Orchard.DisplayManagement.Descriptors; using Orchard.DisplayManagement.Descriptors.ResourceBindingStrategy; +using Orchard.Environment; using Orchard.Mvc; using Orchard.Settings; using Orchard.UI; @@ -20,23 +21,20 @@ using Orchard.Utility.Extensions; namespace Orchard.Core.Shapes { public class CoreShapes : IShapeTableProvider { - private readonly IWorkContextAccessor _workContextAccessor; - private readonly IHttpContextAccessor _httpContextAccessor; + private readonly Work _workContext; + private readonly Work _resourceManager; + private readonly Work _httpContextAccessor; - public CoreShapes(IWorkContextAccessor workContextAccessor, IHttpContextAccessor httpContextAccessor) { - // needed to get CurrentSite. - // note that injecting ISiteService here causes a stack overflow in AutoFac! - _workContextAccessor = workContextAccessor; + public CoreShapes( + Work workContext, + Work resourceManager, + Work httpContextAccessor + ) { + _workContext = workContext; + _resourceManager = resourceManager; _httpContextAccessor = httpContextAccessor; } - // not injected the usual way because this component is a 'static' dependency and RM is per-request - private IResourceManager ResourceManager { - get { - return _workContextAccessor.GetContext(_httpContextAccessor.Current()).Resolve(); - } - } - public void Discover(ShapeTableBuilder builder) { // the root page shape named 'Layout' is wrapped with 'Document' // and has an automatic zone creating behavior @@ -54,6 +52,7 @@ namespace Orchard.Core.Shapes { layout.Content = created.New.Zone(); layout.Content.ZoneName = "Content"; layout.Content.Add(created.New.PlaceChildContent(Source: layout)); + }); // 'Zone' shapes are built on the Zone base class @@ -66,6 +65,8 @@ namespace Orchard.Core.Shapes { string zoneName = zone.ZoneName; zone.Classes.Add("zone-" + zoneName.HtmlClassify()); zone.Classes.Add("zone"); + + // Zone__[ZoneName] e.g. Zone-SideBar zone.Metadata.Alternates.Add("Zone__" + zoneName); }); @@ -208,25 +209,25 @@ namespace Orchard.Core.Shapes { [Shape] public void HeadScripts(dynamic Display, TextWriter Output) { WriteResources(Display, Output, "script", ResourceLocation.Head, null); - WriteLiteralScripts(Output, ResourceManager.GetRegisteredHeadScripts()); + WriteLiteralScripts(Output, _resourceManager.Value.GetRegisteredHeadScripts()); } [Shape] public void FootScripts(dynamic Display, TextWriter Output) { WriteResources(Display, Output, "script", null, ResourceLocation.Head); - WriteLiteralScripts(Output, ResourceManager.GetRegisteredFootScripts()); + WriteLiteralScripts(Output, _resourceManager.Value.GetRegisteredFootScripts()); } [Shape] public void Metas(TextWriter Output) { - foreach (var meta in ResourceManager.GetRegisteredMetas()) { + foreach (var meta in _resourceManager.Value.GetRegisteredMetas() ) { Output.WriteLine(meta.GetTag()); } } [Shape] public void HeadLinks(TextWriter Output) { - foreach (var link in ResourceManager.GetRegisteredLinks()) { + foreach (var link in _resourceManager.Value.GetRegisteredLinks() ) { Output.WriteLine(link.GetTag()); } } @@ -257,7 +258,7 @@ namespace Orchard.Core.Shapes { private void WriteResources(dynamic Display, TextWriter Output, string resourceType, ResourceLocation? includeLocation, ResourceLocation? excludeLocation) { bool debugMode; - var site = _workContextAccessor.GetContext(_httpContextAccessor.Current()).CurrentSite; + var site = _workContext.Value.CurrentSite; switch (site.ResourceDebugMode) { case ResourceDebugMode.Enabled: debugMode = true; @@ -267,15 +268,15 @@ namespace Orchard.Core.Shapes { break; default: Debug.Assert(site.ResourceDebugMode == ResourceDebugMode.FromAppSetting, "Unknown ResourceDebugMode value."); - debugMode = _httpContextAccessor.Current().IsDebuggingEnabled; + debugMode = _httpContextAccessor.Value.Current().IsDebuggingEnabled; break; } var defaultSettings = new RequireSettings { DebugMode = debugMode, Culture = CultureInfo.CurrentUICulture.Name, }; - var requiredResources = ResourceManager.BuildRequiredResources(resourceType); - var appPath = _httpContextAccessor.Current().Request.ApplicationPath; + var requiredResources = _resourceManager.Value.BuildRequiredResources(resourceType); + var appPath = _httpContextAccessor.Value.Current().Request.ApplicationPath; foreach (var context in requiredResources.Where(r => (includeLocation.HasValue ? r.Settings.Location == includeLocation.Value : true) && (excludeLocation.HasValue ? r.Settings.Location != excludeLocation.Value : true))) { diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Module.txt b/src/Orchard.Web/Modules/Orchard.DesignerTools/Module.txt index af5301085..f3a68792e 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Module.txt @@ -11,3 +11,7 @@ Features: Category: Designer Description: Displays all currently displayed shapes and some information to customize them Dependencies: Orchard.jQuery + UrlAlternates: + Name: Url Alternates + Category: Designer + Description: Adds shape alternates for specific urls diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Orchard.DesignerTools.csproj b/src/Orchard.Web/Modules/Orchard.DesignerTools/Orchard.DesignerTools.csproj index 0b5eb5482..14cbcc6ca 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Orchard.DesignerTools.csproj +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Orchard.DesignerTools.csproj @@ -83,6 +83,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/UrlAlternatesFactory.cs b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/UrlAlternatesFactory.cs new file mode 100644 index 000000000..edd967eaa --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/UrlAlternatesFactory.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Orchard.DisplayManagement.Implementation; +using Orchard.Environment.Extensions; +using Orchard.Mvc; + +namespace Orchard.DesignerTools.Services { + [OrchardFeature("UrlAlternates")] + public class UrlAlternatesFactory : ShapeDisplayEvents { + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly List _urlAlternates; + + public UrlAlternatesFactory(IHttpContextAccessor httpContextAccessor) { + _httpContextAccessor = httpContextAccessor; + + var request = _httpContextAccessor.Current().Request; + + // extract each segment of the url + var urlSegments = VirtualPathUtility.ToAppRelative(request.Path.ToLower()) + .Split('/') + .Skip(1) // ignore the heading ~ segment + .Select(url => url.Replace("-", "__").Replace(".", "_")) // format the alternate + .ToArray(); + + if ( String.IsNullOrWhiteSpace(urlSegments[0]) ) { + urlSegments[0] = "homepage"; + } + + _urlAlternates = Enumerable.Range(1, urlSegments.Count()).Select(range => String.Join("__", urlSegments.Take(range))).ToList(); + } + + public override void Displaying(ShapeDisplayingContext context) { + + context.ShapeMetadata.OnDisplaying(displayedContext => { + // appends Url alternates to current ones + displayedContext.ShapeMetadata.Alternates = displayedContext.ShapeMetadata.Alternates.SelectMany( + alternate => new [] { alternate }.Union(_urlAlternates.Select(a => alternate + "__url__" + a)) + ).ToList(); + + // appends [ShapeType__url__[Url] alternates + displayedContext.ShapeMetadata.Alternates = _urlAlternates.Select(url => displayedContext.ShapeMetadata.Type + "__url__" + url) + .Union(displayedContext.ShapeMetadata.Alternates) + .ToList(); + }); + + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Shapes.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Shapes.cs index 0e983b9e6..883fe758a 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Shapes.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Shapes.cs @@ -25,8 +25,12 @@ namespace Orchard.Widgets { widget.Classes.Add("widget-" + contentItem.ContentType.HtmlClassify()); var zoneName = contentItem.As().Zone; - displaying.ShapeMetadata.Alternates.Add("Widget__" + contentItem.ContentType); + + // Widget__[ZoneName] e.g. Widget-SideBar displaying.ShapeMetadata.Alternates.Add("Widget__" + zoneName); + + // Widget__[ContentType] e.g. Widget-BlogArchive + displaying.ShapeMetadata.Alternates.Add("Widget__" + contentItem.ContentType); } }); } diff --git a/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs b/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs index 849a12dd1..331b6f354 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentFieldDriver.cs @@ -44,7 +44,6 @@ namespace Orchard.ContentManagement.Drivers { return contentFieldInfo; } - protected virtual DriverResult Display(ContentPart part, TField field, string displayType, dynamic shapeHelper) { return null; } protected virtual DriverResult Editor(ContentPart part, TField field, dynamic shapeHelper) { return null; } protected virtual DriverResult Editor(ContentPart part, TField field, IUpdateModel updater, dynamic shapeHelper) { return null; } @@ -69,10 +68,8 @@ namespace Orchard.ContentManagement.Drivers { return new ContentShapeResult(shapeType, Prefix, ctx => AddAlternates(shapeBuilder(ctx), differentiator)).Differentiator(differentiator); } - private object AddAlternates(dynamic shape, string differentiator) { + private static object AddAlternates(dynamic shape, string differentiator) { // automatically add shape alternates for shapes added by fields - // [ShapeType__PartName__FieldName] for ShapeType-PartName-FieldName.cshtml templates - // for fields on dynamic parts the part name is the same as the content type name ShapeMetadata metadata = shape.Metadata; @@ -84,8 +81,6 @@ namespace Orchard.ContentManagement.Drivers { var displayType = metadata.DisplayType ?? String.Empty; var dynamicType = string.Equals(partName, contentType, StringComparison.Ordinal); - var url = ""; - // [ShapeType__FieldName] e.g. Fields/Common.Text-Teaser if ( !string.IsNullOrEmpty(fieldName) ) metadata.Alternates.Add(shapeType + "__" + fieldName); @@ -119,12 +114,7 @@ namespace Orchard.ContentManagement.Drivers { if ( !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) && !dynamicType ) { metadata.Alternates.Add(shapeType + "__" + contentType + "__" + partName ); } - - // [ShapeType]__[ContentType]__[PartName]__[FieldName]__url__[Url] e.g. Fields/Common.Text-Blog-TeaserPart-Teaser-url-myBlog - if ( !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) && !dynamicType ) { - metadata.Alternates.Add(shapeType + "__" + contentType + "__" + partName + "__" + fieldName + "__url__" + url); - } - + // [ShapeType]_[DisplayType]__[PartName] e.g. Fields/Common.Text-TeaserPart.Summary if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(partName) ) { metadata.Alternates.Add(shapeType + "_" + displayType + "__" + partName); @@ -145,21 +135,10 @@ namespace Orchard.ContentManagement.Drivers { metadata.Alternates.Add(shapeType + "_" + displayType + "__" + contentType + "__" + partName + "__" + fieldName); } - // [ShapeType]_[DisplayType]__[PartName]__[FieldName]__url__[Url] e.g. Fields/Common.Text-TeaserPart-Teaser-url-myBlog.Summary - if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(partName) && !string.IsNullOrEmpty(fieldName) ) { - metadata.Alternates.Add(shapeType + "_" + displayType + "__" + partName + "__" + fieldName + "__url__" + url); - } - - // [ShapeType]_[DisplayType]__[ContentType]__[PartName]__[FieldName]__url__[Url] e.g. Fields/Common.Text-Blog-TeaserPart-Teaser-url-myBlog.Summary - if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(contentType) && !string.IsNullOrEmpty(partName) && !dynamicType && !string.IsNullOrEmpty(fieldName) ) { - metadata.Alternates.Add(shapeType + "_" + displayType + "__" + contentType + "__" + partName + "__" + fieldName + "__url__" + url); - } - - return shape; } - private object CreateShape(BuildShapeContext context, string shapeType) { + private static object CreateShape(BuildShapeContext context, string shapeType) { IShapeFactory shapeFactory = context.New; return shapeFactory.Create(shapeType); } diff --git a/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs b/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs index f4dbe1692..56f6e4442 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.MetaData; using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Shapes; namespace Orchard.ContentManagement.Drivers { public abstract class ContentPartDriver : IContentPartDriver where TContent : ContentPart, new() { @@ -41,10 +42,46 @@ namespace Orchard.ContentManagement.Drivers { } private ContentShapeResult ContentShapeImplementation(string shapeType, Func shapeBuilder) { - return new ContentShapeResult(shapeType, Prefix, shapeBuilder); + return new ContentShapeResult(shapeType, Prefix, ctx => AddAlternates(shapeBuilder(ctx))); } - private object CreateShape(BuildShapeContext context, string shapeType) { + private static object AddAlternates(dynamic shape) { + ShapeMetadata metadata = shape.Metadata; + ContentPart part = shape.ContentPart; + var id = part != null ? part.ContentItem.Id.ToString() : String.Empty; + var shapeType = metadata.Type; + var contentType = part != null ? part.ContentItem.ContentType : String.Empty; + var displayType = metadata.DisplayType ?? String.Empty; + + // [ShapeType]__[Id] e.g. Parts/Common.Metadata-42 + if ( !string.IsNullOrEmpty(id) ) { + metadata.Alternates.Add(shapeType + "__" + id); + } + + // [ShapeType]__[ContentType] e.g. Parts/Common.Metadata-BlogPost + if ( !string.IsNullOrEmpty(contentType) ) { + metadata.Alternates.Add(shapeType + "__" + contentType); + } + + // [ShapeType]_[DisplayType] e.g. Parts/Common.Metadata.Summary + if ( !string.IsNullOrEmpty(displayType) ) { + metadata.Alternates.Add(shapeType + "_" + displayType); + } + + // [ShapeType]_[DisplayType]__[ContentType] e.g. Parts/Common.Metadata-BlogPost.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(contentType) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + contentType); + } + + // [ShapeType]_[DisplayType]__[Id] e.g. Parts/Common.Metadata-42.Summary + if ( !string.IsNullOrEmpty(displayType) && !string.IsNullOrEmpty(id) ) { + metadata.Alternates.Add(shapeType + "_" + displayType + "__" + id); + } + + return shape; + } + + private static object CreateShape(BuildShapeContext context, string shapeType) { IShapeFactory shapeFactory = context.New; return shapeFactory.Create(shapeType); } diff --git a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs index 9cb19df5e..988066360 100644 --- a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs +++ b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs @@ -68,9 +68,12 @@ namespace Orchard.DisplayManagement.Implementation { shapeBinding.ShapeDescriptor.Displaying.Invoke(action => action(displayingContext), Logger); } + // invoking ShapeMetadata displaying events + shapeMetadata.Displaying.Invoke(action => action(displayingContext), Logger); + // now find the actual binding to render, taking alternates into account ShapeBinding actualBinding; - if (TryGetDescriptorBinding(shapeMetadata.Type, shapeMetadata.Alternates, shapeTable, out actualBinding)) { + if (TryGetDescriptorBinding(shapeMetadata.Type, shapeMetadata.Alternates, shapeTable, out actualBinding) ) { shape.Metadata.ChildContent = Process(actualBinding, shape, context); } else { @@ -108,6 +111,9 @@ namespace Orchard.DisplayManagement.Implementation { }, Logger); } + // invoking ShapeMetadata displayed events + shapeMetadata.Displayed.Invoke(action => action(displayedContext), Logger); + return shape.Metadata.ChildContent; } diff --git a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs index 7ee11a62e..994c0a671 100644 --- a/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs +++ b/src/Orchard/DisplayManagement/Shapes/ShapeMetadata.cs @@ -1,11 +1,16 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Web; +using Orchard.DisplayManagement.Implementation; namespace Orchard.DisplayManagement.Shapes { public class ShapeMetadata { public ShapeMetadata() { Wrappers = new List(); Alternates = new List(); + Displaying = Enumerable.Empty>(); + Displayed = Enumerable.Empty>(); } public string Type { get; set; } @@ -17,5 +22,18 @@ namespace Orchard.DisplayManagement.Shapes { public bool WasExecuted { get; set; } public IHtmlString ChildContent { get; set; } + + public IEnumerable> Displaying { get; private set; } + public IEnumerable> Displayed { get; private set; } + + public void OnDisplaying(Action action) { + var existing = Displaying ?? Enumerable.Empty>(); + Displaying = existing.Concat(new[] { action }); + } + + public void OnDisplayed(Action action) { + var existing = Displayed ?? Enumerable.Empty>(); + Displayed = existing.Concat(new[] { action }); + } } } \ No newline at end of file diff --git a/src/Orchard/Environment/WorkContextModule.cs b/src/Orchard/Environment/WorkContextModule.cs index 49729cda2..ad4a21c88 100644 --- a/src/Orchard/Environment/WorkContextModule.cs +++ b/src/Orchard/Environment/WorkContextModule.cs @@ -1,5 +1,13 @@ -using System.Web; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Web; using Autofac; +using Autofac.Builder; +using Autofac.Core; +using Autofac.Features.Metadata; +using Module = Autofac.Module; namespace Orchard.Environment { public class WorkContextModule : Module { @@ -19,6 +27,96 @@ namespace Orchard.Environment { builder.Register(ctx => ctx.Resolve>().Value) .As() .InstancePerDependency(); + + builder.RegisterGeneric(typeof(WorkValues<>)) + .InstancePerMatchingLifetimeScope("work"); + + builder.RegisterSource(new WorkRegistrationSource()); + } + } + + public class Work where T : class { + private readonly Func, T> _resolve; + + public Work(Func, T> resolve) { + _resolve = resolve; + } + + public T Value { + get { return _resolve(this); } + } + } + + + class WorkValues where T : class { + public WorkValues(IComponentContext componentContext) { + ComponentContext = componentContext; + Values = new Dictionary, T>(); + } + + public IComponentContext ComponentContext { get; private set; } + public IDictionary, T> Values { get; private set; } + } + + /// + /// Support the + /// types automatically whenever type T is registered with the container. + /// Metadata values come from the component registration's metadata. + /// + class WorkRegistrationSource : IRegistrationSource { + static readonly MethodInfo CreateMetaRegistrationMethod = typeof(WorkRegistrationSource).GetMethod( + "CreateMetaRegistration", BindingFlags.Static | BindingFlags.NonPublic); + + private static bool IsClosingTypeOf(Type type, Type openGenericType) { + return type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType; + } + + public IEnumerable RegistrationsFor(Service service, Func> registrationAccessor) { + var swt = service as IServiceWithType; + if (swt == null || !IsClosingTypeOf(swt.ServiceType, typeof(Work<>))) + return Enumerable.Empty(); + + var valueType = swt.ServiceType.GetGenericArguments()[0]; + + var valueService = swt.ChangeType(valueType); + + var registrationCreator = CreateMetaRegistrationMethod.MakeGenericMethod(valueType); + + return registrationAccessor(valueService) + .Select(v => registrationCreator.Invoke(null, new object[] { service, v })) + .Cast(); + } + + public bool IsAdapterForIndividualComponents { + get { return true; } + } + + static IComponentRegistration CreateMetaRegistration(Service providedService, IComponentRegistration valueRegistration) where T : class { + var rb = RegistrationBuilder.ForDelegate( + (c, p) => { + var workContextAccessor = c.Resolve(); + return new Work(w => { + var workContext = workContextAccessor.GetContext(); + if (workContext == null) + return default(T); + + var workValues = workContext.Resolve>(); + + T value; + if (!workValues.Values.TryGetValue(w, out value)) { + value = (T)workValues.ComponentContext.Resolve(valueRegistration, p); + workValues.Values[w] = value; + } + else { + int x = 5; + } + return value; + }); + }) + .As(providedService) + .Targeting(valueRegistration); + + return rb.CreateRegistration(); } } } From c26cc0973e60ce2f351e9118d73e4837171fbf63 Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Mon, 21 Feb 2011 17:48:28 -0800 Subject: [PATCH 4/5] Fixing comment in a Blog view --HG-- branch : dev --- .../Orchard.Blogs/Views/BlogAdmin/Item.cshtml | 2 +- .../Implementation/DefaultDisplayManager.cs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml b/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml index 689c95f24..f56e8dc0c 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Views/BlogAdmin/Item.cshtml @@ -1,5 +1,5 @@ @{ Html.AddTitleParts(T("Manage Blog").ToString()); } - // Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type + @* Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type *@ @Display(Model) diff --git a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs index 988066360..ef30ce852 100644 --- a/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs +++ b/src/Orchard/DisplayManagement/Implementation/DefaultDisplayManager.cs @@ -71,13 +71,19 @@ namespace Orchard.DisplayManagement.Implementation { // invoking ShapeMetadata displaying events shapeMetadata.Displaying.Invoke(action => action(displayingContext), Logger); - // now find the actual binding to render, taking alternates into account - ShapeBinding actualBinding; - if (TryGetDescriptorBinding(shapeMetadata.Type, shapeMetadata.Alternates, shapeTable, out actualBinding) ) { - shape.Metadata.ChildContent = Process(actualBinding, shape, context); + // use pre-fectched content if available (e.g. coming from specific cache implmentation) + if ( displayingContext.ChildContent != null ) { + shape.Metadata.ChildContent = displayingContext.ChildContent; } else { - throw new OrchardException(T("Shape type {0} not found", shapeMetadata.Type)); + // now find the actual binding to render, taking alternates into account + ShapeBinding actualBinding; + if ( TryGetDescriptorBinding(shapeMetadata.Type, shapeMetadata.Alternates, shapeTable, out actualBinding) ) { + shape.Metadata.ChildContent = Process(actualBinding, shape, context); + } + else { + throw new OrchardException(T("Shape type {0} not found", shapeMetadata.Type)); + } } foreach (var frameType in shape.Metadata.Wrappers) { From f99e8ab793cb1934ba6d0b6978180970c0ea65bd Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Mon, 21 Feb 2011 17:49:34 -0800 Subject: [PATCH 5/5] Adding ability to override the content of a shape - Using ShapeDispayingContext.ChildContent to force the HmlString to render --HG-- branch : dev --- .../DefaultDisplayManagerTests.cs | 29 +++++++++++++++++++ .../Core/Contents/Views/Admin/Create.cshtml | 2 +- .../Implementation/IShapeDisplayEvents.cs | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs b/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs index e05bec7c3..c3d2099ba 100644 --- a/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs +++ b/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs @@ -145,6 +145,35 @@ namespace Orchard.Tests.DisplayManagement { Assert.That(result.ToString(), Is.EqualTo("Hi there!")); } + [Test] + public void RenderPreCalculatedShape() { + var displayManager = _container.Resolve(); + + var shape = new Shape { + Metadata = new ShapeMetadata { + Type = "Foo" + } + }; + + shape.Metadata.OnDisplaying( + context => { + context.ChildContent = new HtmlString("Bar"); + }); + + var descriptor = new ShapeDescriptor { + ShapeType = "Foo", + }; + descriptor.Bindings["Foo"] = new ShapeBinding { + BindingName = "Foo", + Binding = ctx => new HtmlString("Hi there!"), + }; + + AddShapeDescriptor(descriptor); + + var result = displayManager.Execute(CreateDisplayContext(shape)); + Assert.That(result.ToString(), Is.EqualTo("Bar")); + } + [Test] public void RenderFallbackShape() { var displayManager = _container.Resolve(); diff --git a/src/Orchard.Web/Core/Contents/Views/Admin/Create.cshtml b/src/Orchard.Web/Core/Contents/Views/Admin/Create.cshtml index 086ac38b1..471b54362 100644 --- a/src/Orchard.Web/Core/Contents/Views/Admin/Create.cshtml +++ b/src/Orchard.Web/Core/Contents/Views/Admin/Create.cshtml @@ -7,6 +7,6 @@ @using (Html.BeginFormAntiForgeryPost()) { @Html.ValidationSummary() -// Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type + // Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type @Display(Model) } \ No newline at end of file diff --git a/src/Orchard/DisplayManagement/Implementation/IShapeDisplayEvents.cs b/src/Orchard/DisplayManagement/Implementation/IShapeDisplayEvents.cs index 063a3f99a..6f1c678fb 100644 --- a/src/Orchard/DisplayManagement/Implementation/IShapeDisplayEvents.cs +++ b/src/Orchard/DisplayManagement/Implementation/IShapeDisplayEvents.cs @@ -10,6 +10,7 @@ namespace Orchard.DisplayManagement.Implementation { public class ShapeDisplayingContext { public dynamic Shape { get; set; } public ShapeMetadata ShapeMetadata { get; set; } + public IHtmlString ChildContent { get; set; } } public class ShapeDisplayedContext {