diff --git a/default.build b/default.build index 6be7ce3532..647559ec61 100644 --- a/default.build +++ b/default.build @@ -14,7 +14,11 @@ + + + + @@ -41,8 +45,7 @@ - - + @@ -68,7 +71,26 @@ - + + + + + + + + + + + + + + + + + + + + @@ -77,16 +99,53 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -108,13 +167,15 @@ + + @@ -133,13 +194,14 @@ + - + @@ -232,35 +294,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -329,7 +363,7 @@ - + @@ -339,7 +373,7 @@ - + @@ -348,15 +382,34 @@ + + + + + + + + + + + + + + + - + + + + + diff --git a/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicNode.cs b/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicNode.cs index e2b22078d6..55e60d2a28 100644 --- a/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicNode.cs +++ b/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicNode.cs @@ -205,20 +205,21 @@ namespace umbraco.MacroEngines { var name = binder.Name; + result = null; //this will never be returned if (name == "ChildrenAsList" || name == "Children") { result = GetChildrenAsList; return true; } - + bool propertyExists = false; if (n != null) { - var data = n.GetProperty(name); + var data = n.GetProperty(name, out propertyExists); // 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 (data == null && name.Substring(0, 1).ToUpper() == name.Substring(0, 1) && !propertyExists) { - data = n.GetProperty(name.Substring(0, 1).ToLower() + name.Substring((1))); + data = n.GetProperty(name.Substring(0, 1).ToLower() + name.Substring((1)), out propertyExists); } if (data != null) @@ -265,10 +266,17 @@ namespace umbraco.MacroEngines //if property access, type lookup and member invoke all failed //at this point, we're going to return null - //instead, we return an empty list + //instead, we return a DynamicNull - see comments in that file //this will let things like Model.ChildItem work and return nothing instead of crashing - result = new DynamicNodeList(new List()); - //changed this to a return true because it breaks testing when using .Children().Random().propertyName + if (!propertyExists && result == null) + { + //.Where explictly checks for this type + //and will make it false + //which means backwards equality (&& property != true) will pass + //forwwards equality (&& property or && property == true) will fail + result = new DynamicNull(); + return true; + } return true; } @@ -281,7 +289,7 @@ namespace umbraco.MacroEngines if (dataType == DATATYPE_YESNO_GUID) { bool parseResult; - if (result.ToString() == "") result = "0"; + if (string.Format("{0}", result) == "") result = "0"; if (Boolean.TryParse(result.ToString().Replace("1", "true").Replace("0", "false"), out parseResult)) { result = parseResult; diff --git a/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicNull.cs b/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicNull.cs new file mode 100644 index 0000000000..7bfe6462c5 --- /dev/null +++ b/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicNull.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections; +using System.Dynamic; + +namespace umbraco.MacroEngines +{ + //This type is used as a return type when TryGetMember fails on a DynamicNode + //.Where explicitly checks for this type, to indicate that nothing was returned + //Because it's IEnumerable, if the user is actually trying @Model.TextPages or similar + //it will still return an enumerable object (assuming the call actually failed because there were no children of that type) + //but in .Where, if they use a property that doesn't exist, the lambda will bypass this and return false + public class DynamicNull : DynamicObject, IEnumerable + { + public IEnumerator GetEnumerator() + { + return (new List()).GetEnumerator(); + } + public DynamicNull Where(string predicate, params object[] values) + { + return this; + } + public DynamicNull OrderBy(string orderBy) + { + return this; + } + public override string ToString() + { + return string.Empty; + } + } +} diff --git a/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicQueryable.cs b/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicQueryable.cs index 6d8202c3b5..1f1833869c 100644 --- a/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicQueryable.cs +++ b/umbraco.MacroEngines.Juno/RazorDynamicNode/DynamicQueryable.cs @@ -54,6 +54,7 @@ namespace System.Linq.Dynamic { return (bool)value; } + return false; } catch (Exception) @@ -1467,6 +1468,12 @@ namespace System.Linq.Dynamic Expression.Assign(result, Expression.Constant(null)), Expression.IfThen(Expression.NotEqual(Expression.Constant(null), instanceExpression), Expression.Call(instanceExpression, method, binder, result)), + Expression.IfThen( + Expression.TypeEqual(result, typeof(DynamicNull)), + Expression.Assign(result, + Expression.Constant(false, typeof(object)) + ) + ), Expression.Return(blockReturnLabel, result), Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))) ); diff --git a/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj b/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj index 9fef26f020..286cde54dc 100644 --- a/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj +++ b/umbraco.MacroEngines.Juno/umbraco.MacroEngines.csproj @@ -64,6 +64,7 @@ + diff --git a/umbraco.sln b/umbraco.sln index 0f4e59c558..f41144e7b7 100644 --- a/umbraco.sln +++ b/umbraco.sln @@ -22,7 +22,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StandardConfig", "StandardC config templates\umbraco.config = config templates\umbraco.config config templates\config\umbracoSettings.config = config templates\config\umbracoSettings.config config templates\config\UrlRewriting.config = config templates\config\UrlRewriting.config - config templates\web.config = config templates\web.config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packages", "Packages", "{5BB61485-B480-4A03-A8DD-1636A5AF575C}" diff --git a/umbraco/interfaces/INode.cs b/umbraco/interfaces/INode.cs index d3c9ca1bd8..e4ecd9c215 100644 --- a/umbraco/interfaces/INode.cs +++ b/umbraco/interfaces/INode.cs @@ -29,6 +29,7 @@ namespace umbraco.interfaces List PropertiesAsList { get; } List ChildrenAsList { get; } IProperty GetProperty(string Alias); + IProperty GetProperty(string Alias, out bool propertyExists); DataTable ChildrenAsTable(); DataTable ChildrenAsTable(string nodeTypeAliasFilter); } diff --git a/umbraco/presentation/umbraco.presentation.csproj b/umbraco/presentation/umbraco.presentation.csproj index 94738f3954..775b903e97 100644 --- a/umbraco/presentation/umbraco.presentation.csproj +++ b/umbraco/presentation/umbraco.presentation.csproj @@ -1922,6 +1922,7 @@ + Designer diff --git a/umbraco/presentation/umbraco/create/DLRScripting.ascx.cs b/umbraco/presentation/umbraco/create/DLRScripting.ascx.cs index 4c4983619d..3c55f39b9f 100644 --- a/umbraco/presentation/umbraco/create/DLRScripting.ascx.cs +++ b/umbraco/presentation/umbraco/create/DLRScripting.ascx.cs @@ -61,6 +61,9 @@ namespace umbraco.presentation.create string abPath = IO.IOHelper.MapPath(path); list.Items.Clear(); + // always add the option of an empty one + list.Items.Add(new ListItem("Empty template", "")); + if (System.IO.Directory.Exists(abPath)) { string extension = "." + scriptType; @@ -72,10 +75,6 @@ namespace umbraco.presentation.create list.Items.Add(new ListItem(helper.SpaceCamelCasing(filename.Replace(extension, "")), scriptType + "/" + filename)); } } - else - { - list.Items.Add(new ListItem("Empty template", "")); - } } } } \ No newline at end of file diff --git a/umbraco/presentation/umbraco/nodeFactory/Page.cs b/umbraco/presentation/umbraco/nodeFactory/Page.cs index a182c6a4b8..a554472f30 100644 --- a/umbraco/presentation/umbraco/nodeFactory/Page.cs +++ b/umbraco/presentation/umbraco/nodeFactory/Page.cs @@ -314,6 +314,20 @@ namespace umbraco.NodeFactory return null; } + public IProperty GetProperty(string Alias, out bool propertyExists) + { + foreach (Property p in Properties) + { + if (p.Alias == Alias) + { + propertyExists = true; + return p; + } + } + propertyExists = false; + return null; + } + public static Node GetNodeByXpath(string xpath) { XPathNodeIterator xpathNode = library.GetXmlNodeByXPath(xpath); diff --git a/umbraco/presentation/web.config b/umbraco/presentation/web.config index 6d8e33b4d3..1308072404 100644 --- a/umbraco/presentation/web.config +++ b/umbraco/presentation/web.config @@ -39,8 +39,8 @@ - - + + diff --git a/umbraco/presentation/web.config.ssaolap01.xslt b/umbraco/presentation/web.config.ssaolap01.xslt index 98e733067d..dc347ada6e 100644 --- a/umbraco/presentation/web.config.ssaolap01.xslt +++ b/umbraco/presentation/web.config.ssaolap01.xslt @@ -8,7 +8,7 @@ - 4.6.1 + 4.7.0.beta