diff --git a/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs b/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs index 2345f1dc2..83d4e4761 100644 --- a/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs +++ b/src/Orchard/DisplayManagement/Descriptors/ShapeAttributeStrategy/ShapeAttributeBindingStrategy.cs @@ -82,6 +82,9 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy { if (parameter.Name == "Output" && parameter.ParameterType == typeof(TextWriter)) return output; + if (parameter.Name == "Output" && parameter.ParameterType == typeof(Action)) + return new Action(output.Write); + // meh-- if (parameter.Name == "Html") { return new HtmlHelper( @@ -90,8 +93,12 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy { _routeCollection); } + if (parameter.Name == "Url" && parameter.ParameterType.IsAssignableFrom(typeof(UrlHelper))) { + return new UrlHelper(displayContext.ViewContext.RequestContext, _routeCollection); + } + var getter = _getters.GetOrAdd(parameter.Name, n => - CallSite>.Create( + CallSite>.Create( Microsoft.CSharp.RuntimeBinder.Binder.GetMember( CSharpBinderFlags.None, n, null, new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }))); @@ -106,13 +113,13 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy { } - static readonly ConcurrentDictionary>> _getters = - new ConcurrentDictionary>>(); + static readonly ConcurrentDictionary>> _getters = + new ConcurrentDictionary>>(); - static readonly ConcurrentDictionary> _converters = - new ConcurrentDictionary>(); + static readonly ConcurrentDictionary> _converters = + new ConcurrentDictionary>(); - static Func CompileConverter(Type targetType) { + static Func CompileConverter(Type targetType) { var valueParameter = Expression.Parameter(typeof(object), "value"); return Expression.Lambda>( diff --git a/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs b/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs index 7bd7dd247..a295bb240 100644 --- a/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs +++ b/src/Orchard/DisplayManagement/Implementation/DefaultShapeFactory.cs @@ -58,14 +58,15 @@ namespace Orchard.DisplayManagement.Implementation { new ClaySharp.Behaviors.InterfaceProxyBehavior(), new ClaySharp.Behaviors.PropBehavior(), new ClaySharp.Behaviors.ArrayBehavior(), - new ClaySharp.Behaviors.NilResultBehavior() + new ClaySharp.Behaviors.NilResultBehavior(), }; } else { creatingContext.Behaviors = new List { new ClaySharp.Behaviors.InterfaceProxyBehavior(), new ClaySharp.Behaviors.PropBehavior(), - new ClaySharp.Behaviors.NilResultBehavior() + new ClaySharp.Behaviors.NilResultBehavior(), + new Shape.ShapeBehavior(), }; } diff --git a/src/Orchard/DisplayManagement/Shapes/Shape.cs b/src/Orchard/DisplayManagement/Shapes/Shape.cs index 536e5edea..be5ca6575 100644 --- a/src/Orchard/DisplayManagement/Shapes/Shape.cs +++ b/src/Orchard/DisplayManagement/Shapes/Shape.cs @@ -1,8 +1,11 @@ using System; +using System.Linq; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Web.Mvc; +using ClaySharp; +using ClaySharp.Implementation; namespace Orchard.DisplayManagement.Shapes { [DebuggerTypeProxy(typeof(ShapeDebugView))] @@ -20,7 +23,7 @@ namespace Orchard.DisplayManagement.Shapes { public virtual IDictionary Attributes { get { return _attributes; } } public virtual IEnumerable Items { get { return _items; } } - public virtual Shape Add(object item, string position = DefaultPosition) { + public virtual Shape Add(object item, string position = null) { // pszmyd: Ignoring null shapes if (item == null) { return this; @@ -32,7 +35,7 @@ namespace Orchard.DisplayManagement.Shapes { item is String ) { // need to implement positioned wrapper for non-shape objects } - else { + else if (item is IShape) { ((dynamic) item).Metadata.Position = position; } } @@ -53,5 +56,123 @@ namespace Orchard.DisplayManagement.Shapes { public virtual IEnumerator GetEnumerator() { return _items.GetEnumerator(); } + + public class ShapeBehavior : ClayBehavior { + public override object SetIndex(Func proceed, dynamic self, IEnumerable keys, object value) { + if (keys.Count() == 1) { + var name = keys.Single().ToString(); + if (name.Equals("Id")) { + // need to mutate the actual type + var s = self as Shape; + if (s != null) { + s.Id = System.Convert.ToString(value); + } + return value; + } + if (name.Equals("Classes")) { + var args = Arguments.From(new[] { value }, Enumerable.Empty()); + MergeClasses(args, self.Classes); + return value; + } + if (name.Equals("Attributes")) { + var args = Arguments.From(new[] { value }, Enumerable.Empty()); + MergeAttributes(args, self.Attributes); + return value; + } + if (name.Equals("Items")) { + var args = Arguments.From(new[] { value }, Enumerable.Empty()); + MergeItems(args, self); + return value; + } + } + return proceed(); + } + + public override object InvokeMember(Func proceed, dynamic self, string name, INamedEnumerable args) { + if (name.Equals("Id")) { + // need to mutate the actual type + var s = self as Shape; + if (s != null) { + s.Id = System.Convert.ToString(args.FirstOrDefault()); + } + return self; + } + if (name.Equals("Classes") && !args.Named.Any()) { + MergeClasses(args, self.Classes); + return self; + } + if (name.Equals("Attributes") && args.Positional.Count() <= 1) { + MergeAttributes(args, self.Attributes); + return self; + } + if (name.Equals("Items")) { + MergeItems(args, self); + return self; + } + return proceed(); + } + + private static void MergeAttributes(INamedEnumerable args, IDictionary attributes) { + var arg = args.Positional.SingleOrDefault(); + if (arg != null) { + if (arg is IDictionary) { + var dictionary = arg as IDictionary; + foreach (var key in dictionary.Keys) { + attributes[System.Convert.ToString(key)] = System.Convert.ToString(dictionary[key]); + } + } + else { + foreach (var prop in arg.GetType().GetProperties()) { + attributes[TranslateIdentifier(prop.Name)] = System.Convert.ToString(prop.GetValue(arg, null)); + } + } + } + foreach (var named in args.Named) { + attributes[named.Key] = System.Convert.ToString(named.Value); + } + } + + private static string TranslateIdentifier(string name) { + // Allows for certain characters in an identifier to represent different + // characters in an HTML attribute (mimics MVC behavior): + // data_foo ==> data-foo + // @keyword ==> keyword + return name.Replace("_", "-").Replace("@", ""); + } + + private static void MergeClasses(INamedEnumerable args, IList classes) { + foreach (var arg in args) { + // look for string first, because the "string" type is also an IEnumerable of char + if (arg is string) { + classes.Add(arg as string); + } + else if (arg is IEnumerable) { + foreach (var item in arg as IEnumerable) { + classes.Add(System.Convert.ToString(item)); + } + } + else { + classes.Add(System.Convert.ToString(arg)); + } + } + } + + private static void MergeItems(INamedEnumerable args, dynamic shape) { + foreach (var arg in args) { + // look for string first, because the "string" type is also an IEnumerable of char + if (arg is string) { + shape.Add(arg as string); + } + else if (arg is IEnumerable) { + foreach (var item in arg as IEnumerable) { + shape.Add(item); + } + } + else { + shape.Add(System.Convert.ToString(arg)); + } + } + } + } } }