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() {