diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index ab576171f4..de641a99a2 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -10,6 +10,7 @@ using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Core.Composing; using Moq; +using Newtonsoft.Json; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -32,6 +33,8 @@ namespace Umbraco.Tests.PublishedContent protected override void Compose() { base.Compose(); + _publishedSnapshotAccessorMock = new Mock(); + Composition.RegisterUnique(_publishedSnapshotAccessorMock.Object); Composition.RegisterUnique(f => new PublishedModelFactory(f.GetInstance().GetTypes())); Composition.RegisterUnique(); @@ -87,6 +90,7 @@ namespace Umbraco.Tests.PublishedContent } private readonly Guid _node1173Guid = Guid.NewGuid(); + private Mock _publishedSnapshotAccessorMock; protected override string GetXmlContent(int templateId) { @@ -792,6 +796,91 @@ namespace Umbraco.Tests.PublishedContent Assert.IsTrue(customDoc3.IsDescendantOrSelf(customDoc3)); } + [Test] + public void SiblingsAndSelf() + { + // Structure: + // - Root : 1046 (no parent) + // -- Level1.1: 1173 (parent 1046) + // --- Level1.1.1: 1174 (parent 1173) + // --- Level1.1.2: 117 (parent 1173) + // --- Level1.1.3: 1177 (parent 1173) + // --- Level1.1.4: 1178 (parent 1173) + // --- Level1.1.5: 1176 (parent 1173) + // -- Level1.2: 1175 (parent 1046) + // -- Level1.3: 4444 (parent 1046) + var root = GetNode(1046); + var level1_1 = GetNode(1173); + var level1_1_1 = GetNode(1174); + var level1_1_2 = GetNode(117); + var level1_1_3 = GetNode(1177); + var level1_1_4 = GetNode(1178); + var level1_1_5 = GetNode(1176); + var level1_2 = GetNode(1175); + var level1_3 = GetNode(4444); + + _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root}); + + CollectionAssertAreEqual(new []{root}, root.SiblingsAndSelf()); + + CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_1.SiblingsAndSelf()); + CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_2.SiblingsAndSelf()); + CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_3.SiblingsAndSelf()); + + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.SiblingsAndSelf()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.SiblingsAndSelf()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_3.SiblingsAndSelf()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_4.SiblingsAndSelf()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_5.SiblingsAndSelf()); + + } + + [Test] + public void Siblings() + { + // Structure: + // - Root : 1046 (no parent) + // -- Level1.1: 1173 (parent 1046) + // --- Level1.1.1: 1174 (parent 1173) + // --- Level1.1.2: 117 (parent 1173) + // --- Level1.1.3: 1177 (parent 1173) + // --- Level1.1.4: 1178 (parent 1173) + // --- Level1.1.5: 1176 (parent 1173) + // -- Level1.2: 1175 (parent 1046) + // -- Level1.3: 4444 (parent 1046) + var root = GetNode(1046); + var level1_1 = GetNode(1173); + var level1_1_1 = GetNode(1174); + var level1_1_2 = GetNode(117); + var level1_1_3 = GetNode(1177); + var level1_1_4 = GetNode(1178); + var level1_1_5 = GetNode(1176); + var level1_2 = GetNode(1175); + var level1_3 = GetNode(4444); + + _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root}); + + CollectionAssertAreEqual(new IPublishedContent[0], root.Siblings()); + + CollectionAssertAreEqual( new []{level1_2, level1_3}, level1_1.Siblings()); + CollectionAssertAreEqual( new []{level1_1, level1_3}, level1_2.Siblings()); + CollectionAssertAreEqual( new []{level1_1, level1_2}, level1_3.Siblings()); + + CollectionAssertAreEqual( new []{ level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.Siblings()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.Siblings()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_4, level1_1_5}, level1_1_3.Siblings()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_5}, level1_1_4.Siblings()); + CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4}, level1_1_5.Siblings()); + + } + + private void CollectionAssertAreEqual(IEnumerable expected, IEnumerable actual) + where T: IPublishedContent + { + var e = expected.Select(x => x.Id); + var a = actual.Select(x => x.Id); + CollectionAssert.AreEquivalent(e, a, $"\nExpected:\n{string.Join(", ", e)}\n\nActual:\n{string.Join(", ", a)}"); + } [Test] public void FragmentProperty() diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 8af2a933f2..bba58dfae5 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1120,15 +1120,60 @@ namespace Umbraco.Web #endregion - #region Axes: Parent Children (siblings including self) + #region Axes: Siblings /// - /// Gets the children of the parent of the content. + /// Gets the siblings of the content. /// /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) - /// The children of the parent of the content. - public static IEnumerable ParentChildren(this IPublishedContent content, string culture = null) + /// The siblings of the content. + /// + /// Note that in V7 this method also return the content node self. + /// + public static IEnumerable Siblings(this IPublishedContent content, string culture = null) + { + return SiblingsAndSelf(content, culture).Where(x => x.Id != content.Id); + } + + /// + /// Gets the siblings of the content, of a given content type. + /// + /// The content. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The content type alias. + /// The siblings of the content, of the given content type. + /// + /// Note that in V7 this method also return the content node self. + /// + public static IEnumerable SiblingsOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) + { + return SiblingsAndSelfOfType(content, contentTypeAlias, culture).Where(x => x.Id != content.Id); + } + + /// + /// Gets the siblings of the content, of a given content type. + /// + /// The content type. + /// The content. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content, of the given content type. + /// + /// Note that in V7 this method also return the content node self. + /// + public static IEnumerable Siblings(this IPublishedContent content, string culture = null) + where T : class, IPublishedContent + { + return SiblingsAndSelf(content, culture).Where(x => x.Id != content.Id); + } + + /// + /// Gets the siblings of the content including the node itself to indicate the position. + /// + /// The content. + /// The specific culture to filter for. If null is used the current culture is used. (Default is null) + /// The siblings of the content including the node itself. + public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) { return content.Parent != null ? content.Parent.Children(culture) @@ -1136,13 +1181,13 @@ namespace Umbraco.Web } /// - /// Gets the children of the parent of the content, of a given content type. + /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. /// /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The content type alias. - /// The children of the parent of the content, of the given content type. - public static IEnumerable ParentChildrenOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) + /// The siblings of the content including the node itself, of the given content type. + public static IEnumerable SiblingsAndSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) { return content.Parent != null ? content.Parent.ChildrenOfType(contentTypeAlias, culture) @@ -1150,13 +1195,13 @@ namespace Umbraco.Web } /// - /// Gets the children of the parent of the content, of a given content type. + /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. /// /// The content type. /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) - /// The children of the parent of the content, of the given content type. - public static IEnumerable ParentChildren(this IPublishedContent content, string culture = null) + /// The siblings of the content including the node itself, of the given content type. + public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) where T : class, IPublishedContent { return content.Parent != null