diff --git a/src/Umbraco.Core/Models/PublishedContent/Fallback.cs b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs index 2c665f1710..e6df70621a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/Fallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs @@ -3,56 +3,82 @@ using System.Collections; namespace Umbraco.Cms.Core.Models.PublishedContent; /// -/// Manages the built-in fallback policies. +/// Manages the built-in fallback policies. /// public struct Fallback : IEnumerable { /// - /// Do not fallback. + /// Do not fallback. /// public const int None = 0; private readonly int[] _values; /// - /// Initializes a new instance of the struct with values. + /// Initializes a new instance of the struct with values. /// + /// The values. private Fallback(int[] values) => _values = values; /// - /// Gets an ordered set of fallback policies. + /// Gets an ordered set of fallback policies. /// - /// + /// The values. + /// + /// The fallback policy. + /// public static Fallback To(params int[] values) => new(values); /// - /// Fallback to default value. + /// Fallback to the default value. /// public const int DefaultValue = 1; /// - /// Fallback to other languages. + /// Fallback to other languages. /// public const int Language = 2; /// - /// Fallback to tree ancestors. + /// Fallback to tree ancestors. /// public const int Ancestors = 3; /// - /// Gets the fallback to default value policy. + /// Fallback to the default language. /// + public const int DefaultLanguage = 4; + + /// + /// Gets the fallback to the default language policy. + /// + /// + /// The default language fallback policy. + /// + public static Fallback ToDefaultLanguage => new Fallback(new[] { DefaultLanguage }); + + /// + /// Gets the fallback to the default value policy. + /// + /// + /// The default value fallback policy. + /// public static Fallback ToDefaultValue => new(new[] { DefaultValue }); /// - /// Gets the fallback to language policy. + /// Gets the fallback to language policy. /// + /// + /// The language fallback policy. + /// public static Fallback ToLanguage => new(new[] { Language }); /// - /// Gets the fallback to tree ancestors policy. + /// Gets the fallback to tree ancestors policy. /// + /// + /// The tree ancestors fallback policy. + /// public static Fallback ToAncestors => new(new[] { Ancestors }); /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs index 64f0160383..0e43a2617f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs @@ -44,6 +44,13 @@ public class PublishedValueFallback : IPublishedValueFallback return true; } + break; + case Fallback.DefaultLanguage: + if (TryGetValueWithDefaultLanguageFallback(property, culture, segment, out value)) + { + return true; + } + break; default: throw NotSupportedFallbackMethod(f, "property"); @@ -85,6 +92,13 @@ public class PublishedValueFallback : IPublishedValueFallback return true; } + break; + case Fallback.DefaultLanguage: + if (TryGetValueWithDefaultLanguageFallback(content, alias, culture, segment, out value)) + { + return true; + } + break; default: throw NotSupportedFallbackMethod(f, "element"); @@ -141,6 +155,13 @@ public class PublishedValueFallback : IPublishedValueFallback return true; } + break; + case Fallback.DefaultLanguage: + if (TryGetValueWithDefaultLanguageFallback(content, alias, culture, segment, out value)) + { + return true; + } + break; default: throw NotSupportedFallbackMethod(f, "content"); @@ -347,4 +368,42 @@ public class PublishedValueFallback : IPublishedValueFallback language = language2; } } + + private bool TryGetValueWithDefaultLanguageFallback(IPublishedProperty property, string? culture, string? segment, out T? value) + { + value = default; + + if (culture.IsNullOrWhiteSpace()) + { + return false; + } + + string? defaultCulture = _localizationService?.GetDefaultLanguageIsoCode(); + if (culture.InvariantEquals(defaultCulture) == false && property.HasValue(defaultCulture, segment)) + { + value = property.Value(this, defaultCulture, segment); + return true; + } + + return false; + } + + private bool TryGetValueWithDefaultLanguageFallback(IPublishedElement element, string alias, string? culture, string? segment, out T? value) + { + value = default; + + if (culture.IsNullOrWhiteSpace()) + { + return false; + } + + string? defaultCulture = _localizationService?.GetDefaultLanguageIsoCode(); + if (culture.InvariantEquals(defaultCulture) == false && element.HasValue(alias, defaultCulture, segment)) + { + value = element.Value(this, alias, defaultCulture, segment); + return true; + } + + return false; + } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs index 342e08acbe..37a5f08286 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs @@ -242,6 +242,15 @@ public class PublishedContentLanguageVariantTests : PublishedSnapshotServiceTest Assert.IsNull(value); } + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_DefaultLanguage_With_Fallback() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(PublishedValueFallback, "welcomeText", "fr", fallback: Fallback.ToDefaultLanguage); + Assert.AreEqual("Welcome", value); + } + [Test] public void Do_Not_Get_Content_Recursively_Unless_Requested() {