diff --git a/src/Orchard.Tests/DisplayManagement/Descriptors/BasicShapeTemplateHarvesterTests.cs b/src/Orchard.Tests/DisplayManagement/Descriptors/BasicShapeTemplateHarvesterTests.cs
index 4167c65d3..fc8b6770b 100644
--- a/src/Orchard.Tests/DisplayManagement/Descriptors/BasicShapeTemplateHarvesterTests.cs
+++ b/src/Orchard.Tests/DisplayManagement/Descriptors/BasicShapeTemplateHarvesterTests.cs
@@ -54,7 +54,6 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
VerifyShapeType("Views/Items", "Content.Edit", "Items_Content_Edit");
}
-
[Test]
public void ExplicitSpecializationMixedWithDisplayTypes() {
VerifyShapeType("Views/Items", "Content-MyType", "Items_Content__MyType");
@@ -62,12 +61,18 @@ namespace Orchard.Tests.DisplayManagement.Descriptors {
VerifyShapeType("Views/Items", "Content-MyType.Edit", "Items_Content_Edit__MyType");
}
-
[Test]
public void ImplicitSpecializationMixedWithDisplayTypes() {
VerifyShapeType("Views/Items", "MyType", "Items_Content__MyType");
VerifyShapeType("Views/Items", "MyType.Summary", "Items_Content_Summary__MyType");
VerifyShapeType("Views/Items", "MyType.Edit", "Items_Content_Edit__MyType");
}
+
+ [Test]
+ public void MultipleDotsAreNormalizedToUnderscore() {
+ VerifyShapeType("Views/Parts", "Common.Body", "Parts_Common_Body");
+ VerifyShapeType("Views/Parts", "Common.Body.Summary", "Parts_Common_Body_Summary");
+ VerifyShapeType("Views/Parts", "Localization.ContentTranslations.Summary", "Parts_Localization_ContentTranslations_Summary");
+ }
}
}
diff --git a/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs b/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs
index a9020d471..7c8d20e7f 100644
--- a/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs
+++ b/src/Orchard.Web/Core/Common/Drivers/BodyPartDriver.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using System.Web;
using JetBrains.Annotations;
@@ -36,33 +37,35 @@ namespace Orchard.Core.Common.Drivers {
protected override DriverResult Display(BodyPart part, string displayType, dynamic shapeHelper) {
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));
- })
- );
+ ContentShape("Parts_Common_Body",
+ () => {
+ 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",
+ () => {
+ var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text));
+ return shapeHelper.Parts_Common_Body_Summary(ContentPart: part, Html: new HtmlString(bodyText));
+ })
+ );
+ }
+
+ private string IfThen(bool predicate, string value) {
+ return predicate ? value : null;
}
protected override DriverResult Editor(BodyPart part, dynamic shapeHelper) {
var model = BuildEditorViewModel(part);
- var location = part.GetLocation("Editor");
- return ContentPartTemplate(model, TemplateName, Prefix).Location(location);
+ return ContentShape("Parts_Common_Body_Editor",
+ () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
protected override DriverResult Editor(BodyPart part, IUpdateModel updater, dynamic shapeHelper) {
var model = BuildEditorViewModel(part);
updater.TryUpdateModel(model, Prefix, null, null);
- // only set the format if it has not yet been set to preserve the initial format type - might want to change this later to support changing body formats but...later
- if (string.IsNullOrWhiteSpace(model.Format))
- model.Format = GetFlavor(part);
-
- var location = part.GetLocation("Editor");
- return ContentPartTemplate(model, TemplateName, Prefix).Location(location);
+ return ContentShape("Parts_Common_Body_Editor",
+ () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
}
private static BodyEditorViewModel BuildEditorViewModel(BodyPart part) {
diff --git a/src/Orchard.Web/Core/Common/Placement.info b/src/Orchard.Web/Core/Common/Placement.info
new file mode 100644
index 000000000..ce389cc31
--- /dev/null
+++ b/src/Orchard.Web/Core/Common/Placement.info
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Orchard.Web/Core/Common/Views/Parts/Common.Body.Summary.cshtml b/src/Orchard.Web/Core/Common/Views/Parts/Common.Body.Summary.cshtml
index c01416962..374c67cfc 100644
--- a/src/Orchard.Web/Core/Common/Views/Parts/Common.Body.Summary.cshtml
+++ b/src/Orchard.Web/Core/Common/Views/Parts/Common.Body.Summary.cshtml
@@ -1,9 +1,9 @@
-@model BodyDisplayViewModel
-@using Orchard.Core.Common.ViewModels;
-@*doing excerpt generation on the way out for now so we don't stick ourselves with needing to regen excerpts for existing data
+@*doing excerpt generation on the way out for now so we don't stick ourselves with needing to regen excerpts for existing data
also, doing this here, inline, until we have a pluggable processing model (both in and out)
also, ...this is ugly *@
@{
- var body = new HtmlString(Html.Excerpt(Model.Html.ToString(), 200).ToString().Replace(Environment.NewLine, "
" + Environment.NewLine + ""));
+ Orchard.ContentManagement.ContentItem contentItem = Model.ContentPart.ContentItem;
+ string bodyHtml = Model.Html.ToString();
+ var body = new HtmlString(Html.Excerpt(bodyHtml, 200).ToString().Replace(Environment.NewLine, "
" + Environment.NewLine + ""));
}
-
@body @Html.ItemDisplayLink(T("[more]").ToString(), Model.BodyPart.ContentItem)
+@body @Html.ItemDisplayLink(T("[more]").ToString(), contentItem)
diff --git a/src/Orchard.Web/Core/Contents/Shapes.cs b/src/Orchard.Web/Core/Contents/Shapes.cs
index fddd989a0..b492501ea 100644
--- a/src/Orchard.Web/Core/Contents/Shapes.cs
+++ b/src/Orchard.Web/Core/Contents/Shapes.cs
@@ -8,7 +8,6 @@ namespace Orchard.Core.Contents {
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) {
@@ -16,12 +15,14 @@ namespace Orchard.Core.Contents {
displaying.ShapeMetadata.Alternates.Add("Items_Content__" + contentItem.Id);
}
});
- }
- private static object ContentZone(ShapeCreatingContext creating, string name) {
- var zone = creating.New.ContentZone();
- zone.ZoneName = name;
- return zone;
+ builder.Describe("Items_Content_Editor")
+ .OnDisplaying(displaying => {
+ ContentItem contentItem = displaying.Shape.ContentItem;
+ if (contentItem != null) {
+ displaying.ShapeMetadata.Alternates.Add("Items_Content_Editor__" + contentItem.ContentType);
+ }
+ });
}
}
}
diff --git a/src/Orchard.Web/Core/Contents/Views/Items/Content.Edit.cshtml b/src/Orchard.Web/Core/Contents/Views/Items/Content.Editor.cshtml
similarity index 100%
rename from src/Orchard.Web/Core/Contents/Views/Items/Content.Edit.cshtml
rename to src/Orchard.Web/Core/Contents/Views/Items/Content.Editor.cshtml
diff --git a/src/Orchard.Web/Core/Orchard.Core.csproj b/src/Orchard.Web/Core/Orchard.Core.csproj
index 2beb761d3..ae26cebbb 100644
--- a/src/Orchard.Web/Core/Orchard.Core.csproj
+++ b/src/Orchard.Web/Core/Orchard.Core.csproj
@@ -381,7 +381,7 @@
-
+
@@ -392,6 +392,7 @@
+
diff --git a/src/Orchard.Web/Core/Shapes/CoreShapes.cs b/src/Orchard.Web/Core/Shapes/CoreShapes.cs
index fefbaec10..d31a001e2 100644
--- a/src/Orchard.Web/Core/Shapes/CoreShapes.cs
+++ b/src/Orchard.Web/Core/Shapes/CoreShapes.cs
@@ -31,7 +31,7 @@ namespace Orchard.Core.Shapes {
// and has an automatic zone creating behavior
builder.Describe("Layout")
.Configure(descriptor => descriptor.Wrappers.Add("Document"))
- .OnCreating(creating => creating.Behaviors.Add(new ZoneHoldingBehavior(name => CreateZone(creating))))
+ .OnCreating(creating => creating.Behaviors.Add(new ZoneHoldingBehavior(() => creating.New.Zone())))
.OnCreated(created => {
var layout = created.Shape;
layout.Head = created.New.DocumentZone(ZoneName: "Head");
@@ -81,9 +81,6 @@ namespace Orchard.Core.Shapes {
});
}
- static object CreateZone(ShapeCreatingContext context) {
- return context.New.Zone();
- }
static TagBuilder GetTagBuilder(string tagName, string id, IEnumerable classes, IDictionary attributes) {
var tagBuilder = new TagBuilder(tagName);
@@ -271,8 +268,8 @@ namespace Orchard.Core.Shapes {
}
[Shape]
- public void Partial(HtmlHelper Html, TextWriter Output, string TemplateName, object Model) {
- RenderInternal(Html, Output, TemplateName, Model, null);
+ public void Partial(HtmlHelper Html, TextWriter Output, string TemplateName, object Model, string Prefix) {
+ RenderInternal(Html, Output, TemplateName, Model, Prefix);
}
[Shape]
diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Drivers/TagsPartDriver.cs b/src/Orchard.Web/Modules/Orchard.Tags/Drivers/TagsPartDriver.cs
index bfc32bfd3..2b63945e2 100644
--- a/src/Orchard.Web/Modules/Orchard.Tags/Drivers/TagsPartDriver.cs
+++ b/src/Orchard.Web/Modules/Orchard.Tags/Drivers/TagsPartDriver.cs
@@ -24,11 +24,8 @@ namespace Orchard.Tags.Drivers {
public virtual IUser CurrentUser { get; set; }
protected override DriverResult Display(TagsPart part, string displayType, dynamic shapeHelper) {
- var showTags = shapeHelper.Parts_Tags_ShowTags(ContentPart: part, Tags: part.CurrentTags);
- if (!string.IsNullOrWhiteSpace(displayType))
- showTags.Metadata.Type = string.Format("{0}.{1}", showTags.Metadata.Type, displayType);
- var location = part.GetLocation(displayType);
- return ContentShape(showTags).Location(location);
+ return ContentShape("Parts_Tags_ShowTags",
+ () => shapeHelper.Parts_Tags_ShowTags(ContentPart: part, Tags: part.CurrentTags));
}
protected override DriverResult Editor(TagsPart part, dynamic shapeHelper) {
diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj b/src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj
index 5b2217ea1..819b7050b 100644
--- a/src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj
+++ b/src/Orchard.Web/Modules/Orchard.Tags/Orchard.Tags.csproj
@@ -112,6 +112,9 @@
Orchard.Core
+
+
+
-
-
-
diff --git a/src/Orchard/ContentManagement/DefaultContentDisplay.cs b/src/Orchard/ContentManagement/DefaultContentDisplay.cs
index a90f29add..589fe36c2 100644
--- a/src/Orchard/ContentManagement/DefaultContentDisplay.cs
+++ b/src/Orchard/ContentManagement/DefaultContentDisplay.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Routing;
+using ClaySharp;
+using ClaySharp.Implementation;
using Microsoft.CSharp.RuntimeBinder;
using Orchard.ContentManagement.Handlers;
using Orchard.DisplayManagement;
@@ -10,11 +12,13 @@ using Orchard.DisplayManagement.Descriptors;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Themes;
+using Orchard.UI.Zones;
namespace Orchard.ContentManagement {
public class DefaultContentDisplay : IContentDisplay {
private readonly Lazy> _handlers;
private readonly IShapeHelperFactory _shapeHelperFactory;
+ private readonly IShapeFactory _shapeFactory;
private readonly IShapeTableManager _shapeTableManager;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IHttpContextAccessor _httpContextAccessor;
@@ -24,6 +28,7 @@ namespace Orchard.ContentManagement {
public DefaultContentDisplay(
Lazy> handlers,
IShapeHelperFactory shapeHelperFactory,
+ IShapeFactory shapeFactory,
IShapeTableManager shapeTableManager,
IWorkContextAccessor workContextAccessor,
IHttpContextAccessor httpContextAccessor,
@@ -31,6 +36,7 @@ namespace Orchard.ContentManagement {
RequestContext requestContext) {
_handlers = handlers;
_shapeHelperFactory = shapeHelperFactory;
+ _shapeFactory = shapeFactory;
_shapeTableManager = shapeTableManager;
_workContextAccessor = workContextAccessor;
_httpContextAccessor = httpContextAccessor;
@@ -55,29 +61,30 @@ namespace Orchard.ContentManagement {
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);
+ var actualShapeType = "Items_" + stereotype;
+ var actualDisplayType = string.IsNullOrWhiteSpace(displayType) ? "Detail" : displayType;
+ dynamic itemShape = CreateItemShape(actualShapeType);
itemShape.ContentItem = content.ContentItem;
- itemShape.Metadata.DisplayType = shapeDisplayType;
+ itemShape.Metadata.DisplayType = actualDisplayType;
- var context = new BuildDisplayContext(itemShape, content, shapeDisplayType, _shapeHelperFactory);
- BindPlacement(context, displayType);
+ var context = new BuildDisplayContext(itemShape, content, actualDisplayType, _shapeHelperFactory);
+ BindPlacement(context, actualDisplayType);
_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();
+ var contentTypeDefinition = content.ContentItem.TypeDefinition;
+ string stereotype;
+ if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype))
+ stereotype = "Content";
- IContent iContent = content;
- if (iContent != null)
- itemShape.ContentItem = iContent.ContentItem;
+ var actualShapeType = "Items_" + stereotype + "_Editor";
+
+ dynamic itemShape = CreateItemShape(actualShapeType);
+ itemShape.ContentItem = content.ContentItem;
var context = new BuildEditorContext(itemShape, content, _shapeHelperFactory);
BindPlacement(context, null);
@@ -87,12 +94,15 @@ namespace Orchard.ContentManagement {
}
public dynamic UpdateEditor(IContent content, IUpdateModel updater) {
- var shapeHelper = _shapeHelperFactory.CreateHelper();
- var itemShape = shapeHelper.Items_Content_Edit();
+ var contentTypeDefinition = content.ContentItem.TypeDefinition;
+ string stereotype;
+ if (!contentTypeDefinition.Settings.TryGetValue("Stereotype", out stereotype))
+ stereotype = "Content";
- IContent iContent = content;
- if (iContent != null)
- itemShape.ContentItem = iContent.ContentItem;
+ var actualShapeType = "Items_" + stereotype + "_Editor";
+
+ dynamic itemShape = CreateItemShape(actualShapeType);
+ itemShape.ContentItem = content.ContentItem;
var context = new UpdateEditorContext(itemShape, content, updater, _shapeHelperFactory);
BindPlacement(context, null);
@@ -101,6 +111,11 @@ namespace Orchard.ContentManagement {
return context.Shape;
}
+ private dynamic CreateItemShape(string actualShapeType) {
+ var zoneHoldingBehavior = new ZoneHoldingBehavior(() => _shapeFactory.Create("ContentZone", Arguments.Empty()));
+ return _shapeFactory.Create(actualShapeType, Arguments.Empty(), new[] { zoneHoldingBehavior });
+ }
+
private void BindPlacement(BuildShapeContext context, string displayType) {
context.FindPlacement = (partShapeType, defaultLocation) => {
//var workContext = _workContextAccessor.GetContext();
@@ -109,9 +124,9 @@ namespace Orchard.ContentManagement {
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 placementContext = new ShapePlacementContext {
+ ContentType = context.ContentItem.ContentType,
+ DisplayType = displayType
};
var location = descriptor.Placement(placementContext);
return location ?? defaultLocation;
diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs
index 1c7a64109..592b5b561 100644
--- a/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs
+++ b/src/Orchard/DisplayManagement/Descriptors/ShapeDescriptor.cs
@@ -24,7 +24,8 @@ namespace Orchard.DisplayManagement.Descriptors {
///
public string BindingSource {
get {
- return Bindings[ShapeType].BindingSource;
+ ShapeBinding binding;
+ return Bindings.TryGetValue(ShapeType, out binding) ? binding.BindingSource : null;
}
}
diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs
index f77518c78..5461c6d6b 100644
--- a/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs
+++ b/src/Orchard/DisplayManagement/Descriptors/ShapePlacementStrategy/ShapePlacementParsingStrategy.cs
@@ -27,11 +27,11 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
public void Discover(ShapeTableBuilder builder) {
var availableFeatures = _extensionManager.AvailableFeatures();
- var activeFeatures = availableFeatures.Where(fd => FeatureIsTheme(fd) || FeatureIsEnabled(fd));
+ 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)) {
+ foreach (var featureDescriptor in extensionDescriptor.Features.Where(fd => fd.Name == fd.Extension.Name)) {
ProcessFeatureDescriptor(builder, featureDescriptor);
}
}
@@ -46,7 +46,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
}
private void ProcessPlacementFile(ShapeTableBuilder builder, FeatureDescriptor featureDescriptor, PlacementFile placementFile) {
- var feature = new Feature {Descriptor = featureDescriptor};
+ var feature = new Feature { Descriptor = featureDescriptor };
// invert the tree into a list of leaves and the stack
var entries = DrillDownShapeLocations(placementFile.Nodes, Enumerable.Empty());
@@ -55,7 +55,9 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
var matches = entry.Item2;
Func predicate = ctx => true;
- predicate = matches.SelectMany(match=>match.Terms).Aggregate(predicate, BuildPredicate);
+ if (matches.Any()) {
+ predicate = matches.SelectMany(match => match.Terms).Aggregate(predicate, BuildPredicate);
+ }
builder.Describe(shapeLocation.ShapeType)
.From(feature)
@@ -65,31 +67,39 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
private Func BuildPredicate(Func predicate, KeyValuePair term) {
var expression = term.Value;
- switch(term.Key) {
+ switch (term.Key) {
case "ContentType":
- return ctx=>ctx.ContentType == expression ? true : predicate(ctx);
+ if (expression.EndsWith("*")) {
+ var prefix = expression.Substring(0, expression.Length - 1);
+ return ctx => (ctx.ContentType ?? "").StartsWith(prefix) && predicate(ctx);
+ }
+ return ctx => (ctx.ContentType == expression) && predicate(ctx);
case "DisplayType":
- return ctx=>ctx.DisplayType == expression ? true : predicate(ctx);
+ if (expression.EndsWith("*")) {
+ var prefix = expression.Substring(0, expression.Length - 1);
+ return ctx => (ctx.DisplayType ?? "").StartsWith(prefix) && predicate(ctx);
+ }
+ return ctx => (ctx.DisplayType == expression) && predicate(ctx);
}
return predicate;
}
private static IEnumerable>> DrillDownShapeLocations(
- IEnumerable nodes,
+ IEnumerable nodes,
IEnumerable path) {
-
+
// return shape locations nodes in this place
foreach (var placementShapeLocation in nodes.OfType()) {
yield return new Tuple>(placementShapeLocation, path);
}
// recurse down into match nodes
foreach (var placementMatch in nodes.OfType()) {
- foreach (var findShapeLocation in DrillDownShapeLocations(placementMatch.Nodes, path.Concat(new[] {placementMatch}))) {
+ foreach (var findShapeLocation in DrillDownShapeLocations(placementMatch.Nodes, path.Concat(new[] { placementMatch }))) {
yield return findShapeLocation;
}
}
- }
+ }
private bool FeatureIsTheme(FeatureDescriptor fd) {
return fd.Extension.ExtensionType == "Theme";
@@ -98,7 +108,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapePlacementStrategy {
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/DisplayManagement/Descriptors/ShapeTemplateStrategy/IShapeTemplateHarvester.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/IShapeTemplateHarvester.cs
index 5d3ab8e00..40e573555 100644
--- a/src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/IShapeTemplateHarvester.cs
+++ b/src/Orchard/DisplayManagement/Descriptors/ShapeTemplateStrategy/IShapeTemplateHarvester.cs
@@ -26,7 +26,7 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
}
public IEnumerable HarvestShape(HarvestShapeInfo info) {
- var lastDot = info.FileName.IndexOf('.');
+ var lastDot = info.FileName.LastIndexOf('.');
if (lastDot <= 0) {
yield return new HarvestShapeHit {
ShapeType = Adjust(info.SubPath, info.FileName, null)
diff --git a/src/Orchard/DisplayManagement/IShapeFactory.cs b/src/Orchard/DisplayManagement/IShapeFactory.cs
index 3de5e3531..d0bd47aab 100644
--- a/src/Orchard/DisplayManagement/IShapeFactory.cs
+++ b/src/Orchard/DisplayManagement/IShapeFactory.cs
@@ -1,4 +1,5 @@
-using ClaySharp;
+using System.Collections.Generic;
+using ClaySharp;
using ClaySharp.Implementation;
namespace Orchard.DisplayManagement {
@@ -8,6 +9,7 @@ namespace Orchard.DisplayManagement {
///
public interface IShapeFactory : IDependency {
IShape Create(string shapeType, INamedEnumerable