diff --git a/src/Umbraco.Core/Constants-Content.cs b/src/Umbraco.Core/Constants-Content.cs
new file mode 100644
index 0000000000..4b8c383e6f
--- /dev/null
+++ b/src/Umbraco.Core/Constants-Content.cs
@@ -0,0 +1,22 @@
+namespace Umbraco.Core
+{
+ public static partial class Constants
+ {
+ ///
+ /// Defines content retrieval related constants
+ ///
+ public static class Content
+ {
+ ///
+ /// Defines core supported content fall-back options when retrieving content property values.
+ /// Defined as constants rather than enum to allow solution or package defined fall-back methods.
+ ///
+ public static class FallbackMethods
+ {
+ public const int None = 0;
+ public const int RecursiveTree = 1;
+ public const int FallbackLanguage = 2;
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
index d9d9c0c298..7a7b67a2d1 100644
--- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
@@ -2,12 +2,6 @@
namespace Umbraco.Core.Models.PublishedContent
{
- public enum PublishedValueFallbackPriority
- {
- RecursiveTree,
- FallbackLanguage
- }
-
///
/// Provides a fallback strategy for getting values.
///
@@ -36,8 +30,8 @@ namespace Umbraco.Core.Models.PublishedContent
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, ICollection visitedLanguages);
+ object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages);
- T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages);
+ T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages);
}
}
diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs
index a7de0709e6..a8d55176a3 100644
--- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs
@@ -23,9 +23,9 @@ namespace Umbraco.Core.Models.PublishedContent
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, ICollection visitedLanguages) => defaultValue;
+ public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) => defaultValue;
///
- public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, bool recurse, PublishedValueFallbackPriority fallbackPriority, ICollection visitedLanguages) => defaultValue;
+ public T GetValue(IPublishedContent content, string alias, string culture, string segment, T defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages) => defaultValue;
}
}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 67028568eb..0a24cc5286 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -286,6 +286,7 @@
+
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs
index 17ce032005..7ee2e11209 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanuageVariantTests.cs
@@ -45,7 +45,8 @@ namespace Umbraco.Tests.PublishedContent
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 }
+ new Language("no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 },
+ new Language("nl") { Id = 9, CultureName = "Dutch", FallbackLanguageId = 1 }
};
var localizationService = Mock.Get(serviceContext.LocalizationService);
@@ -68,12 +69,28 @@ namespace Umbraco.Tests.PublishedContent
{
Alias = "welcomeText",
};
- prop1.SetSourceValue("en-US", "Welcome");
- prop1.SetValue("en-US", "Welcome");
+ prop1.SetSourceValue("en-US", "Welcome", true);
+ prop1.SetValue("en-US", "Welcome", true);
prop1.SetSourceValue("de", "Willkommen");
prop1.SetValue("de", "Willkommen");
+ prop1.SetSourceValue("nl", "Welkom");
+ prop1.SetValue("nl", "Welkom");
- cache.Add(new SolidPublishedContent(contentType1)
+ var prop2 = new SolidPublishedPropertyWithLanguageVariants
+ {
+ Alias = "welcomeText2",
+ };
+ prop2.SetSourceValue("en-US", "Welcome", true);
+ prop2.SetValue("en-US", "Welcome", true);
+
+ var prop3 = new SolidPublishedPropertyWithLanguageVariants
+ {
+ Alias = "welcomeText",
+ };
+ prop3.SetSourceValue("en-US", "Welcome", true);
+ prop3.SetValue("en-US", "Welcome", true);
+
+ var item1 = new SolidPublishedContent(contentType1)
{
Id = 1,
SortOrder = 0,
@@ -83,12 +100,35 @@ namespace Umbraco.Tests.PublishedContent
Level = 1,
Url = "/content-1",
ParentId = -1,
+ ChildIds = new[] { 2 },
+ Properties = new Collection
+ {
+ prop1, prop2
+ }
+ };
+
+ var item2 = new SolidPublishedContent(contentType1)
+ {
+ Id = 2,
+ SortOrder = 0,
+ Name = "Content 2",
+ UrlSegment = "content-2",
+ Path = "/1/2",
+ Level = 2,
+ Url = "/content-1/content-2",
+ ParentId = 1,
ChildIds = new int[] { },
Properties = new Collection
{
- prop1
+ prop3
}
- });
+ };
+
+ item1.Children = new List { item2 };
+ item2.Parent = item1;
+
+ cache.Add(item1);
+ cache.Add(item2);
}
[Test]
@@ -116,10 +156,18 @@ namespace Umbraco.Tests.PublishedContent
}
[Test]
- public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback()
+ public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Unless_Requested()
{
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
var value = content.Value("welcomeText", "es");
+ Assert.IsNull(value);
+ }
+
+ [Test]
+ public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
+ var value = content.Value("welcomeText", "es", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage });
Assert.AreEqual("Welcome", value);
}
@@ -127,7 +175,7 @@ namespace Umbraco.Tests.PublishedContent
public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels()
{
var content = UmbracoContext.Current.ContentCache.GetAtRoot().First();
- var value = content.Value("welcomeText", "it");
+ var value = content.Value("welcomeText", "it", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage });
Assert.AreEqual("Welcome", value);
}
@@ -135,8 +183,44 @@ namespace Umbraco.Tests.PublishedContent
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");
+ var value = content.Value("welcomeText", "no", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage });
Assert.IsNull(value);
}
+
+ [Test]
+ public void Do_Not_Get_Content_Recursively_Unless_Requested()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First();
+ var value = content.Value("welcomeText2");
+ Assert.IsNull(value);
+ }
+
+ [Test]
+ public void Can_Get_Content_Recursively()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First();
+ var value = content.Value("welcomeText2", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree });
+ Assert.AreEqual("Welcome", value);
+ }
+
+ [Test]
+ public void Can_Get_Content_With_Recursive_Priority()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First();
+ var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.RecursiveTree, Core.Constants.Content.FallbackMethods.FallbackLanguage });
+
+ // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent.
+ Assert.AreEqual("Welkom", value);
+ }
+
+ [Test]
+ public void Can_Get_Content_With_Fallback_Language_Priority()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First();
+ var value = content.Value("welcomeText", "nl", fallbackMethods: new[] { Core.Constants.Content.FallbackMethods.FallbackLanguage, Core.Constants.Content.FallbackMethods.RecursiveTree });
+
+ // No Dutch value is directly assigned. Check has fallen back to English value from language variant.
+ Assert.AreEqual("Welcome", value);
+ }
}
}
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
index a09cf6d4ad..330711a6ba 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
@@ -326,8 +326,8 @@ namespace Umbraco.Tests.PublishedContent
public void Get_Property_Value_Recursive()
{
var doc = GetNode(1174);
- var rVal = doc.Value("testRecursive", recurse: true);
- var nullVal = doc.Value("DoNotFindThis", recurse: true);
+ var rVal = doc.Value("testRecursive", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree } );
+ var nullVal = doc.Value("DoNotFindThis", fallbackMethods: new[] { Constants.Content.FallbackMethods.RecursiveTree });
Assert.AreEqual("This is the recursive val", rVal);
Assert.AreEqual(null, nullVal);
}
diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
index 33e315ebec..cbf7f3189d 100644
--- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
+++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs
@@ -248,7 +248,7 @@ namespace Umbraco.Tests.PublishedContent
#endregion
}
- class SolidPublishedProperty : IPublishedProperty
+ internal class SolidPublishedProperty : IPublishedProperty
{
public PublishedPropertyType PropertyType { get; set; }
public string Alias { get; set; }
@@ -309,19 +309,33 @@ namespace Umbraco.Tests.PublishedContent
return _solidSourceValues.ContainsKey(culture);
}
- public void SetSourceValue(string culture, object value)
+ public void SetSourceValue(string culture, object value, bool defaultValue = false)
{
_solidSourceValues.Add(culture, value);
+ if (defaultValue)
+ {
+ SolidSourceValue = value;
+ SolidHasValue = true;
+ }
}
- public void SetValue(string culture, object value)
+ public void SetValue(string culture, object value, bool defaultValue = false)
{
_solidValues.Add(culture, value);
+ if (defaultValue)
+ {
+ SolidValue = value;
+ SolidHasValue = true;
+ }
}
- public void SetXPathValue(string culture, object value)
+ public void SetXPathValue(string culture, object value, bool defaultValue = false)
{
_solidXPathValues.Add(culture, value);
+ if (defaultValue)
+ {
+ SolidXPathValue = value;
+ }
}
}
diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs
index 86823767fd..85b10be480 100644
--- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs
+++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Web.Models.PublishedContent
@@ -8,54 +9,79 @@ namespace Umbraco.Web.Models.PublishedContent
///
public class PublishedValueFallback : IPublishedValueFallback
{
- // this is our default implementation
// kinda reproducing what was available in v7
///
- public virtual object GetValue(IPublishedProperty property, string culture, string segment, object defaultValue, ICollection visitedLanguages)
+ public 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, ICollection visitedLanguages)
+ public 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, ICollection visitedLanguages)
+ public 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, ICollection visitedLanguages)
+ public 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, ICollection visitedLanguages)
+ public object GetValue(IPublishedContent content, string alias, string culture, string segment, object defaultValue, IEnumerable fallbackMethods, ICollection visitedLanguages)
{
- // no fallback here
- if (!recurse) return defaultValue;
-
// is that ok?
- return GetValue