diff --git a/.hgsubstate b/.hgsubstate
index c26a8309e..bc1b63a79 100644
--- a/.hgsubstate
+++ b/.hgsubstate
@@ -1 +1 @@
-c6970f1f2d9c7a2bfdc0aeb43658dfcaf5688871 Clay
+57ebfff6441a6a246e81c1992996f06cd66c60ac Clay
diff --git a/src/Orchard.Web/Core/Dashboard/Controllers/AdminController.cs b/src/Orchard.Web/Core/Dashboard/Controllers/AdminController.cs
index 6c946e649..ccc4c6323 100644
--- a/src/Orchard.Web/Core/Dashboard/Controllers/AdminController.cs
+++ b/src/Orchard.Web/Core/Dashboard/Controllers/AdminController.cs
@@ -1,9 +1,34 @@
using System.Web.Mvc;
+using Orchard.DisplayManagement;
namespace Orchard.Core.Dashboard.Controllers {
public class AdminController : Controller {
+ private readonly IShapeHelperFactory _shapeHelperFactory;
+
+ public AdminController(IShapeHelperFactory shapeHelperFactory) {
+ _shapeHelperFactory = shapeHelperFactory;
+ }
+
public ActionResult Index() {
- return View();
+ var shape = _shapeHelperFactory.CreateHelper();
+ var list = shape.List();
+ var list2 = shape.List();
+
+ list.Id = "the-list";
+ list.Classes.Add("foo");
+ list.Attributes["onclick"] = "etc";
+ list.ItemClasses.Add("yarg");
+
+ list.Add("one");
+ list.Add("two");
+ list.Add(list2);
+ list.Add(shape.DumpShapeTable());
+ list.Add("four");
+
+ list2.Add("three a");
+ list2.Add("three b");
+
+ return View(list);
}
}
}
\ No newline at end of file
diff --git a/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.ascx b/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.ascx
index 4b5852427..f117526a3 100644
--- a/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.ascx
+++ b/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.ascx
@@ -4,3 +4,4 @@
<%: T("This is the place where you can manage your web site, its appearance and its contents. Please take a moment to explore the different menu items on the left of the screen to familiarize yourself with the features of the application. For example, try to change the theme through the “Manage Themes” menu entry. You can also create new pages and manage existing ones through the “Manage Pages” menu entry or create blogs through “Manage Blogs”.") %>
<%: T("Have fun!") %>
<%: T("The Orchard Team") %>
+<%:Display(Model) %>
diff --git a/src/Orchard.Web/Core/Shapes/CoreShapes.cs b/src/Orchard.Web/Core/Shapes/CoreShapes.cs
index 235a83f38..b4f47a180 100644
--- a/src/Orchard.Web/Core/Shapes/CoreShapes.cs
+++ b/src/Orchard.Web/Core/Shapes/CoreShapes.cs
@@ -1,4 +1,8 @@
-using System.Web;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using Orchard.DisplayManagement;
@@ -7,11 +11,13 @@ using Orchard.Environment.Extensions.Models;
using Orchard.UI;
using Orchard.UI.Zones;
+// ReSharper disable InconsistentNaming
+
namespace Orchard.Core.Shapes {
public class CoreShapes : IShapeDescriptorBindingStrategy {
public Feature Feature { get; set; }
- public void Discover(ShapeTableBuilder builder) {
+ public void Discover(ShapeTableBuilder builder) {
// the root page shape named 'Layout' is wrapped with 'Document'
// and has an automatic zone creating behavior
builder.Describe.Named("Layout").From(Feature.Descriptor)
@@ -21,6 +27,16 @@ namespace Orchard.Core.Shapes {
// 'Zone' shapes are built on the Zone base class
builder.Describe.Named("Zone").From(Feature.Descriptor)
.OnCreating(creating => creating.BaseType = typeof(Zone));
+
+ // 'List' shapes start with several empty collections
+ builder.Describe.Named("List").From(Feature.Descriptor)
+ .OnCreated(created => {
+ created.Shape.Tag = "ul";
+ created.Shape.Classes = new List();
+ created.Shape.Attributes = new Dictionary();
+ created.Shape.ItemClasses = new List();
+ created.Shape.ItemAttributes = new Dictionary();
+ });
}
static object DetermineModel(HtmlHelper Html, object Model) {
@@ -28,6 +44,53 @@ namespace Orchard.Core.Shapes {
return isNull ? Html.ViewData.Model : Model;
}
+ static TagBuilder GetTagBuilder(string tagName, string id, IEnumerable classes, IDictionary attributes) {
+ var tagBuilder = new TagBuilder(tagName);
+ tagBuilder.MergeAttributes(attributes, false);
+ foreach (var cssClass in classes ?? Enumerable.Empty())
+ tagBuilder.AddCssClass(cssClass);
+ if (id != null)
+ tagBuilder.GenerateId(id);
+ return tagBuilder;
+ }
+
+ [Shape]
+ public void List(
+ dynamic Display,
+ TextWriter Output,
+ IEnumerable Items,
+ string Tag,
+ string Id,
+ IEnumerable Classes,
+ IDictionary Attributes,
+ IEnumerable ItemClasses,
+ IDictionary ItemAttributes) {
+
+ var listTagName = string.IsNullOrEmpty(Tag) ? "ul" : Tag;
+ const string itemTagName = "li";
+
+ var listTag = GetTagBuilder(listTagName, Id, Classes, Attributes);
+ Output.Write(listTag.ToString(TagRenderMode.StartTag));
+
+ if (Items != null) {
+ var count = Items.Count();
+ var index = 0;
+ foreach (var item in Items) {
+ var itemTag = GetTagBuilder(itemTagName, null, ItemClasses, ItemAttributes);
+ if (index == 0)
+ itemTag.AddCssClass("first");
+ if (index == count - 1)
+ itemTag.AddCssClass("last");
+ Output.Write(itemTag.ToString(TagRenderMode.StartTag));
+ Output.Write(Display(item));
+ Output.Write(itemTag.ToString(TagRenderMode.EndTag));
+ ++index;
+ }
+ }
+ Output.Write(listTag.ToString(TagRenderMode.EndTag));
+ }
+
+
[Shape]
public IHtmlString Partial(HtmlHelper Html, string TemplateName, object Model) {
return Html.Partial(TemplateName, DetermineModel(Html, Model));
@@ -42,5 +105,6 @@ namespace Orchard.Core.Shapes {
public IHtmlString EditorTemplate(HtmlHelper Html, string TemplateName, object Model, string Prefix) {
return Html.Partial(TemplateName, DetermineModel(Html, Model));
}
+
}
}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj b/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj
index 1cb76f27a..24b88e2af 100644
--- a/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj
+++ b/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj
@@ -87,7 +87,6 @@
-
diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Shapes.cs b/src/Orchard.Web/Modules/Orchard.DevTools/Shapes.cs
deleted file mode 100644
index a0e6bc68a..000000000
--- a/src/Orchard.Web/Modules/Orchard.DevTools/Shapes.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.Web.Mvc;
-using Orchard.DisplayManagement;
-
-namespace Orchard.DevTools {
- public class Shapes : IDependency {
- [Shape]
- public IHtmlString Title(dynamic text) {
- return new HtmlString("" + text + "
");
- }
-
- [Shape]
- public IHtmlString Explosion(int? Height, int? Width) {
-
- return new HtmlString(string.Format("Boom {0}x{1}", Height, Width));
- }
-
- [Shape]
- public IHtmlString Page(dynamic Display, dynamic Shape) {
- return Display(Shape.Sidebar, Shape.Messages);
- }
-
- [Shape]
- public IHtmlString Zone(dynamic Display, dynamic Shape) {
- var tag = new TagBuilder("div");
- tag.GenerateId("zone-" + Shape.Name);
- tag.AddCssClass("zone-" + Shape.Name);
- tag.AddCssClass("zone");
-
- IEnumerable all = DisplayAll(Display, Shape);
- tag.InnerHtml = Combine(all.ToArray()).ToString();
-
- return new HtmlString(tag.ToString());
- }
-
- [Shape]
- public IHtmlString Message(dynamic Display, object Content, string Severity) {
- return Display(new HtmlString(""), Severity ?? "Neutral", ": ", Content, new HtmlString("
"));
- }
-
- static IHtmlString Combine(IEnumerable contents) {
- return new HtmlString(contents.Aggregate("", (a, b) => a + b));
- }
-
- static IEnumerable DisplayAll(dynamic Display, dynamic Shape) {
- foreach (var item in Shape) {
- yield return Display(item);
- }
- }
- }
-}
diff --git a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs
index 10863af45..7aed2aa37 100644
--- a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs
+++ b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs
@@ -19,6 +19,7 @@ using Orchard.DisplayManagement.Implementation;
using Orchard.DisplayManagement.Shapes;
using Orchard.Environment;
using Orchard.Environment.Extensions;
+using Orchard.Environment.Extensions.Models;
using Orchard.Localization;
using Orchard.Mvc;
using Orchard.Mvc.Filters;
@@ -37,6 +38,8 @@ using Orchard.UI.Zones;
namespace Orchard.Setup {
public class SetupMode : Module {
+ public Feature Feature { get; set; }
+
protected override void Load(ContainerBuilder builder) {
// standard services needed in setup mode
@@ -77,7 +80,7 @@ namespace Orchard.Setup {
builder.RegisterType().As();
builder.RegisterType().As();
- builder.RegisterType().As();
+ builder.RegisterType().As().WithProperty("Feature", Feature);
builder.RegisterType().As();
builder.RegisterType().As();
}
diff --git a/src/Orchard.sln b/src/Orchard.sln
index 15597547e..2f19b81bc 100644
--- a/src/Orchard.sln
+++ b/src/Orchard.sln
@@ -53,6 +53,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClaySharp", "..\Clay\src\Cl
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClaySharp.Tests", "..\Clay\src\ClaySharp.Tests\ClaySharp.Tests.csproj", "{10369238-A590-48BF-8D3E-E83EB6F0C931}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Notes", "_Notes", "{8A49DB66-40B2-4B6A-BFF0-D4839A240D00}"
+ ProjectSection(SolutionItems) = preProject
+ Shapes.txt = Shapes.txt
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CodeCoverage|Any CPU = CodeCoverage|Any CPU
diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs
index b45cb84d9..aa0b226a4 100644
--- a/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs
+++ b/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs
@@ -1,9 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
@@ -12,6 +14,7 @@ using Autofac.Core;
using ClaySharp.Implementation;
using Microsoft.CSharp.RuntimeBinder;
using Orchard.DisplayManagement.Implementation;
+using Orchard.DisplayManagement.Shapes;
namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy {
public class ShapeAttributeBindingStrategy : IShapeDescriptorBindingStrategy {
@@ -55,27 +58,30 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy {
private IHtmlString PerformInvoke(DisplayContext displayContext, MethodInfo methodInfo, object serviceInstance) {
+ var output = new StringWriter();
var arguments = methodInfo.GetParameters()
- .Select(parameter => BindParameter(displayContext, parameter));
+ .Select(parameter => BindParameter(displayContext, parameter, output));
- return CoerceHtmlString(methodInfo.Invoke(serviceInstance, arguments.ToArray()));
+ var returnValue = methodInfo.Invoke(serviceInstance, arguments.ToArray());
+ if (methodInfo.ReturnType != typeof(void)) {
+ output.Write(returnValue);
+ }
+ return CoerceHtmlString(output);
}
private static IHtmlString CoerceHtmlString(object invoke) {
return invoke as IHtmlString ?? (invoke != null ? new HtmlString(invoke.ToString()) : null);
}
- private object BindParameter(DisplayContext displayContext, ParameterInfo parameter) {
+ private object BindParameter(DisplayContext displayContext, ParameterInfo parameter, TextWriter output) {
if (parameter.Name == "Shape")
return displayContext.Value;
if (parameter.Name == "Display")
return displayContext.Display;
- if (parameter.Name == "Attributes") {
- var attributes = new RouteValueDictionary(((dynamic)(displayContext.Value))[parameter.Name]);
- return Arguments.From(attributes.Values, attributes.Keys);
- }
+ if (parameter.Name == "Output" && parameter.ParameterType == typeof(TextWriter))
+ return output;
// meh--
if (parameter.Name == "Html") {
@@ -85,11 +91,31 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy {
_routeCollection);
}
- var result = ((dynamic)(displayContext.Value))[parameter.Name];
+ var getter = _getters.GetOrAdd(parameter.Name, n =>
+ CallSite>.Create(
+ Microsoft.CSharp.RuntimeBinder.Binder.GetMember(
+ CSharpBinderFlags.None, n, null, new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) })));
+
+ var result = getter.Target(getter, displayContext.Value);
+
+ //var result = ((dynamic)(displayContext.Value))[parameter.Name];
+ if (result == null)
+ return null;
+
+ //if (parameter.Name == "Attributes") {
+ // var attributes = new RouteValueDictionary(result);
+ // return Arguments.From(attributes.Values, attributes.Keys);
+ //}
+
var converter = _converters.GetOrAdd(parameter.ParameterType, CompileConverter);
- return converter.Invoke((object)result);
+ var argument = converter.Invoke((object)result);
+ return argument;
}
+
+ static readonly ConcurrentDictionary>> _getters =
+ new ConcurrentDictionary>>();
+
static readonly ConcurrentDictionary> _converters =
new ConcurrentDictionary>();
diff --git a/src/Orchard/DisplayManagement/Shapes/Shape.cs b/src/Orchard/DisplayManagement/Shapes/Shape.cs
index b7c63d963..d6c611e71 100644
--- a/src/Orchard/DisplayManagement/Shapes/Shape.cs
+++ b/src/Orchard/DisplayManagement/Shapes/Shape.cs
@@ -17,7 +17,7 @@ namespace Orchard.DisplayManagement.Shapes {
public virtual Shape Add(object item, string position) {
try {
- ((dynamic) item).Metadata.Position = position;
+ ((dynamic)item).Metadata.Position = position;
}
catch {
// need to implemented positioned wrapper for non-shape objects
@@ -29,5 +29,9 @@ namespace Orchard.DisplayManagement.Shapes {
public virtual IEnumerator GetEnumerator() {
return _items.GetEnumerator();
}
+
+ public virtual IEnumerable Items {
+ get { return _items; }
+ }
}
}
diff --git a/src/Orchard/UI/Navigation/MenuFilter.cs b/src/Orchard/UI/Navigation/MenuFilter.cs
index 014ef8cc9..3c51423cf 100644
--- a/src/Orchard/UI/Navigation/MenuFilter.cs
+++ b/src/Orchard/UI/Navigation/MenuFilter.cs
@@ -39,7 +39,7 @@ namespace Orchard.UI.Navigation {
.Text(menuItem.Text)
.Href(menuItem.Href)
.RouteValues(menuItem.RouteValues)
- .Object(menuItem);
+ .Item(menuItem);
if (menuItem.Items != null && menuItem.Items.Any()) {
PopulateMenu(shape, menuItemShape, menuItem.Items);
diff --git a/src/Shapes.txt b/src/Shapes.txt
new file mode 100644
index 000000000..d29eaacf3
--- /dev/null
+++ b/src/Shapes.txt
@@ -0,0 +1,91 @@
+
+==context==
+
+WorkContext.Page == Layout shape
+
+
+==shapes==
+
+.Id
+.Classes
+.Attributes
+
+
+Document [:Layout]
+
+Layout
+- Zones (meta-property)
+- Title
+
+
+Zone
+-ZoneName
+
+Menu
+-MenuName
+
+MenuItem
+-Text
+-Href
+-RouteValues
+-Item
+
+Menu
+ MenuItem
+
+List: ul|ol + li*
+-Items (meta-property, bound to shape children or passed in)
+
+Pager
+-CurrentPage
+-RouteValues
+-Count
+?PageSize ?? 1
+
+Shape.Pager(CurrentPage:page, PageSize:10, ItemCount:53)
+Shape.Pager(CurrentPage:page, PageCount:6)
+Shape.Pager(CurrentPage:page)
+
+ x
+ y
+ z
+
+
+List
+ x
+ y
+ z
+
+Items_Content
+Items_Content__BlogPost
+Items_Content__Page
+Items_Content__Product
+
+Parts/Content
+
+Fields/Content
+
+
+.Items_Content()
+.Items_Widget()
+.Items_User()
+
+==template discovery strategy==
+Items/Content.cshtml -> "Items_Content"
+Items/Content-Page.cshtml -> "Items_Content__Page"
+Items/Content-45.cshtml -> "Items_Content__45"
+Items/Page.cshtml -> "Items_Content__Page"
+Items/BlogPost.cshtml -> "Items_Content__BlogPost"
+
+Widgets-TwitterThing.cshtml -> "Widget__TwitterThing"
+
+Items/User.cshtml -> "Items_User"
+
+Parts/Yarg -> "Parts_Yarg"
+Parts/Yarg-BlogPost -> "Parts_Yarg__BlogPost"
+
+==template discovery strategy==
+
+==templates==
+Header
+Footer