From 695f21eadbe6aee6415f8983998686db2ffd49f8 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sat, 21 Jul 2018 09:41:07 +0200 Subject: [PATCH] Removed unnecessary loop in looking up value from a fall-back language. Put in a check to abort fall-back if there's a loop in language fall-backs. --- .../IPublishedValueFallback.cs | 14 +- .../NoopPublishedValueFallback.cs | 16 ++- .../PublishedContentLanuageVariantTests.cs | 14 +- .../PublishedValueFallback.cs | 17 +-- .../PublishedValueLanguageFallback.cs | 129 +++++++++--------- src/Umbraco.Web/PublishedContentExtensions.cs | 15 +- .../PublishedContentPropertyExtension.cs | 9 +- src/Umbraco.Web/PublishedElementExtensions.cs | 11 +- 8 files changed, 127 insertions(+), 98 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs index f154d9ef27..d9d9c0c298 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs @@ -1,4 +1,4 @@ -using Umbraco.Core.Composing; +using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent { @@ -22,22 +22,22 @@ namespace Umbraco.Core.Models.PublishedContent // this method is called whenever property.Value(culture, segment, defaultValue) is called, and // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). - object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue); + object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages); // this method is called whenever property.Value(culture, segment, defaultValue) is called, and // property.HasValue(culture, segment) is false. it can only fallback at property level (no recurse). - T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue); + T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages); // these methods to be called whenever getting the property value for the specified alias, culture and segment, // either returned no property at all, or a property that does not HasValue for the specified culture and segment. - object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue); + object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages); - T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue); + T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages); - object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); + object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages); - T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority); + T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs index 75ab9df35a..a7de0709e6 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Models.PublishedContent +using System.Collections.Generic; + +namespace Umbraco.Core.Models.PublishedContent { /// /// Provides a noop implementation for . @@ -9,21 +11,21 @@ public class NoopPublishedValueFallback : IPublishedValueFallback { /// - public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) => defaultValue; + public object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) => defaultValue; /// - public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) => defaultValue; + public T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) => defaultValue; /// - public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) => defaultValue; + public object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) => defaultValue; /// - public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) => defaultValue; + public T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) => defaultValue; /// - public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) => defaultValue; + public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) => defaultValue; /// - public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) => defaultValue; + public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) => defaultValue; } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs index 22eb4bd799..17ce032005 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs @@ -35,13 +35,17 @@ namespace Umbraco.Tests.PublishedContent // Set up languages. // Spanish falls back to English and Italian to Spanish (and then to English). // French has no fall back. + // Danish, Swedish and Norweigan create an invalid loop. var languages = new List { new Language("en-US") { Id = 1, CultureName = "English", IsDefaultVariantLanguage = true }, new Language("fr") { Id = 2, CultureName = "French" }, new Language("es") { Id = 3, CultureName = "Spanish", FallbackLanguageId = 1 }, new Language("it") { Id = 4, CultureName = "Italian", FallbackLanguageId = 3 }, - new Language("de") { Id = 5, CultureName = "German" } + new Language("de") { Id = 5, CultureName = "German" }, + new Language("da") { Id = 6, CultureName = "Danish", FallbackLanguageId = 8 }, + new Language("sv") { Id = 7, CultureName = "Swedish", FallbackLanguageId = 6 }, + new Language("no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 } }; var localizationService = Mock.Get(serviceContext.LocalizationService); @@ -126,5 +130,13 @@ namespace Umbraco.Tests.PublishedContent var value = content.Value("welcomeText", "it"); Assert.AreEqual("Welcome", value); } + + [Test] + public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() + { + var content = UmbracoContext.Current.ContentCache.GetAtRoot().First(); + var value = content.Value("welcomeText", "no"); + Assert.IsNull(value); + } } } diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs index 562b8e393b..86823767fd 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models.PublishedContent; +using System.Collections.Generic; +using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Models.PublishedContent { @@ -11,45 +12,45 @@ namespace Umbraco.Web.Models.PublishedContent // kinda reproducing what was available in v7 /// - public virtual object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + public virtual object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + public virtual T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + public virtual object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + public virtual T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) { // no fallback here return defaultValue; } /// - public virtual object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public virtual object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { // no fallback here if (!recurse) return defaultValue; // is that ok? - return GetValue(content, alias, culture, segment, defaultValue, true, fallbackPriority); + return GetValue(content, alias, culture, segment, defaultValue, true, fallbackPriority, visitedLanguages); } /// - public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public virtual T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { // no fallback here if (!recurse) return defaultValue; diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs index d6e8db83f4..d19ef80732 100644 --- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs +++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueLanguageFallback.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models; +using System.Collections.Generic; +using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; @@ -21,72 +22,72 @@ namespace Umbraco.Web.Models.PublishedContent } /// - public override object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue) + public override object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages) { object value; - if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(property, culture, segment, defaultValue); + return base.GetValue(property, culture, segment, defaultValue, visitedLanguages); } /// - public override T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue) + public override T GetValue(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages) { T value; - if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(property, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(property, culture, segment, defaultValue); + return base.GetValue(property, culture, segment, defaultValue, visitedLanguages); } /// - public override object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue) + public override object GetValue(IPublishedElement content, string alias, string culture, string segment, object defaultValue, ICollection visitedLanguages) { object value; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(content, alias, culture, segment, defaultValue); + return base.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages); } /// - public override T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue) + public override T GetValue(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages) { T value; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, out value)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, visitedLanguages, out value)) { return value; } - return base.GetValue(content, alias, culture, segment, defaultValue); + return base.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages); } /// - public override object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public override object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { - return GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); } /// - public override T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority) + public override T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) { if (fallbackPriority == PublishedValueFallbackPriority.RecursiveTree) { - var result = base.GetValue(content, alias, culture, segment, defaultValue, recurse, PublishedValueFallbackPriority.RecursiveTree); + var result = base.GetValue(content, alias, culture, segment, defaultValue, recurse, PublishedValueFallbackPriority.RecursiveTree, visitedLanguages); if (ValueIsNotNullEmptyOrDefault(result, defaultValue)) { // We've prioritised recursive tree search and found a value, so can return it. return result; } - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, out result)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages, out result)) { return result; } @@ -97,112 +98,116 @@ namespace Umbraco.Web.Models.PublishedContent if (fallbackPriority == PublishedValueFallbackPriority.FallbackLanguage) { T result; - if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, out result)) + if (TryGetValueFromFallbackLanguage(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages, out result)) { return result; } } // No language fall back content found, so use base implementation - return base.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return base.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); } - private bool TryGetValueFromFallbackLanguage(IPublishedProperty property, string culture, string segment, T defaultValue, out T value) + private bool TryGetValueFromFallbackLanguage(IPublishedProperty property, string culture, string segment, T defaultValue, ICollection visitedLanguages, out T value) { + value = defaultValue; + if (string.IsNullOrEmpty(culture)) { - value = defaultValue; return false; } var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguageId.HasValue == false) { - value = defaultValue; return false; } - var fallbackLanguageId = language.FallbackLanguageId; - while (fallbackLanguageId.HasValue) + if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) { - var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); - value = property.Value(fallbackLanguage.IsoCode, segment, defaultValue); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - fallbackLanguageId = fallbackLanguage.FallbackLanguageId; + return false; + } + + visitedLanguages.Add(language.FallbackLanguageId.Value); + + var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); + value = property.Value(fallbackLanguage.IsoCode, segment, defaultValue, visitedLanguages); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; } - value = defaultValue; return false; } - private bool TryGetValueFromFallbackLanguage(IPublishedElement content, string alias, string culture, string segment, T defaultValue, out T value) + private bool TryGetValueFromFallbackLanguage(IPublishedElement content, string alias, string culture, string segment, T defaultValue, ICollection visitedLanguages, out T value) { + value = defaultValue; + if (string.IsNullOrEmpty(culture)) { - value = defaultValue; return false; } var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguageId.HasValue == false) { - value = defaultValue; return false; } - var fallbackLanguageId = language.FallbackLanguageId; - while (fallbackLanguageId.HasValue) + if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) { - var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); - value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - fallbackLanguageId = fallbackLanguage.FallbackLanguageId; + return false; + } + + visitedLanguages.Add(language.FallbackLanguageId.Value); + + var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); + value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, visitedLanguages); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; } - value = defaultValue; return false; } - private bool TryGetValueFromFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, out T value) + private bool TryGetValueFromFallbackLanguage(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages, out T value) { + value = defaultValue; if (string.IsNullOrEmpty(culture)) { - value = defaultValue; return false; } var language = _localizationService.GetLanguageByIsoCode(culture); if (language.FallbackLanguageId.HasValue == false) { - value = defaultValue; return false; } - var fallbackLanguageId = language.FallbackLanguageId; - while (fallbackLanguageId.HasValue) + if (AlreadyVisitedLanguage(visitedLanguages, language.FallbackLanguageId.Value)) { - var fallbackLanguage = GetLanguageById(fallbackLanguageId.Value); - value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, recurse); - if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) - { - return true; - } - - fallbackLanguageId = fallbackLanguage.FallbackLanguageId; + return false; + } + + visitedLanguages.Add(language.FallbackLanguageId.Value); + + var fallbackLanguage = GetLanguageById(language.FallbackLanguageId.Value); + value = content.Value(alias, fallbackLanguage.IsoCode, segment, defaultValue, recurse, fallbackPriority, visitedLanguages); + if (ValueIsNotNullEmptyOrDefault(value, defaultValue)) + { + return true; } - value = defaultValue; return false; } + private static bool AlreadyVisitedLanguage(ICollection visitedLanguages, int fallbackLanguageId) + { + return visitedLanguages.Contains(fallbackLanguageId); + } + private ILanguage GetLanguageById(int id) { return _localizationService.GetLanguageById(id); diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 22a0bc8aca..4ab2d1dc79 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -162,6 +162,7 @@ namespace Umbraco.Web /// The default value. /// A value indicating whether to recurse. /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// Recursively means: walking up the tree from , get the first value that can be found. @@ -170,14 +171,15 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) + public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, object defaultValue = default, + bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages ?? new List()); } #endregion @@ -195,6 +197,7 @@ namespace Umbraco.Web /// The default value. /// A value indicating whether to recurse. /// Flag indicating priority order of fallback paths in cases when content does not exist and a fall back method is used. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, converted to the specified type. /// /// Recursively means: walking up the tree from , get the first value that can be found. @@ -203,18 +206,20 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) + public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, T defaultValue = default, + bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, recurse, fallbackPriority, visitedLanguages ?? new List()); } // fixme - .Value() refactoring - in progress - public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree) + public static IHtmlString Value(this IPublishedContent content, string aliases, Func format, string alt = "", + bool recurse = false, PublishedValueFallbackPriority fallbackPriority = PublishedValueFallbackPriority.RecursiveTree, ICollection visitedLanguages = null) { var aliasesA = aliases.Split(','); if (aliasesA.Length == 0) diff --git a/src/Umbraco.Web/PublishedContentPropertyExtension.cs b/src/Umbraco.Web/PublishedContentPropertyExtension.cs index fcbfc7f431..fdfd772ce7 100644 --- a/src/Umbraco.Web/PublishedContentPropertyExtension.cs +++ b/src/Umbraco.Web/PublishedContentPropertyExtension.cs @@ -1,4 +1,5 @@ -using Umbraco.Core; +using System.Collections.Generic; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; @@ -15,19 +16,19 @@ namespace Umbraco.Web #region Value - public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default) + public static object Value(this IPublishedProperty property, string culture = null, string segment = null, object defaultValue = default, ICollection visitedLanguages = null) { if (property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(property, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(property, culture, segment, defaultValue, visitedLanguages ?? new List()); } #endregion #region Value - public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default) + public static T Value(this IPublishedProperty property, string culture = null, string segment = null, T defaultValue = default, ICollection visitedLanguages = null) { // for Value when defaultValue is not specified, and HasValue() is false, we still want to convert the result (see below) // but we have no way to tell whether default value is specified or not - we could do it with overloads, but then defaultValue diff --git a/src/Umbraco.Web/PublishedElementExtensions.cs b/src/Umbraco.Web/PublishedElementExtensions.cs index 945270cb9e..b0d2826df4 100644 --- a/src/Umbraco.Web/PublishedElementExtensions.cs +++ b/src/Umbraco.Web/PublishedElementExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Models.PublishedContent; @@ -99,6 +100,7 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -106,14 +108,14 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default) + public static object Value(this IPublishedElement content, string alias, string culture = null, string segment = null, object defaultValue = default, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.GetValue(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List()); } #endregion @@ -129,6 +131,7 @@ namespace Umbraco.Web /// The variation language. /// The variation segment. /// The default value. + /// A list of cultures already visited in looking for a value via a fall-back method. /// The value of the content's property identified by the alias, converted to the specified type. /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content. @@ -136,14 +139,14 @@ namespace Umbraco.Web /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter. /// The alias is case-insensitive. /// - public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default) + public static T Value(this IPublishedElement content, string alias, string culture = null, string segment = null, T defaultValue = default, ICollection visitedLanguages = null) { var property = content.GetProperty(alias); if (property != null && property.HasValue(culture, segment)) return property.Value(culture, segment); - return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue); + return PublishedValueFallback.GetValue(content, alias, culture, segment, defaultValue, visitedLanguages ?? new List()); } #endregion