From 9259dd897f725c03905b7aab2e6b10b0bdc348c1 Mon Sep 17 00:00:00 2001 From: "agrath@gmail.com" Date: Tue, 25 Jan 2011 22:24:40 -1300 Subject: [PATCH] Upgraded DynamicNode to handle IEnumerable better so that you can chain method/property calls through an IEnumerable set like this: @Model.Children.Count() or @Model.Children.ElementAt(2) --- .../Core/umbraco.Linq.Core.csproj | 3 +- .../umbraco.editorControls.csproj | 3 +- .../umbraco.webservices.csproj | 4 +- umbraco.MacroEngines.Juno/DynamicNode.cs | 226 +++++++++++++----- umbraco.MacroEngines.Juno/RazorEngine.cs | 16 +- .../umbraco.MacroEngines.csproj | 2 +- umbraco.sln | 1 + .../org.umbraco.update/Reference.cs | 2 +- .../UpgradeResult.datasource | 2 +- .../presentation/umbraco.presentation.csproj | 4 +- umbraco/presentation/web.STANDARD.config | 2 +- umbraco/presentation/web.config | 9 +- 12 files changed, 195 insertions(+), 79 deletions(-) diff --git a/LinqToUmbraco/src/umbraco.Linq/Core/umbraco.Linq.Core.csproj b/LinqToUmbraco/src/umbraco.Linq/Core/umbraco.Linq.Core.csproj index 23653137dd..290f1b91ff 100644 --- a/LinqToUmbraco/src/umbraco.Linq/Core/umbraco.Linq.Core.csproj +++ b/LinqToUmbraco/src/umbraco.Linq/Core/umbraco.Linq.Core.csproj @@ -10,7 +10,7 @@ Properties umbraco.Linq.Core umbraco.Linq.Core - v3.5 + v4.0 512 @@ -40,6 +40,7 @@ false false true + true diff --git a/components/editorControls/umbraco.editorControls.csproj b/components/editorControls/umbraco.editorControls.csproj index af991252a2..4206304045 100644 --- a/components/editorControls/umbraco.editorControls.csproj +++ b/components/editorControls/umbraco.editorControls.csproj @@ -37,7 +37,7 @@ 3.5 true - v3.5 + v4.0 http://localhost/umbraco.editorControls/ true Web @@ -52,6 +52,7 @@ 1.0.0.%2a false true + ..\..\umbraco\presentation\bin\ diff --git a/components/umbraco.webservices/umbraco.webservices.csproj b/components/umbraco.webservices/umbraco.webservices.csproj index 08cfae2b51..0f4212651f 100644 --- a/components/umbraco.webservices/umbraco.webservices.csproj +++ b/components/umbraco.webservices/umbraco.webservices.csproj @@ -10,7 +10,7 @@ Properties umbraco.webservices umbraco.webservices - v3.5 + v4.0 512 @@ -39,6 +39,7 @@ false false true + true @@ -68,6 +69,7 @@ + diff --git a/umbraco.MacroEngines.Juno/DynamicNode.cs b/umbraco.MacroEngines.Juno/DynamicNode.cs index 8178a3cb45..9718f4ec3b 100644 --- a/umbraco.MacroEngines.Juno/DynamicNode.cs +++ b/umbraco.MacroEngines.Juno/DynamicNode.cs @@ -3,10 +3,12 @@ using System.Collections.Generic; using System.Dynamic; using System.Linq; using umbraco.interfaces; +using System.Collections; +using System.Reflection; namespace umbraco.MacroEngines { - public class DynamicNode : DynamicObject + public class DynamicNode : DynamicObject, IEnumerable { private readonly INode n; public DynamicNode(INode n) @@ -16,16 +18,111 @@ namespace umbraco.MacroEngines else throw new ArgumentNullException("n", "A node must be provided to make a dynamic instance"); } + public DynamicNode() + { + //Empty constructor for a special case with Generic Methods + } - public IEnumerable GetChildrenAsList + IEnumerable _children; + public DynamicNode(IEnumerable children) + { + _children = new List(children); + } + public IEnumerator GetEnumerator() + { + return _children.Select(x => new DynamicNode(x)).GetEnumerator(); + } + + + public DynamicNode GetChildrenAsList { get { - return from nn in n.ChildrenAsList - select new DynamicNode(nn); + return new DynamicNode(n.ChildrenAsList); } } + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + var name = binder.Name; + + try + { + //Property? + result = _children.GetType().InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.GetProperty, + null, + _children, + args); + return true; + } + catch (MissingMethodException) + { + try + { + //Static or Instance Method? + result = _children.GetType().InvokeMember(binder.Name, + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.Static | + System.Reflection.BindingFlags.InvokeMethod, + null, + _children, + args); + return true; + } + catch (MissingMethodException) + { + try + { + //Extension method + Type tObject = _children.GetType(); + Type t = tObject.GetGenericArguments()[0]; + Type tIEnumerable = typeof(IEnumerable<>).MakeGenericType(t); + + MethodInfo enumerableMethod = + typeof(Enumerable) + .GetMethod(name) + .MakeGenericMethod(t); + + var genericArgs = (new[] { _children }).Concat(args); + result = enumerableMethod.Invoke(null, genericArgs.ToArray()); + + return true; + } + catch (TargetInvocationException) + { + //We do this to enable error checking of Razor Syntax when a method e.g. ElementAt(2) is used. + //When the Script is tested, there's no INode which means no children which means ElementAt(2) is invalid (IndexOutOfRange) + //Instead, we are going to return an empty DynamicNode. + //This could be improved by checking return type of generic method above + //So we could support Generic Methods that return IEnumerable as well as Singular + result = new DynamicNode(); + return true; + } + catch + { + result = null; + return false; + } + + } + + + } + catch + { + result = null; + return false; + } + + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) { @@ -37,46 +134,52 @@ namespace umbraco.MacroEngines return true; } - var data = n.GetProperty(name); - // check for nicer support of Pascal Casing EVEN if alias is camelCasing: - if (data == null && name.Substring(0,1).ToUpper() == name.Substring(0,1)) + if (n != null) { - data = n.GetProperty(name.Substring(0, 1).ToLower() + name.Substring((1))); + var data = n.GetProperty(name); + // check for nicer support of Pascal Casing EVEN if alias is camelCasing: + if (data == null && name.Substring(0, 1).ToUpper() == name.Substring(0, 1)) + { + data = n.GetProperty(name.Substring(0, 1).ToLower() + name.Substring((1))); + } + + if (data != null) + { + result = data.Value; + return true; + } + + //check if the alias is that of a child type + var typeChildren = n.ChildrenAsList + .Where(x => MakePluralName(x.NodeTypeAlias) == name || x.NodeTypeAlias == name); + if (typeChildren.Any()) + { + result = new DynamicNode(typeChildren); + return true; + } + + try + { + result = n.GetType().InvokeMember(binder.Name, + System.Reflection.BindingFlags.GetProperty | + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.NonPublic, + null, + n, + null); + return true; + } + catch + { + result = null; + return false; + } } - if (data != null) - { - result = data.Value; - return true; - } - //check if the alias is that of a child type - var typeChildren = n.ChildrenAsList - .Where(x => MakePluralName(x.NodeTypeAlias) == name || x.NodeTypeAlias == name); - if (typeChildren.Any()) - { - result = typeChildren - .Select(x => new DynamicNode(x)); - return true; - } - - try - { - result = n.GetType().InvokeMember(binder.Name, - System.Reflection.BindingFlags.GetProperty | - System.Reflection.BindingFlags.Instance | - System.Reflection.BindingFlags.Public | - System.Reflection.BindingFlags.NonPublic, - null, - n, - null); - return true; - } - catch - { - result = null; - return false; - } + result = null; + return false; } //this is from SqlMetal and just makes it a bit of fun to allow pluralisation @@ -135,62 +238,62 @@ namespace umbraco.MacroEngines public DynamicNode Parent { - get { return new DynamicNode(n.Parent); } + get { if (n == null) return null; return new DynamicNode(n.Parent); } } public int Id { - get { return n.Id; } + get { if (n == null) return 0; return n.Id; } } public int template { - get { return n.template; } + get { if (n == null) return 0; return n.template; } } public int SortOrder { - get { return n.SortOrder; } + get { if (n == null) return 0; return n.SortOrder; } } public string Name { - get { return n.Name; } + get { if (n == null) return null; return n.Name; } } public string Url { - get { return n.Url; } + get { if (n == null) return null; return n.Url; } } public string UrlName { - get { return n.UrlName; } + get { if (n == null) return null; return n.UrlName; } } public string NodeTypeAlias { - get { return n.NodeTypeAlias; } + get { if (n == null) return null; return n.NodeTypeAlias; } } public string WriterName { - get { return n.WriterName; } + get { if (n == null) return null; return n.WriterName; } } public string CreatorName { - get { return n.CreatorName; } + get { if (n == null) return null; return n.CreatorName; } } public int WriterID { - get { return n.WriterID; } + get { if (n == null) return 0; return n.WriterID; } } public int CreatorID { - get { return n.CreatorID; } + get { if (n == null) return 0; return n.CreatorID; } } public string Path @@ -200,52 +303,57 @@ namespace umbraco.MacroEngines public DateTime CreateDate { - get { return n.CreateDate; } + get { if (n == null) return DateTime.MinValue; return n.CreateDate; } } public DateTime UpdateDate { - get { return n.UpdateDate; } + get { if (n == null) return DateTime.MinValue; return n.UpdateDate; } } public Guid Version { - get { return n.Version; } + get { if (n == null) return Guid.Empty; return n.Version; } } public string NiceUrl { - get { return n.NiceUrl; } + get { if (n == null) return null; return n.NiceUrl; } } public int Level { - get { return n.Level; } + get { if (n == null) return 0; return n.Level; } } public List PropertiesAsList { - get { return n.PropertiesAsList; } + get { if (n == null) return null; return n.PropertiesAsList; } } public List ChildrenAsList { - get { return n.ChildrenAsList; } + get { if (n == null) return null; return n.ChildrenAsList; } } public IProperty GetProperty(string alias) { + if (n == null) return null; return n.GetProperty(alias); } public System.Data.DataTable ChildrenAsTable() { + if (n == null) return null; return n.ChildrenAsTable(); } public System.Data.DataTable ChildrenAsTable(string nodeTypeAliasFilter) { + if (n == null) return null; return n.ChildrenAsTable(nodeTypeAliasFilter); } + + } } diff --git a/umbraco.MacroEngines.Juno/RazorEngine.cs b/umbraco.MacroEngines.Juno/RazorEngine.cs index a298af799b..0c9341fb51 100644 --- a/umbraco.MacroEngines.Juno/RazorEngine.cs +++ b/umbraco.MacroEngines.Juno/RazorEngine.cs @@ -25,7 +25,7 @@ namespace umbraco.MacroEngines { get { - var exts = new List {"razor"}; + var exts = new List { "razor" }; return exts; } } @@ -40,7 +40,8 @@ namespace umbraco.MacroEngines try { string parsedResult; - if (!GetResult(null, code, currentPage, out parsedResult)) { + if (!GetResult(null, code, currentPage, out parsedResult)) + { errorMessage = parsedResult; return false; } @@ -53,8 +54,8 @@ namespace umbraco.MacroEngines } // clear razor compilation cache (a hack - by setting the template base type back/forward as there isn't a clear cache method) - Razor.SetTemplateBaseType(typeof (HtmlTemplateBase<>)); - Razor.SetTemplateBaseType(typeof (UmbracoTemplateBase<>)); + Razor.SetTemplateBaseType(typeof(HtmlTemplateBase<>)); + Razor.SetTemplateBaseType(typeof(UmbracoTemplateBase<>)); errorMessage = String.Empty; @@ -70,7 +71,8 @@ namespace umbraco.MacroEngines GetResult(macro.CacheIdentifier, template, currentPage, out parsedResult); // if it's a file we'll monitor changes to ensure that any updates to the file clears the cache - if (String.IsNullOrEmpty(macro.ScriptCode)) { + if (String.IsNullOrEmpty(macro.ScriptCode)) + { FileMonitor.Listen(SystemDirectories.Python + "/" + macro.ScriptName, action => RazorEngine.ClearRazorCompilationCache()); } @@ -92,7 +94,7 @@ namespace umbraco.MacroEngines { try { - Razor.SetTemplateBaseType(typeof (UmbracoTemplateBase<>)); + Razor.SetTemplateBaseType(typeof(UmbracoTemplateBase<>)); result = Razor.Parse(template, new DynamicNode(currentPage), cacheIdentifier); return true; } @@ -149,7 +151,7 @@ namespace umbraco.MacroEngines public override T Model { - get { return (T) m_model; } + get { return (T)m_model; } set { m_model = value; } } diff --git a/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj b/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj index 2de07b2d3b..dd3454dd8c 100644 --- a/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj +++ b/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj @@ -88,7 +88,7 @@ - xcopy "$(ProjectDir)bin\debug\umbraco.MacroEngines.*" "$(ProjectDir)..\presentation\bin\" /Y + xcopy "$(ProjectDir)bin\debug\umbraco.MacroEngines.*" "$(ProjectDir)..\presentation\bin\" /Y/F - umbraco.presentation.org.umbraco.update.UpgradeResult + umbraco.presentation.org.umbraco.update.UpgradeResult, Web References.org.umbraco.update.Reference.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null \ No newline at end of file diff --git a/umbraco/presentation/umbraco.presentation.csproj b/umbraco/presentation/umbraco.presentation.csproj index 4eb7eb118b..7ed4b8a2f9 100644 --- a/umbraco/presentation/umbraco.presentation.csproj +++ b/umbraco/presentation/umbraco.presentation.csproj @@ -36,7 +36,7 @@ 3.5 - v3.5 + v4.0 @@ -132,6 +132,7 @@ 3.5 + @@ -1642,6 +1643,7 @@ + diff --git a/umbraco/presentation/web.STANDARD.config b/umbraco/presentation/web.STANDARD.config index c203a6de19..d51765a1e9 100644 --- a/umbraco/presentation/web.STANDARD.config +++ b/umbraco/presentation/web.STANDARD.config @@ -76,7 +76,7 @@ - + diff --git a/umbraco/presentation/web.config b/umbraco/presentation/web.config index ec10d6c1d8..ea4ae790a8 100644 --- a/umbraco/presentation/web.config +++ b/umbraco/presentation/web.config @@ -33,8 +33,8 @@ - - + + @@ -65,14 +65,13 @@ - - - + +