diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs
index f220f307d6..921883b822 100644
--- a/src/Umbraco.Core/PublishedContentExtensions.cs
+++ b/src/Umbraco.Core/PublishedContentExtensions.cs
@@ -103,18 +103,26 @@ namespace Umbraco.Core
/// Gets the children of the content item.
///
/// The content item.
- /// The specific culture to get the url children for. If null is used the current culture is used (Default is null).
+ ///
+ /// The specific culture to get the url children for. Default is null which will use the current culture in
+ ///
///
/// Gets children that are available for the specified culture.
/// Children are sorted by their sortOrder.
- /// The '*' culture and supported and returns everything.
+ ///
+ /// For culture,
+ /// if null is used the current culture is used.
+ /// If an empty string is used only invariant children are returned.
+ /// If "*" is used all children are returned.
+ ///
+ ///
+ /// If a variant culture is specified or there is a current culture in the then the Children returned
+ /// will include both the variant children matching the culture AND the invariant children because the invariant children flow with the current culture.
+ /// However, if an empty string is specified only invariant children are returned.
+ ///
///
public static IEnumerable Children(this IPublishedContent content, string culture = null)
{
- // invariant has invariant value (whatever the requested culture)
- if (!content.ContentType.VariesByCulture() && culture != "*")
- culture = "";
-
// handle context culture for variant
if (culture == null)
culture = VariationContextAccessor?.VariationContext?.Culture ?? "";
diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs
index f3a520ead1..65736aabd4 100644
--- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs
@@ -151,117 +151,139 @@ namespace Umbraco.Tests.PublishedContent
Mock.Get(factory).Setup(x => x.GetInstance(typeof(IVariationContextAccessor))).Returns(_variationAccesor);
}
+ private IEnumerable GetNestedVariantKits()
+ {
+ var paths = new Dictionary { { -1, "-1" } };
+
+ //1x variant (root)
+ yield return CreateVariantKit(1, -1, 1, paths);
+
+ //1x invariant under root
+ yield return CreateInvariantKit(4, 1, 1, paths);
+
+ //1x variant under root
+ yield return CreateVariantKit(7, 1, 4, paths);
+
+ //2x mixed under invariant
+ yield return CreateVariantKit(10, 4, 1, paths);
+ yield return CreateInvariantKit(11, 4, 2, paths);
+
+ //2x mixed under variant
+ yield return CreateVariantKit(12, 7, 1, paths);
+ yield return CreateInvariantKit(13, 7, 2, paths);
+ }
+
private IEnumerable GetInvariantKits()
{
var paths = new Dictionary { { -1, "-1" } };
- ContentNodeKit CreateKit(int id, int parentId, int sortOrder)
+ yield return CreateInvariantKit(1, -1, 1, paths);
+ yield return CreateInvariantKit(2, -1, 2, paths);
+ yield return CreateInvariantKit(3, -1, 3, paths);
+
+ yield return CreateInvariantKit(4, 1, 1, paths);
+ yield return CreateInvariantKit(5, 1, 2, paths);
+ yield return CreateInvariantKit(6, 1, 3, paths);
+
+ yield return CreateInvariantKit(7, 2, 3, paths);
+ yield return CreateInvariantKit(8, 2, 2, paths);
+ yield return CreateInvariantKit(9, 2, 1, paths);
+
+ yield return CreateInvariantKit(10, 3, 1, paths);
+
+ yield return CreateInvariantKit(11, 4, 1, paths);
+ yield return CreateInvariantKit(12, 4, 2, paths);
+ }
+
+ private ContentNodeKit CreateInvariantKit(int id, int parentId, int sortOrder, Dictionary paths)
+ {
+ if (!paths.TryGetValue(parentId, out var parentPath))
+ throw new Exception("Unknown parent.");
+
+ var path = paths[id] = parentPath + "," + id;
+ var level = path.Count(x => x == ',');
+ var now = DateTime.Now;
+
+ return new ContentNodeKit
{
- if (!paths.TryGetValue(parentId, out var parentPath))
- throw new Exception("Unknown parent.");
-
- var path = paths[id] = parentPath + "," + id;
- var level = path.Count(x => x == ',');
- var now = DateTime.Now;
-
- return new ContentNodeKit
+ ContentTypeId = _contentTypeInvariant.Id,
+ Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0),
+ DraftData = null,
+ PublishedData = new ContentData
{
- ContentTypeId = _contentTypeInvariant.Id,
- Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0),
- DraftData = null,
- PublishedData = new ContentData
- {
- Name = "N" + id,
- Published = true,
- TemplateId = 0,
- VersionId = 1,
- VersionDate = now,
- WriterId = 0,
- Properties = new Dictionary(),
- CultureInfos = new Dictionary()
- }
- };
- }
-
- yield return CreateKit(1, -1, 1);
- yield return CreateKit(2, -1, 2);
- yield return CreateKit(3, -1, 3);
-
- yield return CreateKit(4, 1, 1);
- yield return CreateKit(5, 1, 2);
- yield return CreateKit(6, 1, 3);
-
- yield return CreateKit(7, 2, 3);
- yield return CreateKit(8, 2, 2);
- yield return CreateKit(9, 2, 1);
-
- yield return CreateKit(10, 3, 1);
-
- yield return CreateKit(11, 4, 1);
- yield return CreateKit(12, 4, 2);
+ Name = "N" + id,
+ Published = true,
+ TemplateId = 0,
+ VersionId = 1,
+ VersionDate = now,
+ WriterId = 0,
+ Properties = new Dictionary(),
+ CultureInfos = new Dictionary()
+ }
+ };
}
private IEnumerable GetVariantKits()
{
var paths = new Dictionary { { -1, "-1" } };
- Dictionary GetCultureInfos(int id, DateTime now)
+ yield return CreateVariantKit(1, -1, 1, paths);
+ yield return CreateVariantKit(2, -1, 2, paths);
+ yield return CreateVariantKit(3, -1, 3, paths);
+
+ yield return CreateVariantKit(4, 1, 1, paths);
+ yield return CreateVariantKit(5, 1, 2, paths);
+ yield return CreateVariantKit(6, 1, 3, paths);
+
+ yield return CreateVariantKit(7, 2, 3, paths);
+ yield return CreateVariantKit(8, 2, 2, paths);
+ yield return CreateVariantKit(9, 2, 1, paths);
+
+ yield return CreateVariantKit(10, 3, 1, paths);
+
+ yield return CreateVariantKit(11, 4, 1, paths);
+ yield return CreateVariantKit(12, 4, 2, paths);
+ }
+
+ private static Dictionary GetCultureInfos(int id, DateTime now)
+ {
+ var en = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
+ var fr = new[] { 1, 3, 4, 6, 7, 9, 10, 12 };
+
+ var infos = new Dictionary();
+ if (en.Contains(id))
+ infos["en-US"] = new CultureVariation { Name = "N" + id + "-" + "en-US", Date = now, IsDraft = false };
+ if (fr.Contains(id))
+ infos["fr-FR"] = new CultureVariation { Name = "N" + id + "-" + "fr-FR", Date = now, IsDraft = false };
+ return infos;
+ }
+
+ private ContentNodeKit CreateVariantKit(int id, int parentId, int sortOrder, Dictionary paths)
+ {
+ if (!paths.TryGetValue(parentId, out var parentPath))
+ throw new Exception("Unknown parent.");
+
+ var path = paths[id] = parentPath + "," + id;
+ var level = path.Count(x => x == ',');
+ var now = DateTime.Now;
+
+ return new ContentNodeKit
{
- var en = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
- var fr = new[] { 1, 3, 4, 6, 7, 9, 10, 12 };
-
- var infos = new Dictionary();
- if (en.Contains(id))
- infos["en-US"] = new CultureVariation { Name = "N" + id + "-" + "en-US", Date = now, IsDraft = false };
- if (fr.Contains(id))
- infos["fr-FR"] = new CultureVariation { Name = "N" + id + "-" + "fr-FR", Date = now, IsDraft = false };
- return infos;
- }
-
- ContentNodeKit CreateKit(int id, int parentId, int sortOrder)
- {
- if (!paths.TryGetValue(parentId, out var parentPath))
- throw new Exception("Unknown parent.");
-
- var path = paths[id] = parentPath + "," + id;
- var level = path.Count(x => x == ',');
- var now = DateTime.Now;
-
- return new ContentNodeKit
+ ContentTypeId = _contentTypeVariant.Id,
+ Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0),
+ DraftData = null,
+ PublishedData = new ContentData
{
- ContentTypeId = _contentTypeVariant.Id,
- Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0),
- DraftData = null,
- PublishedData = new ContentData
- {
- Name = "N" + id,
- Published = true,
- TemplateId = 0,
- VersionId = 1,
- VersionDate = now,
- WriterId = 0,
- Properties = new Dictionary(),
- CultureInfos = GetCultureInfos(id, now)
- }
- };
- }
-
- yield return CreateKit(1, -1, 1);
- yield return CreateKit(2, -1, 2);
- yield return CreateKit(3, -1, 3);
-
- yield return CreateKit(4, 1, 1);
- yield return CreateKit(5, 1, 2);
- yield return CreateKit(6, 1, 3);
-
- yield return CreateKit(7, 2, 3);
- yield return CreateKit(8, 2, 2);
- yield return CreateKit(9, 2, 1);
-
- yield return CreateKit(10, 3, 1);
-
- yield return CreateKit(11, 4, 1);
- yield return CreateKit(12, 4, 2);
+ Name = "N" + id,
+ Published = true,
+ TemplateId = 0,
+ VersionId = 1,
+ VersionDate = now,
+ WriterId = 0,
+ Properties = new Dictionary(),
+ CultureInfos = GetCultureInfos(id, now)
+ }
+ };
}
private IEnumerable GetVariantWithDraftKits()
@@ -660,6 +682,96 @@ namespace Umbraco.Tests.PublishedContent
Assert.AreEqual(1, snapshot.Content.GetById(7).Parent?.Id);
}
+ [Test]
+ public void NestedVariationChildrenTest()
+ {
+ var mixedKits = GetNestedVariantKits();
+ Init(mixedKits);
+
+ var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null);
+ _snapshotAccessor.PublishedSnapshot = snapshot;
+
+ //TEST with en-us variation context
+
+ _variationAccesor.VariationContext = new VariationContext("en-US");
+
+ var documents = snapshot.Content.GetAtRoot().ToArray();
+ AssertDocuments(documents, "N1-en-US");
+
+ documents = snapshot.Content.GetById(1).Children().ToArray();
+ AssertDocuments(documents, "N4", "N7-en-US");
+
+ //Get the invariant and list children, there's a variation context so it should return invariant AND en-us variants
+ documents = snapshot.Content.GetById(4).Children().ToArray();
+ AssertDocuments(documents, "N10-en-US", "N11");
+
+ //Get the variant and list children, there's a variation context so it should return invariant AND en-us variants
+ documents = snapshot.Content.GetById(7).Children().ToArray();
+ AssertDocuments(documents, "N12-en-US", "N13");
+
+ //TEST with fr-fr variation context
+
+ _variationAccesor.VariationContext = new VariationContext("fr-FR");
+
+ documents = snapshot.Content.GetAtRoot().ToArray();
+ AssertDocuments(documents, "N1-fr-FR");
+
+ documents = snapshot.Content.GetById(1).Children().ToArray();
+ AssertDocuments(documents, "N4", "N7-fr-FR");
+
+ //Get the invariant and list children, there's a variation context so it should return invariant AND en-us variants
+ documents = snapshot.Content.GetById(4).Children().ToArray();
+ AssertDocuments(documents, "N10-fr-FR", "N11");
+
+ //Get the variant and list children, there's a variation context so it should return invariant AND en-us variants
+ documents = snapshot.Content.GetById(7).Children().ToArray();
+ AssertDocuments(documents, "N12-fr-FR", "N13");
+
+ //TEST specific cultures
+
+ documents = snapshot.Content.GetAtRoot("fr-FR").ToArray();
+ AssertDocuments(documents, "N1-fr-FR");
+
+ documents = snapshot.Content.GetById(1).Children("fr-FR").ToArray();
+ AssertDocuments(documents, "N4", "N7-fr-FR"); //NOTE: Returns invariant, this is expected
+ documents = snapshot.Content.GetById(1).Children("").ToArray();
+ AssertDocuments(documents, "N4"); //Only returns invariant since that is what was requested
+
+ documents = snapshot.Content.GetById(4).Children("fr-FR").ToArray();
+ AssertDocuments(documents, "N10-fr-FR", "N11"); //NOTE: Returns invariant, this is expected
+ documents = snapshot.Content.GetById(4).Children("").ToArray();
+ AssertDocuments(documents, "N11"); //Only returns invariant since that is what was requested
+
+ documents = snapshot.Content.GetById(7).Children("fr-FR").ToArray();
+ AssertDocuments(documents, "N12-fr-FR", "N13"); //NOTE: Returns invariant, this is expected
+ documents = snapshot.Content.GetById(7).Children("").ToArray();
+ AssertDocuments(documents, "N13"); //Only returns invariant since that is what was requested
+
+ //TEST without variation context
+ // This will actually convert the culture to "" which will be invariant since that's all it will know how to do
+ // This will return a NULL name for culture specific entities because there is no variation context
+
+ _variationAccesor.VariationContext = null;
+
+ documents = snapshot.Content.GetAtRoot().ToArray();
+ //will return nothing because there's only variant at root
+ Assert.AreEqual(0, documents.Length);
+ //so we'll continue to getting the known variant, do not fully assert this because the Name will NULL
+ documents = snapshot.Content.GetAtRoot("fr-FR").ToArray();
+ Assert.AreEqual(1, documents.Length);
+
+ documents = snapshot.Content.GetById(1).Children().ToArray();
+ AssertDocuments(documents, "N4");
+
+ //Get the invariant and list children
+ documents = snapshot.Content.GetById(4).Children().ToArray();
+ AssertDocuments(documents, "N11");
+
+ //Get the variant and list children
+ documents = snapshot.Content.GetById(7).Children().ToArray();
+ AssertDocuments(documents, "N13");
+ }
+
[Test]
public void VariantChildrenTest()
{