From 010893a73a08e8f2f8cb3ef45920e0079f56d05f Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Sat, 2 Mar 2013 01:02:38 +0600 Subject: [PATCH] Fixes: #U4-1822, #U4-1797 - Is helpers and indexes on result collections using IPublishedContent or DynamicPublishedContent were incorrect, added unit tests to support issues and fixes. --- src/Umbraco.Core/Dynamics/DynamicXml.cs | 6 +- src/Umbraco.Core/Dynamics/ExtensionMethods.cs | 59 +- src/Umbraco.Core/Models/ContentExtensions.cs | 6 + .../DynamicDocumentTestsBase.cs | 49 +- .../PublishedContent/PublishedContentTests.cs | 148 ++++- .../StronglyTypedQueryTests.cs | 43 +- src/Umbraco.Web/Dynamics/Grouping.cs | 2 +- .../Models/DynamicPublishedContent.cs | 44 +- .../Models/DynamicPublishedContentList.cs | 50 +- .../Models/IOwnerCollectionAware.cs | 12 + .../Models/PublishedContentBase.cs | 36 +- src/Umbraco.Web/Models/XmlPublishedContent.cs | 2 +- src/Umbraco.Web/PublishedContentExtensions.cs | 539 +++++++++++------- src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../RazorDynamicNode/DynamicNode.cs | 10 +- .../RazorDynamicNode/ExtensionMethods.cs | 6 +- 16 files changed, 736 insertions(+), 277 deletions(-) create mode 100644 src/Umbraco.Web/Models/IOwnerCollectionAware.cs diff --git a/src/Umbraco.Core/Dynamics/DynamicXml.cs b/src/Umbraco.Core/Dynamics/DynamicXml.cs index a3f9ae7089..0e0d14d9c9 100644 --- a/src/Umbraco.Core/Dynamics/DynamicXml.cs +++ b/src/Umbraco.Core/Dynamics/DynamicXml.cs @@ -716,7 +716,8 @@ namespace Umbraco.Core.Dynamics } public IEnumerable Descendants(Func func) { - var flattenedNodes = this.BaseElement.Elements().Map(func, n => n.Elements()); + //var flattenedNodes = this.BaseElement.Elements().Map(func, n => n.Elements()); + var flattenedNodes = this.BaseElement.Elements().SelectMany(n => n.Elements()).Where(func); return flattenedNodes.ToList().ConvertAll(n => new DynamicXml(n)); } public IEnumerable DescendantsOrSelf() @@ -725,7 +726,8 @@ namespace Umbraco.Core.Dynamics } public IEnumerable DescendantsOrSelf(Func func) { - var flattenedNodes = this.BaseElement.Elements().Map(func, n => n.Elements()); + //var flattenedNodes = this.BaseElement.Elements().Map(func, n => n.Elements()); + var flattenedNodes = this.BaseElement.Elements().SelectMany(n => n.Elements()).Where(func); var list = new List(); list.Add(this); list.AddRange(flattenedNodes.ToList().ConvertAll(n => new DynamicXml(n))); diff --git a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs index f36d4f255d..0637864ceb 100644 --- a/src/Umbraco.Core/Dynamics/ExtensionMethods.cs +++ b/src/Umbraco.Core/Dynamics/ExtensionMethods.cs @@ -5,36 +5,35 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Dynamics { + [Obsolete("This class should not be used, it is just referenced by already obsoleted code and will be removed in the future")] internal static class ExtensionMethods { - public static IEnumerable Map( - this IEnumerable source, - Func selectorFunction, - Func> getChildrenFunction) - { - if (!source.Any()) - { - return source; - } - // Add what we have to the stack - var flattenedList = source.Where(selectorFunction); - // Go through the input enumerable looking for children, - // and add those if we have them - foreach (TSource element in source) - { - var secondInner = getChildrenFunction(element); - if (secondInner.Any()) - { - secondInner = secondInner.Map(selectorFunction, getChildrenFunction); - } - flattenedList = flattenedList.Concat(secondInner); - } - return flattenedList; - } - - - + //public static IEnumerable Map( + // this IEnumerable source, + // Func selectorFunction, + // Func> getChildrenFunction) + //{ + // if (!source.Any()) + // { + // return source; + // } + // // Add what we have to the stack + // var flattenedList = source.Where(selectorFunction); + // // Go through the input enumerable looking for children, + // // and add those if we have them + // foreach (TSource element in source) + // { + // var secondInner = getChildrenFunction(element); + // if (secondInner.Any()) + // { + // secondInner = secondInner.Map(selectorFunction, getChildrenFunction); + // } + // flattenedList = flattenedList.Concat(secondInner); + // } + // return flattenedList; + //} + [Obsolete("This method should not be used and will be removed in the future")] public static bool ContainsAny(this string haystack, IEnumerable needles) { if (haystack == null) throw new ArgumentNullException("haystack"); @@ -44,6 +43,7 @@ namespace Umbraco.Core.Dynamics } return false; } + [Obsolete("This method should not be used and will be removed in the future")] public static bool ContainsAny(this string haystack, params string[] needles) { if (haystack == null) throw new ArgumentNullException("haystack"); @@ -53,6 +53,7 @@ namespace Umbraco.Core.Dynamics } return false; } + [Obsolete("This method should not be used and will be removed in the future")] public static bool ContainsAny(this string haystack, StringComparison comparison, IEnumerable needles) { if (haystack == null) throw new ArgumentNullException("haystack"); @@ -62,6 +63,7 @@ namespace Umbraco.Core.Dynamics } return false; } + [Obsolete("This method should not be used and will be removed in the future")] public static bool ContainsAny(this string haystack, StringComparison comparison, params string[] needles) { if (haystack == null) throw new ArgumentNullException("haystack"); @@ -71,12 +73,13 @@ namespace Umbraco.Core.Dynamics } return false; } + [Obsolete("This method should not be used and will be removed in the future")] public static bool ContainsInsensitive(this string haystack, string needle) { if (haystack == null) throw new ArgumentNullException("haystack"); return haystack.IndexOf(needle, StringComparison.CurrentCultureIgnoreCase) >= 0; } - + [Obsolete("This method should not be used and will be removed in the future")] public static bool HasValue(this string s) { return !string.IsNullOrWhiteSpace(s); diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 2ee979cdf6..8cfa743964 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -472,6 +472,12 @@ namespace Umbraco.Core.Models return xml; } + /// + /// Used by ToDeepXml to recursively add children + /// + /// + /// + /// private static void AddChildXml( IContent[] originalDescendants, IEnumerable currentChildren, diff --git a/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs index df5b3fd6be..4cc5b7e6a3 100644 --- a/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs +++ b/src/Umbraco.Tests/PublishedContent/DynamicDocumentTestsBase.cs @@ -46,17 +46,18 @@ namespace Umbraco.Tests.PublishedContent This is some content]]> - + - - + + + + + 1 - - @@ -77,6 +78,28 @@ namespace Umbraco.Tests.PublishedContent /// protected abstract dynamic GetDynamicNode(int id); + /// + /// Tests the IsLast method with the result set from a Where statement + /// + [Test] + public void Is_Last_From_Where_Filter() + { + var doc = GetDynamicNode(1173); + + foreach (var d in doc.Children.Where("Visible")) + { + if (d.Id != 1178) + { + Assert.IsFalse(d.IsLast()); + } + else + { + Assert.IsTrue(d.IsLast()); + } + } + + } + [Test] public void Single() { @@ -222,11 +245,11 @@ namespace Umbraco.Tests.PublishedContent var doc = GetDynamicNode(1173); Assert.AreEqual(0, doc.Index()); doc = GetDynamicNode(1176); - Assert.AreEqual(1, doc.Index()); - doc = GetDynamicNode(1177); - Assert.AreEqual(2, doc.Index()); - doc = GetDynamicNode(1178); Assert.AreEqual(3, doc.Index()); + doc = GetDynamicNode(1177); + Assert.AreEqual(1, doc.Index()); + doc = GetDynamicNode(1178); + Assert.AreEqual(2, doc.Index()); } [Test] @@ -372,7 +395,7 @@ namespace Umbraco.Tests.PublishedContent var casted = (IEnumerable)skip; Assert.AreEqual(2, casted.Count()); - Assert.IsTrue(casted.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[]{1177, 1178})); + Assert.IsTrue(casted.Select(x => ((dynamic) x).Id).ContainsAll(new dynamic[] {1178, 1176})); } @@ -397,7 +420,7 @@ namespace Umbraco.Tests.PublishedContent var casted = (IEnumerable)take; Assert.AreEqual(2, casted.Count()); - Assert.IsTrue(casted.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1176 })); + Assert.IsTrue(casted.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1177 })); } [Test] @@ -615,7 +638,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Next_Without_Sibling() { - var asDynamic = GetDynamicNode(1178); + var asDynamic = GetDynamicNode(1176); Assert.IsNull(asDynamic.Next()); } @@ -637,7 +660,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsNotNull(result); - Assert.AreEqual((int) 1174, (int) result.Id); + Assert.AreEqual((int)1178, (int)result.Id); } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2e60f11c12..50b8f7d39b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Web; using NUnit.Framework; @@ -36,25 +37,26 @@ namespace Umbraco.Tests.PublishedContent 1 - + This is some content]]> - + - - + + + + + 1 - - - + - + @@ -85,7 +87,123 @@ namespace Umbraco.Tests.PublishedContent return doc; } - [Test] + [Test] + public void Is_Last_From_Where_Filter_Dynamic_Linq() + { + var doc = GetNode(1173); + + foreach (var d in doc.Children.Where("Visible")) + { + if (d.Id != 1178) + { + Assert.IsFalse(d.IsLast()); + } + else + { + Assert.IsTrue(d.IsLast()); + } + } + } + + [Test] + public void Is_Last_From_Where_Filter() + { + var doc = GetNode(1173); + + foreach (var d in doc.Children.Where(x => x.IsVisible())) + { + if (d.Id != 1178) + { + Assert.IsFalse(d.IsLast()); + } + else + { + Assert.IsTrue(d.IsLast()); + } + } + } + + [Test] + public void Is_Last_From_Take() + { + var doc = GetNode(1173); + + foreach (var d in doc.Children.Take(3)) + { + if (d.Id != 1178) + { + Assert.IsFalse(d.IsLast()); + } + else + { + Assert.IsTrue(d.IsLast()); + } + } + } + + [Test] + public void Is_Last_From_Skip() + { + var doc = GetNode(1173); + + foreach (var d in doc.Children.Skip(1)) + { + if (d.Id != 1176) + { + Assert.IsFalse(d.IsLast()); + } + else + { + Assert.IsTrue(d.IsLast()); + } + } + } + + [Test] + public void Is_Last_From_Concat() + { + var doc = GetNode(1173); + + + foreach (var d in doc.Children.Concat(new[] { GetNode(1175), GetNode(4444) })) + { + if (d.Id != 4444) + { + Assert.IsFalse(d.IsLast()); + } + else + { + Assert.IsTrue(d.IsLast()); + } + } + } + + [Test] + public void Descendants_Ordered_Properly() + { + var doc = GetNode(1046); + + var currentLevel = 0; + var lastSortOrder = 0; + var levelChangesAt = new[] { 1046, 1173, 1174 }; + + foreach (var d in doc.DescendantsOrSelf()) + { + if (levelChangesAt.Contains(d.Id)) + { + Assert.Greater(d.Level, currentLevel); + currentLevel = d.Level; + } + else + { + Assert.AreEqual(currentLevel, d.Level); + Assert.Greater(d.SortOrder, lastSortOrder); + } + lastSortOrder = d.SortOrder; + } + } + + [Test] public void Test_Get_Recursive_Val() { var doc = GetNode(1174); @@ -128,11 +246,11 @@ namespace Umbraco.Tests.PublishedContent var doc = GetNode(1173); Assert.AreEqual(0, doc.Index()); doc = GetNode(1176); - Assert.AreEqual(1, doc.Index()); - doc = GetNode(1177); - Assert.AreEqual(2, doc.Index()); - doc = GetNode(1178); Assert.AreEqual(3, doc.Index()); + doc = GetNode(1177); + Assert.AreEqual(1, doc.Index()); + doc = GetNode(1178); + Assert.AreEqual(2, doc.Index()); } [Test] @@ -363,7 +481,7 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Next_Without_Sibling() { - var doc = GetNode(1178); + var doc = GetNode(1176); Assert.IsNull(doc.Next()); } @@ -385,7 +503,7 @@ namespace Umbraco.Tests.PublishedContent Assert.IsNotNull(result); - Assert.AreEqual((int)1174, (int)result.Id); + Assert.AreEqual((int)1178, (int)result.Id); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/PublishedContent/StronglyTypedQueryTests.cs b/src/Umbraco.Tests/PublishedContent/StronglyTypedQueryTests.cs index ea28854c1e..40371a653a 100644 --- a/src/Umbraco.Tests/PublishedContent/StronglyTypedQueryTests.cs +++ b/src/Umbraco.Tests/PublishedContent/StronglyTypedQueryTests.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; using Umbraco.Web; +using Umbraco.Web.Models; namespace Umbraco.Tests.PublishedContent { @@ -214,7 +215,7 @@ namespace Umbraco.Tests.PublishedContent } } - public class PublishedContentWrapper : IPublishedContent + public class PublishedContentWrapper : IPublishedContent, IOwnerCollectionAware { protected IPublishedContent WrappedContent { get; private set; } @@ -320,6 +321,46 @@ namespace Umbraco.Tests.PublishedContent { return WrappedContent.GetProperty(alias); } + + private IEnumerable _ownersCollection; + + /// + /// Need to get/set the owner collection when an item is returned from the result set of a query + /// + /// + /// Based on this issue here: http://issues.umbraco.org/issue/U4-1797 + /// + IEnumerable IOwnerCollectionAware.OwnersCollection + { + get + { + var publishedContentBase = WrappedContent as IOwnerCollectionAware; + if (publishedContentBase != null) + { + return publishedContentBase.OwnersCollection; + } + + //if the owners collection is null, we'll default to it's siblings + if (_ownersCollection == null) + { + //get the root docs if parent is null + _ownersCollection = this.Siblings(); + } + return _ownersCollection; + } + set + { + var publishedContentBase = WrappedContent as IOwnerCollectionAware; + if (publishedContentBase != null) + { + publishedContentBase.OwnersCollection = value; + } + else + { + _ownersCollection = value; + } + } + } } public partial class HomeContentItem : ContentPageContentItem diff --git a/src/Umbraco.Web/Dynamics/Grouping.cs b/src/Umbraco.Web/Dynamics/Grouping.cs index e36ce9167f..4675b16e1b 100644 --- a/src/Umbraco.Web/Dynamics/Grouping.cs +++ b/src/Umbraco.Web/Dynamics/Grouping.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Dynamics internal class Grouping : IGrouping where T : DynamicObject { public K Key { get; set; } - public IEnumerable Elements; + public IEnumerable Elements { get; set; } public IEnumerator GetEnumerator() { diff --git a/src/Umbraco.Web/Models/DynamicPublishedContent.cs b/src/Umbraco.Web/Models/DynamicPublishedContent.cs index 5ba8a169e3..86a6972fba 100644 --- a/src/Umbraco.Web/Models/DynamicPublishedContent.cs +++ b/src/Umbraco.Web/Models/DynamicPublishedContent.cs @@ -20,9 +20,9 @@ namespace Umbraco.Web.Models /// /// The base dynamic model for views /// - public class DynamicPublishedContent : DynamicObject, IPublishedContent + public class DynamicPublishedContent : DynamicObject, IPublishedContent, IOwnerCollectionAware { - protected IPublishedContent PublishedContent { get; private set; } + protected internal IPublishedContent PublishedContent { get; private set; } private DynamicPublishedContentList _cachedChildren; private readonly ConcurrentDictionary _cachedMemberOutput = new ConcurrentDictionary(); @@ -36,6 +36,46 @@ namespace Umbraco.Web.Models #endregion + private IEnumerable _ownersCollection; + + /// + /// Need to get/set the owner collection when an item is returned from the result set of a query + /// + /// + /// Based on this issue here: http://issues.umbraco.org/issue/U4-1797 + /// + IEnumerable IOwnerCollectionAware.OwnersCollection + { + get + { + var publishedContentBase = PublishedContent as IOwnerCollectionAware; + if (publishedContentBase != null) + { + return publishedContentBase.OwnersCollection; + } + + //if the owners collection is null, we'll default to it's siblings + if (_ownersCollection == null) + { + //get the root docs if parent is null + _ownersCollection = this.Siblings(); + } + return _ownersCollection; + } + set + { + var publishedContentBase = PublishedContent as IOwnerCollectionAware; + if (publishedContentBase != null) + { + publishedContentBase.OwnersCollection = value; + } + else + { + _ownersCollection = value; + } + } + } + public dynamic AsDynamic() { return this; diff --git a/src/Umbraco.Web/Models/DynamicPublishedContentList.cs b/src/Umbraco.Web/Models/DynamicPublishedContentList.cs index bc24b99aca..3b323b004a 100644 --- a/src/Umbraco.Web/Models/DynamicPublishedContentList.cs +++ b/src/Umbraco.Web/Models/DynamicPublishedContentList.cs @@ -11,10 +11,19 @@ using Umbraco.Web.Dynamics; namespace Umbraco.Web.Models { - public class DynamicPublishedContentList : DynamicObject, IEnumerable + /// + /// A collection of DynamicPublishedContent items + /// + /// + /// Implements many of the dynamic methods required for execution against this list. It also ensures + /// that the correct OwnersCollection properties is assigned to the underlying PublishedContentBase object + /// of the DynamicPublishedContent item (so long as the IPublishedContent item is actually PublishedContentBase). + /// All relates to this issue here: http://issues.umbraco.org/issue/U4-1797 + /// + public class DynamicPublishedContentList : DynamicObject, IEnumerable, IEnumerable { internal List Items { get; set; } - + public DynamicPublishedContentList() { Items = new List(); @@ -22,14 +31,28 @@ namespace Umbraco.Web.Models public DynamicPublishedContentList(IEnumerable items) { var list = items.ToList(); + //set the owners list for each item + list.ForEach(x => SetOwnersList(x, this)); Items = list; } public DynamicPublishedContentList(IEnumerable items) { var list = items.Select(x => new DynamicPublishedContent(x)).ToList(); + //set the owners list for each item + list.ForEach(x => SetOwnersList(x, this)); Items = list; } + + private static void SetOwnersList(IPublishedContent content, IEnumerable list) + { + var publishedContentBase = content as IOwnerCollectionAware; + if (publishedContentBase != null) + { + publishedContentBase.OwnersCollection = list; + } + } + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { int index = (int)indexes[0]; @@ -134,12 +157,12 @@ namespace Umbraco.Web.Models } if (name == "Take") { - result = new DynamicPublishedContentList(this.Take((int)firstArg)); + result = new DynamicPublishedContentList(this.Take((int)firstArg)); return true; } if (name == "Skip") { - result = new DynamicPublishedContentList(this.Skip((int)firstArg)); + result = new DynamicPublishedContentList(this.Skip((int)firstArg)); return true; } if (name == "InGroupsOf") @@ -483,14 +506,26 @@ namespace Umbraco.Web.Models return DynamicQueryable.Select(Items.AsQueryable(), predicate, values); } + /// + /// Allows the adding of an item from the collection + /// + /// public void Add(DynamicPublishedContent publishedContent) { + SetOwnersList(publishedContent, this); this.Items.Add(publishedContent); } + + /// + /// Allows the removal of an item from the collection + /// + /// public void Remove(DynamicPublishedContent publishedContent) { if (this.Items.Contains(publishedContent)) { + //set owners list to null + SetOwnersList(publishedContent, null); this.Items.Remove(publishedContent); } } @@ -503,7 +538,12 @@ namespace Umbraco.Web.Models return true; } - public IEnumerator GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerator GetEnumerator() { return Items.GetEnumerator(); } diff --git a/src/Umbraco.Web/Models/IOwnerCollectionAware.cs b/src/Umbraco.Web/Models/IOwnerCollectionAware.cs new file mode 100644 index 0000000000..a224f5c227 --- /dev/null +++ b/src/Umbraco.Web/Models/IOwnerCollectionAware.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Umbraco.Web.Models +{ + /// + /// An interface describing that the object should be aware of it's containing collection + /// + internal interface IOwnerCollectionAware + { + IEnumerable OwnersCollection { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs index 8ce6f65ac4..bb45ac12c4 100644 --- a/src/Umbraco.Web/Models/PublishedContentBase.cs +++ b/src/Umbraco.Web/Models/PublishedContentBase.cs @@ -9,17 +9,42 @@ using Umbraco.Web.Templates; namespace Umbraco.Web.Models { - - /// + /// /// An abstract base class to use for IPublishedContent which ensures that the Url and Indexed property return values /// are consistently returned. /// - public abstract class PublishedContentBase : IPublishedContent + /// + /// This also ensures that we have an OwnersCollection property so that the IsFirst/IsLast/Index helper methods work + /// when referenced inside the result of a collection. http://issues.umbraco.org/issue/U4-1797 + /// + public abstract class PublishedContentBase : IPublishedContent, IOwnerCollectionAware { private string _url; private readonly Dictionary _resolvePropertyValues = new Dictionary(); + private IEnumerable _ownersCollection; - /// + /// + /// Need to get/set the owner collection when an item is returned from the result set of a query + /// + /// + /// Based on this issue here: http://issues.umbraco.org/issue/U4-1797 + /// + IEnumerable IOwnerCollectionAware.OwnersCollection + { + get + { + //if the owners collection is null, we'll default to it's siblings + if (_ownersCollection == null) + { + //get the root docs if parent is null + _ownersCollection = this.Siblings(); + } + return _ownersCollection; + } + set { _ownersCollection = value; } + } + + /// /// Returns the Url for this content item /// /// @@ -98,5 +123,6 @@ namespace Umbraco.Web.Models public abstract IPublishedContentProperty GetProperty(string alias); public abstract IPublishedContent Parent { get; } public abstract IEnumerable Children { get; } - } + + } } diff --git a/src/Umbraco.Web/Models/XmlPublishedContent.cs b/src/Umbraco.Web/Models/XmlPublishedContent.cs index 18bfbe8115..867fc8c144 100644 --- a/src/Umbraco.Web/Models/XmlPublishedContent.cs +++ b/src/Umbraco.Web/Models/XmlPublishedContent.cs @@ -72,7 +72,7 @@ namespace Umbraco.Web.Models { if (!_initialized) Initialize(); - return _children; + return _children.OrderBy(x => x.SortOrder); } } diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 58cff517d2..fff51929aa 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; @@ -272,9 +273,138 @@ namespace Umbraco.Web } #endregion - #region List Extensions + + #region Linq Wrapping Extensions - public static IQueryable OrderBy(this IEnumerable list, string predicate) + //NOTE: These are all purely required to fix this issue: http://issues.umbraco.org/issue/U4-1797 which requires that any + // content item knows about it's containing collection. + + public static IEnumerable Where(this IEnumerable source, Func predicate) + { + var internalResult = Enumerable.Where(source, predicate); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Where(this IEnumerable source, Func predicate) + { + var internalResult = Enumerable.Where(source, predicate); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Take(this IEnumerable source, int count) + { + var internalResult = Enumerable.Take(source, count); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable TakeWhile(this IEnumerable source, Func predicate) + { + var internalResult = Enumerable.TakeWhile(source, predicate); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable TakeWhile(this IEnumerable source, Func predicate) + { + var internalResult = Enumerable.TakeWhile(source, predicate); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Skip(this IEnumerable source, int count) + { + var internalResult = Enumerable.Skip(source, count); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable SkipWhile(this IEnumerable source, Func predicate) + { + var internalResult = Enumerable.SkipWhile(source, predicate); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable SkipWhile(this IEnumerable source, Func predicate) + { + var internalResult = Enumerable.SkipWhile(source, predicate); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Concat(this IEnumerable first, IEnumerable second) + { + var internalResult = Enumerable.Concat(first, second); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Distinct(this IEnumerable source) + { + var internalResult = Enumerable.Distinct(source); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Distinct(this IEnumerable source, IEqualityComparer comparer) + { + var internalResult = Enumerable.Distinct(source, comparer); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Union(this IEnumerable first, IEnumerable second) + { + var internalResult = Enumerable.Union(first, second); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Union(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + { + var internalResult = Enumerable.Union(first, second, comparer); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Intersect(this IEnumerable first, IEnumerable second) + { + var internalResult = Enumerable.Intersect(first, second); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Intersect(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + { + var internalResult = Enumerable.Intersect(first, second, comparer); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Except(this IEnumerable first, IEnumerable second) + { + var internalResult = Enumerable.Except(first, second); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Except(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + { + var internalResult = Enumerable.Except(first, second, comparer); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable Reverse(this IEnumerable source) + { + var internalResult = Enumerable.Reverse(source); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable DefaultIfEmpty(this IEnumerable source) + { + var internalResult = Enumerable.DefaultIfEmpty(source); + return new DynamicPublishedContentList(internalResult); + } + + public static IEnumerable DefaultIfEmpty(this IEnumerable source, IPublishedContent defaultValue) + { + var internalResult = Enumerable.DefaultIfEmpty(source, defaultValue); + return new DynamicPublishedContentList(internalResult); + } + + #endregion + + + #region Dynamic Linq Extensions + + public static IQueryable OrderBy(this IEnumerable list, string predicate) { var dList = new DynamicPublishedContentList(list); return dList.OrderBy(predicate); @@ -283,7 +413,11 @@ namespace Umbraco.Web public static IQueryable Where(this IEnumerable list, string predicate) { var dList = new DynamicPublishedContentList(list); - return dList.Where(predicate); + //we have to wrap the result in another DynamicPublishedContentList so that the OwnersList get's set on + //the individual items. See: http://issues.umbraco.org/issue/U4-1797 + return new DynamicPublishedContentList( + dList.Where(predicate)) + .AsQueryable(); } public static IEnumerable> GroupBy(this IEnumerable list, string predicate) @@ -336,14 +470,14 @@ namespace Umbraco.Web } return new HtmlString(valueIfFalse); } - public static bool Where(this IPublishedContent doc, string predicate) + + public static bool Where(this IPublishedContent doc, string predicate) { if (doc == null) throw new ArgumentNullException("doc"); - //Totally gonna cheat here var dynamicDocumentList = new DynamicPublishedContentList(); dynamicDocumentList.Add(doc.AsDynamicPublishedContent()); var filtered = dynamicDocumentList.Where(predicate); - if (Queryable.Count(filtered) == 1) + if (filtered.Count() == 1) { //this node matches the predicate return true; @@ -359,13 +493,8 @@ namespace Umbraco.Web return content.Index(); } public static int Index(this IPublishedContent content) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; - - var container = ownersList.ToList(); + { + var container = content.GetOwnersList().ToList(); int currentIndex = container.FindIndex(n => n.Id == content.Id); if (currentIndex != -1) { @@ -376,6 +505,30 @@ namespace Umbraco.Web throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicDocumentList but could not retrieve the index for it's position in the list", content.Id)); } } + + /// + /// Return the owners collection of the current content item. + /// + /// + /// + /// + /// If the content item is of type PublishedContentBase we will have a property called OwnersCollection which will + /// be the collection of a resultant set (i.e. from a where clause, a call to Children(), etc...) otherwise it will + /// be the item's siblings. All relates to this issue: http://issues.umbraco.org/issue/U4-1797 + /// + private static IEnumerable GetOwnersList(this IPublishedContent content) + { + //Here we need to type check, we need to see if we have a real OwnersCollection list based on the result set + // of a query, otherwise we can only lookup among the item's siblings. All related to this issue here: + // http://issues.umbraco.org/issue/U4-1797 + + var publishedContentBase = content as IOwnerCollectionAware; + var ownersList = publishedContentBase != null + ? publishedContentBase.OwnersCollection + : content.Siblings(); + return ownersList; + } + #endregion #region Is Helpers @@ -394,159 +547,145 @@ namespace Umbraco.Web public static bool IsNull(this IPublishedContent content, string alias) { return content.IsNull(alias, false); - } - public static bool IsFirst(this IPublishedContent content) - { - return content.IsHelper(n => n.Index() == 0); - } - public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue) - { - return content.IsHelper(n => n.Index() == 0, valueIfTrue); - } - public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() == 0, valueIfTrue, valueIfFalse); - } - public static bool IsNotFirst(this IPublishedContent content) - { - return !content.IsHelper(n => n.Index() == 0); - } - public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue) - { - return content.IsHelper(n => n.Index() != 0, valueIfTrue); - } - public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() != 0, valueIfTrue, valueIfFalse); - } - public static bool IsPosition(this IPublishedContent content, int index) - { - return content.IsHelper(n => n.Index() == index); - } - public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue) - { - return content.IsHelper(n => n.Index() == index, valueIfTrue); - } - public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() == index, valueIfTrue, valueIfFalse); - } - public static bool IsModZero(this IPublishedContent content, int modulus) - { - return content.IsHelper(n => n.Index() % modulus == 0); - } - public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue) - { - return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue); - } - public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue, valueIfFalse); - } + } - public static bool IsNotModZero(this IPublishedContent content, int modulus) - { - return content.IsHelper(n => n.Index() % modulus != 0); - } - public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue) - { - return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue); - } - public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue, valueIfFalse); - } - public static bool IsNotPosition(this IPublishedContent content, int index) - { - return !content.IsHelper(n => n.Index() == index); - } - public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue) - { - return content.IsHelper(n => n.Index() != index, valueIfTrue); - } - public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() != index, valueIfTrue, valueIfFalse); - } - public static bool IsLast(this IPublishedContent content) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; - var count = ownersList.Count(); - return content.IsHelper(n => n.Index() == count - 1); - } - public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; - var count = ownersList.Count(); - return content.IsHelper(n => n.Index() == count - 1, valueIfTrue); - } - public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; - var count = ownersList.Count(); - return content.IsHelper(n => n.Index() == count - 1, valueIfTrue, valueIfFalse); - } - public static bool IsNotLast(this IPublishedContent content) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; - var count = ownersList.Count(); - return !content.IsHelper(n => n.Index() == count - 1); - } - public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; - var count = ownersList.Count(); - return content.IsHelper(n => n.Index() != count - 1, valueIfTrue); - } - public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; - var count = ownersList.Count(); - return content.IsHelper(n => n.Index() != count - 1, valueIfTrue, valueIfFalse); - } - public static bool IsEven(this IPublishedContent content) - { - return content.IsHelper(n => n.Index() % 2 == 0); - } - public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue) - { - return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue); - } - public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue, valueIfFalse); - } - public static bool IsOdd(this IPublishedContent content) - { - return content.IsHelper(n => n.Index() % 2 == 1); - } - public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue) - { - return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue); - } - public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue, string valueIfFalse) - { - return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue, valueIfFalse); - } - public static bool IsEqual(this IPublishedContent content, IPublishedContent other) + #region Position in list + + public static bool IsFirst(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() == 0); + } + public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() == 0, valueIfTrue); + } + public static HtmlString IsFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() == 0, valueIfTrue, valueIfFalse); + } + public static bool IsNotFirst(this IPublishedContent content) + { + return !content.IsHelper(n => n.Index() == 0); + } + public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() != 0, valueIfTrue); + } + public static HtmlString IsNotFirst(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() != 0, valueIfTrue, valueIfFalse); + } + public static bool IsPosition(this IPublishedContent content, int index) + { + return content.IsHelper(n => n.Index() == index); + } + public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue) + { + return content.IsHelper(n => n.Index() == index, valueIfTrue); + } + public static HtmlString IsPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() == index, valueIfTrue, valueIfFalse); + } + public static bool IsModZero(this IPublishedContent content, int modulus) + { + return content.IsHelper(n => n.Index() % modulus == 0); + } + public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue); + } + public static HtmlString IsModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % modulus == 0, valueIfTrue, valueIfFalse); + } + public static bool IsNotModZero(this IPublishedContent content, int modulus) + { + return content.IsHelper(n => n.Index() % modulus != 0); + } + public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue); + } + public static HtmlString IsNotModZero(this IPublishedContent content, int modulus, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % modulus != 0, valueIfTrue, valueIfFalse); + } + public static bool IsNotPosition(this IPublishedContent content, int index) + { + return !content.IsHelper(n => n.Index() == index); + } + public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue) + { + return content.IsHelper(n => n.Index() != index, valueIfTrue); + } + public static HtmlString IsNotPosition(this IPublishedContent content, int index, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() != index, valueIfTrue, valueIfFalse); + } + public static bool IsLast(this IPublishedContent content) + { + var ownersList = content.GetOwnersList(); + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1); + } + public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue) + { + var ownersList = content.GetOwnersList(); + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1, valueIfTrue); + } + public static HtmlString IsLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + var ownersList = content.GetOwnersList(); + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() == count - 1, valueIfTrue, valueIfFalse); + } + public static bool IsNotLast(this IPublishedContent content) + { + var ownersList = content.GetOwnersList(); + var count = ownersList.Count(); + return !content.IsHelper(n => n.Index() == count - 1); + } + public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue) + { + var ownersList = content.GetOwnersList(); + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() != count - 1, valueIfTrue); + } + public static HtmlString IsNotLast(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + var ownersList = content.GetOwnersList(); + var count = ownersList.Count(); + return content.IsHelper(n => n.Index() != count - 1, valueIfTrue, valueIfFalse); + } + public static bool IsEven(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() % 2 == 0); + } + public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue); + } + public static HtmlString IsEven(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % 2 == 0, valueIfTrue, valueIfFalse); + } + public static bool IsOdd(this IPublishedContent content) + { + return content.IsHelper(n => n.Index() % 2 == 1); + } + public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue) + { + return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue); + } + public static HtmlString IsOdd(this IPublishedContent content, string valueIfTrue, string valueIfFalse) + { + return content.IsHelper(n => n.Index() % 2 == 1, valueIfTrue, valueIfFalse); + } + #endregion + + public static bool IsEqual(this IPublishedContent content, IPublishedContent other) { return content.IsHelper(n => n.Id == other.Id); } @@ -782,7 +921,10 @@ namespace Umbraco.Web } private static IEnumerable Descendants(this IPublishedContent content, Func func) { - return content.Children.Map(func, (IPublishedContent n) => n.Children); + //return content.Children.Map(func, (IPublishedContent n) => n.Children); + return content.Children.FlattenList(x => x.Children).Where(func) + .OrderBy(x => x.Level) //ensure its sorted by level and then by sort order + .ThenBy(x => x.SortOrder); } public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level) { @@ -805,8 +947,13 @@ namespace Umbraco.Web { thisNode.Add(content); } - var flattenedNodes = content.Children.Map(func, (IPublishedContent n) => n.Children); - return thisNode.Concat(flattenedNodes).ToList().ConvertAll(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem)); + //var flattenedNodes = content.Children.Map(func, (IPublishedContent n) => n.Children); + var flattenedNodes = content.Children.FlattenList(n => n.Children).Where(func); + + return thisNode.Concat(flattenedNodes) + .Select(dynamicBackingItem => new DynamicPublishedContent(dynamicBackingItem)) + .OrderBy(x => x.Level) //ensure its sorted by level and then by sort order + .ThenBy(x => x.SortOrder); } return Enumerable.Empty(); } @@ -871,10 +1018,7 @@ namespace Umbraco.Web } public static IPublishedContent Next(this IPublishedContent content, int number) { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; + var ownersList = content.GetOwnersList(); var container = ownersList.ToList(); var currentIndex = container.FindIndex(n => n.Id == content.Id); @@ -887,10 +1031,7 @@ namespace Umbraco.Web public static IPublishedContent Next(this IPublishedContent content, string nodeTypeAlias) { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; + var ownersList = content.GetOwnersList(); var container = ownersList.ToList(); var currentIndex = container.FindIndex(n => n.Id == content.Id); @@ -909,10 +1050,7 @@ namespace Umbraco.Web } public static IPublishedContent Previous(this IPublishedContent content, int number) { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; + var ownersList = content.GetOwnersList(); var container = ownersList.ToList(); var currentIndex = container.FindIndex(n => n.Id == content.Id); @@ -924,10 +1062,7 @@ namespace Umbraco.Web } public static IPublishedContent Previous(this IPublishedContent content, string nodeTypeAlias) { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; + var ownersList = content.GetOwnersList(); var container = ownersList.ToList(); int currentIndex = container.FindIndex(n => n.Id == content.Id); @@ -945,12 +1080,9 @@ namespace Umbraco.Web } public static IPublishedContent Sibling(this IPublishedContent content, int number) { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; + var siblings = content.Siblings(); - var container = ownersList.ToList(); + var container = siblings.ToList(); var currentIndex = container.FindIndex(n => n.Id == content.Id); if (currentIndex != -1) { @@ -959,13 +1091,10 @@ namespace Umbraco.Web throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); } public static IPublishedContent Sibling(this IPublishedContent content, string nodeTypeAlias) - { - //get the root docs if parent is null - var ownersList = content.Parent == null - ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) - : content.Parent.Children; + { + var siblings = content.Siblings(); - var container = ownersList.ToList(); + var container = siblings.ToList(); var currentIndex = container.FindIndex(n => n.Id == content.Id); if (currentIndex != -1) { @@ -987,7 +1116,21 @@ namespace Umbraco.Web } throw new IndexOutOfRangeException(string.Format("Node {0} belongs to a DynamicNodeList but could not retrieve the index for it's position in the list", content.Id)); } - #endregion + + /// + /// Return the items siblings + /// + /// + /// + public static IEnumerable Siblings(this IPublishedContent content) + { + //get the root docs if parent is null + return content.Parent == null + ? PublishedContentStoreResolver.Current.PublishedContentStore.GetRootDocuments(UmbracoContext.Current) + : content.Parent.Children; + } + + #endregion /// /// Method to return the Children of the content item @@ -999,7 +1142,7 @@ namespace Umbraco.Web /// public static IEnumerable Children(this IPublishedContent p) { - return p.Children; + return p.Children.OrderBy(x => x.SortOrder); } /// @@ -1043,7 +1186,7 @@ namespace Umbraco.Web //create all row data var tableData = Umbraco.Core.DataTableExtensions.CreateTableData(); //loop through each child and create row data for it - foreach (var n in node.Children) + foreach (var n in node.Children.OrderBy(x => x.SortOrder)) { if (!nodeTypeAliasFilter.IsNullOrWhiteSpace()) { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a180dc9889..f022459ee0 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -304,6 +304,7 @@ + diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs index f5ed186acc..ad7dca8cc8 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -1025,8 +1025,9 @@ namespace umbraco.MacroEngines } public DynamicNodeList Descendants(Func func) { - var flattenedNodes = this.n.ChildrenAsList.Map(func, (DynamicBackingItem n) => { return n.ChildrenAsList; }); - return new DynamicNodeList(flattenedNodes.ToList().ConvertAll(DynamicBackingItem => new DynamicNode(DynamicBackingItem))); + //var flattenedNodes = this.n.ChildrenAsList.Map(func, (DynamicBackingItem n) => { return n.ChildrenAsList; }); + var flattenedNodes = this.n.ChildrenAsList.FlattenList(item => item.ChildrenAsList).Where(func); + return new DynamicNodeList(flattenedNodes.Select(dynamicBackingItem => new DynamicNode(dynamicBackingItem))); } public DynamicNodeList DescendantsOrSelf(int level) { @@ -1049,8 +1050,9 @@ namespace umbraco.MacroEngines { thisNode.Add(this.n); } - var flattenedNodes = this.n.ChildrenAsList.Map(func, (DynamicBackingItem n) => { return n.ChildrenAsList; }); - return new DynamicNodeList(thisNode.Concat(flattenedNodes).ToList().ConvertAll(DynamicBackingItem => new DynamicNode(DynamicBackingItem))); + //var flattenedNodes = this.n.ChildrenAsList.Map(func, (DynamicBackingItem n) => { return n.ChildrenAsList; }); + var flattenedNodes = this.n.ChildrenAsList.FlattenList(item => item.ChildrenAsList).Where(func); + return new DynamicNodeList(thisNode.Concat(flattenedNodes).Select(dynamicBackingItem => new DynamicNode(dynamicBackingItem))); } return new DynamicNodeList(new List()); } diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/ExtensionMethods.cs b/src/umbraco.MacroEngines/RazorDynamicNode/ExtensionMethods.cs index df08e906e1..276b71354e 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/ExtensionMethods.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/ExtensionMethods.cs @@ -3,18 +3,20 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; +using Umbraco.Core; namespace umbraco.MacroEngines { public static class ExtensionMethods { - [Obsolete("This has been superceded by Umbraco.Core.Dynamics.ExtensionMethods.Map method")] + [Obsolete("This has been superceded by Umbraco.Core.EnumerableExtensions.FlattenList method")] public static IEnumerable Map( this IEnumerable source, Func selectorFunction, Func> getChildrenFunction) { - return Umbraco.Core.Dynamics.ExtensionMethods.Map(source, selectorFunction, getChildrenFunction); + //return Umbraco.Core.Dynamics.ExtensionMethods.Map(source, selectorFunction, getChildrenFunction); + return source.FlattenList(getChildrenFunction).Where(selectorFunction); } public static DynamicNodeList Random(this DynamicNodeList all, int Min, int Max)