From fcfaff9daab89f9a90075bc74c206811c3fecb05 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 10 Nov 2025 03:02:10 +0100 Subject: [PATCH] Querying: Restore ability to retrieve all children published in any culture (closes #20760) (#20766) * Restore ability to retrieve all children published in any culture. * Fixed typo in test name. --- .../PublishStatus/PublishStatusService.cs | 4 +- .../PublishedContentStatusFilteringService.cs | 9 +-- ...ishedContentStatusFilteringServiceTests.cs | 72 ++++++++++++++----- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Core/Services/PublishStatus/PublishStatusService.cs b/src/Umbraco.Core/Services/PublishStatus/PublishStatusService.cs index 63d6d657fc..43b954a7f7 100644 --- a/src/Umbraco.Core/Services/PublishStatus/PublishStatusService.cs +++ b/src/Umbraco.Core/Services/PublishStatus/PublishStatusService.cs @@ -69,7 +69,9 @@ public class PublishStatusService : IPublishStatusManagementService, IPublishSta if (_publishedCultures.TryGetValue(documentKey, out ISet? publishedCultures)) { - return publishedCultures.Contains(culture, StringComparer.InvariantCultureIgnoreCase); + // If "*" is provided as the culture, we consider this as "published in any culture". This aligns + // with behaviour in Umbraco 13. + return culture == Constants.System.InvariantCulture || publishedCultures.Contains(culture, StringComparer.InvariantCultureIgnoreCase); } _logger.LogDebug("Document {DocumentKey} not found in the publish status cache", documentKey); diff --git a/src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs b/src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs index 860b4cb2f3..fce1eb949c 100644 --- a/src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs +++ b/src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Extensions; @@ -40,13 +40,14 @@ internal sealed class PublishedContentStatusFilteringService : IPublishedContent _publishStatusQueryService.IsDocumentPublished(key, culture) && _publishStatusQueryService.HasPublishedAncestorPath(key)); - return WhereIsInvariantOrHasCulture(candidateKeys, culture, preview).ToArray(); + return WhereIsInvariantOrHasCultureOrRequestedAllCultures(candidateKeys, culture, preview).ToArray(); } - private IEnumerable WhereIsInvariantOrHasCulture(IEnumerable keys, string culture, bool preview) + private IEnumerable WhereIsInvariantOrHasCultureOrRequestedAllCultures(IEnumerable keys, string culture, bool preview) => keys .Select(key => _publishedContentCache.GetById(preview, key)) .WhereNotNull() - .Where(content => content.ContentType.VariesByCulture() is false + .Where(content => culture == Constants.System.InvariantCulture + || content.ContentType.VariesByCulture() is false || content.Cultures.ContainsKey(culture)); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs index 6949d47c88..d73fff433b 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs @@ -1,5 +1,6 @@ -using Moq; +using Moq; using NUnit.Framework; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; @@ -44,9 +45,10 @@ public partial class PublishedContentStatusFilteringServiceTests [TestCase("da-DK", 3)] [TestCase("en-US", 4)] + [TestCase("*", 5)] public void FilterAvailable_Variant_ForNonPreview_YieldsPublishedItemsInCulture(string culture, int expectedNumberOfChildren) { - var (sut, items) = SetupVariant(false, culture); + var (sut, items) = SetupVariant(false, culture == Constants.System.InvariantCulture ? "en-US" : culture); var children = sut.FilterAvailable(items.Keys, culture).ToArray(); Assert.AreEqual(expectedNumberOfChildren, children.Length); @@ -70,16 +72,24 @@ public partial class PublishedContentStatusFilteringServiceTests { Assert.AreEqual(8, children[2].Id); } + + if (culture == Constants.System.InvariantCulture) + { + Assert.AreEqual(4, children[2].Id); + Assert.AreEqual(6, children[3].Id); + Assert.AreEqual(8, children[4].Id); + } } - [TestCase("da-DK")] - [TestCase("en-US")] - public void FilterAvailable_Variant_ForPreview_YieldsUnpublishedItemsInCulture(string culture) + [TestCase("da-DK", 7)] + [TestCase("en-US", 7)] + [TestCase("*", 10)] + public void FilterAvailable_Variant_ForPreview_YieldsUnpublishedItemsInCulture(string culture, int expectedNumberOfChildren) { - var (sut, items) = SetupVariant(true, culture); + var (sut, items) = SetupVariant(true, culture == Constants.System.InvariantCulture ? "en-US" : culture); var children = sut.FilterAvailable(items.Keys, culture).ToArray(); - Assert.AreEqual(7, children.Length); + Assert.AreEqual(expectedNumberOfChildren, children.Length); // IDs 0 through 3 exist in both en-US and da-DK Assert.Multiple(() => @@ -105,16 +115,27 @@ public partial class PublishedContentStatusFilteringServiceTests Assert.AreEqual(8, children[5].Id); Assert.AreEqual(9, children[6].Id); } + + if (culture == Constants.System.InvariantCulture) + { + Assert.AreEqual(4, children[4].Id); + Assert.AreEqual(5, children[5].Id); + Assert.AreEqual(6, children[6].Id); + Assert.AreEqual(7, children[7].Id); + Assert.AreEqual(8, children[8].Id); + Assert.AreEqual(9, children[9].Id); + } } - [TestCase("da-DK")] - [TestCase("en-US")] - public void FilterAvailable_MixedVariance_ForNonPreview_YieldsPublishedItemsInCultureOrInvariant(string culture) + [TestCase("da-DK", 4)] + [TestCase("en-US", 4)] + [TestCase("*", 5)] + public void FilterAvailable_MixedVariance_ForNonPreview_YieldsPublishedItemsInCultureOrInvariant(string culture, int expectedNumberOfChildren) { - var (sut, items) = SetupMixedVariance(false, culture); + var (sut, items) = SetupMixedVariance(false, culture == Constants.System.InvariantCulture ? "en-US" : culture); var children = sut.FilterAvailable(items.Keys, culture).ToArray(); - Assert.AreEqual(4, children.Length); + Assert.AreEqual(expectedNumberOfChildren, children.Length); // IDs 0 through 2 are invariant - only even IDs are published Assert.Multiple(() => @@ -140,16 +161,23 @@ public partial class PublishedContentStatusFilteringServiceTests { Assert.AreEqual(8, children[3].Id); } + + if (culture == Constants.System.InvariantCulture) + { + Assert.AreEqual(6, children[3].Id); + Assert.AreEqual(8, children[4].Id); + } } - [TestCase("da-DK")] - [TestCase("en-US")] - public void FilterAvailable_MixedVariance_FoPreview_YieldsPublishedItemsInCultureOrInvariant(string culture) + [TestCase("da-DK", 8)] + [TestCase("en-US", 8)] + [TestCase("*", 10)] + public void FilterAvailable_MixedVariance_ForPreview_YieldsPublishedItemsInCultureOrInvariant(string culture, int expectedNumberOfChildren) { - var (sut, items) = SetupMixedVariance(true, culture); + var (sut, items) = SetupMixedVariance(true, culture == Constants.System.InvariantCulture ? "en-US" : culture); var children = sut.FilterAvailable(items.Keys, culture).ToArray(); - Assert.AreEqual(8, children.Length); + Assert.AreEqual(expectedNumberOfChildren, children.Length); // IDs 0 through 2 are invariant Assert.Multiple(() => @@ -180,6 +208,14 @@ public partial class PublishedContentStatusFilteringServiceTests Assert.AreEqual(8, children[6].Id); Assert.AreEqual(9, children[7].Id); } + + if (culture == Constants.System.InvariantCulture) + { + Assert.AreEqual(6, children[6].Id); + Assert.AreEqual(7, children[7].Id); + Assert.AreEqual(8, children[8].Id); + Assert.AreEqual(9, children[9].Id); + } } // sets up invariant test data: @@ -328,7 +364,7 @@ public partial class PublishedContentStatusFilteringServiceTests .Returns((Guid key, string culture) => items .TryGetValue(key, out var item) && idIsPublished(item.Id) - && (item.ContentType.VariesByCulture() is false || item.Cultures.ContainsKey(culture))); + && (culture == Constants.System.InvariantCulture || item.ContentType.VariesByCulture() is false || item.Cultures.ContainsKey(culture))); publishStatusQueryService .Setup(s => s.HasPublishedAncestorPath(It.IsAny())) .Returns(true);