Further shapes work

Adding the ability to bind a method with a TextWriter Output parameter
Bringing over work from UnorderedList
Parameter bound with GetMember instead of array access to be compatible with base class properties

--HG--
branch : theming
This commit is contained in:
Louis DeJardin
2010-09-08 19:30:30 -07:00
parent fceea540ba
commit 899abcca96
12 changed files with 235 additions and 70 deletions

View File

@@ -1 +1 @@
c6970f1f2d9c7a2bfdc0aeb43658dfcaf5688871 Clay
57ebfff6441a6a246e81c1992996f06cd66c60ac Clay

View File

@@ -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);
}
}
}

View File

@@ -4,3 +4,4 @@
<p><%: 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”.") %></p>
<p><%: T("Have fun!") %><br /><%: T("The Orchard Team") %></p>
<%:Display(Model) %>

View File

@@ -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,6 +11,8 @@ 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; }
@@ -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<string>();
created.Shape.Attributes = new Dictionary<string, string>();
created.Shape.ItemClasses = new List<string>();
created.Shape.ItemAttributes = new Dictionary<string, string>();
});
}
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<string> classes, IDictionary<string, string> attributes) {
var tagBuilder = new TagBuilder(tagName);
tagBuilder.MergeAttributes(attributes, false);
foreach (var cssClass in classes ?? Enumerable.Empty<string>())
tagBuilder.AddCssClass(cssClass);
if (id != null)
tagBuilder.GenerateId(id);
return tagBuilder;
}
[Shape]
public void List(
dynamic Display,
TextWriter Output,
IEnumerable<dynamic> Items,
string Tag,
string Id,
IEnumerable<string> Classes,
IDictionary<string, string> Attributes,
IEnumerable<string> ItemClasses,
IDictionary<string, string> 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));
}
}
}

View File

@@ -87,7 +87,6 @@
<Content Include="ScaffoldingTemplates\DataMigration.txt" />
<Compile Include="Services\ScaffoldingCommandInterpreter.cs" />
<Compile Include="Settings\DevToolsSettings.cs" />
<Compile Include="Shapes.cs" />
<Compile Include="ViewModels\CommandsExecuteViewModel.cs" />
<Compile Include="ViewModels\ContentIndexViewModel.cs" />
<Compile Include="ViewModels\ContentDetailsViewModel.cs" />

View File

@@ -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("<h2>" + text + "</h2>");
}
[Shape]
public IHtmlString Explosion(int? Height, int? Width) {
return new HtmlString(string.Format("<span>Boom {0}x{1}</span>", 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<IHtmlString> 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("<p class=\"message\">"), Severity ?? "Neutral", ": ", Content, new HtmlString("</p>"));
}
static IHtmlString Combine(IEnumerable<IHtmlString> contents) {
return new HtmlString(contents.Aggregate("", (a, b) => a + b));
}
static IEnumerable<IHtmlString> DisplayAll(dynamic Display, dynamic Shape) {
foreach (var item in Shape) {
yield return Display(item);
}
}
}
}

View File

@@ -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<ConfiguredEnginesCache>().As<IConfiguredEnginesCache>();
builder.RegisterType<PageWorkContext>().As<IWorkContextStateProvider>();
builder.RegisterType<CoreShapes>().As<IShapeDescriptorBindingStrategy>();
builder.RegisterType<CoreShapes>().As<IShapeDescriptorBindingStrategy>().WithProperty("Feature", Feature);
builder.RegisterType<ShapeTemplateBindingStrategy>().As<IShapeDescriptorBindingStrategy>();
builder.RegisterType<BasicShapeTemplateHarvester>().As<IShapeTemplateHarvester>();
}

View File

@@ -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

View File

@@ -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<Func<CallSite, object, dynamic>>.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<string, CallSite<Func<CallSite, object, dynamic>>> _getters =
new ConcurrentDictionary<string, CallSite<Func<CallSite, object, dynamic>>>();
static readonly ConcurrentDictionary<Type, Func<object, object>> _converters =
new ConcurrentDictionary<Type, Func<object, object>>();

View File

@@ -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<dynamic> Items {
get { return _items; }
}
}
}

View File

@@ -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);

91
src/Shapes.txt Normal file
View File

@@ -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