diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
index 4b6e7cb10a..23df9e485f 100644
--- a/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedValueFallback.cs
@@ -117,10 +117,15 @@
/// A fallback strategy.
/// An optional default value.
/// The fallback value.
+ /// The property that does not have a value.
/// A value indicating whether a fallback value could be provided.
///
/// This method is called whenever getting the property value for the specified alias, culture and
/// segment, either returned no property at all, or a property with HasValue(culture, segment) being false.
+ /// In an , because walking up the tree is possible, the content itself may not even
+ /// have a property with the specified alias, but such a property may exist up in the tree. The
+ /// parameter is used to return a property with no value. That can then be used to invoke a converter and get the
+ /// converter's interpretation of "no value".
///
bool TryGetValue(IPublishedContent content, string alias, string culture, string segment, Fallback fallback, T defaultValue, out T value, out IPublishedProperty noValueProperty);
}
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs
index 7bb249a652..42703f9bae 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs
@@ -63,14 +63,20 @@ namespace Umbraco.Tests.PublishedContent
var prop1Type = factory.CreatePropertyType("prop1", 1, variations: ContentVariation.Culture);
var welcomeType = factory.CreatePropertyType("welcomeText", 1, variations: ContentVariation.Culture);
var welcome2Type = factory.CreatePropertyType("welcomeText2", 1, variations: ContentVariation.Culture);
+ var nopropType = factory.CreatePropertyType("noprop", 1, variations: ContentVariation.Culture);
+
var props = new[]
{
prop1Type,
welcomeType,
welcome2Type,
+ nopropType
};
var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), props);
+ var prop3Type = factory.CreatePropertyType("prop3", 1, variations: ContentVariation.Culture);
+ var contentType2 = factory.CreateContentType(2, "contentType2", Enumerable.Empty(), new[] { prop3Type });
+
var prop1 = new SolidPublishedPropertyWithLanguageVariants
{
Alias = "welcomeText",
@@ -99,6 +105,14 @@ namespace Umbraco.Tests.PublishedContent
prop3.SetSourceValue("en-US", "Welcome", true);
prop3.SetValue("en-US", "Welcome", true);
+ var noprop = new SolidPublishedProperty
+ {
+ Alias = "noprop",
+ PropertyType = nopropType
+ };
+ noprop.SolidHasValue = false; // has no value
+ noprop.SolidValue = "xxx"; // but returns something
+
var item1 = new SolidPublishedContent(contentType1)
{
Id = 1,
@@ -112,7 +126,7 @@ namespace Umbraco.Tests.PublishedContent
ChildIds = new[] { 2 },
Properties = new Collection
{
- prop1, prop2
+ prop1, prop2, noprop
}
};
@@ -126,18 +140,47 @@ namespace Umbraco.Tests.PublishedContent
Level = 2,
Url = "/content-1/content-2",
ParentId = 1,
- ChildIds = new int[] { },
+ ChildIds = new int[] { 3 },
Properties = new Collection
{
prop3
}
};
+ var prop4 = new SolidPublishedPropertyWithLanguageVariants
+ {
+ Alias = "prop3",
+ PropertyType = prop3Type
+ };
+ prop4.SetSourceValue("en-US", "Oxxo", true);
+ prop4.SetValue("en-US", "Oxxo", true);
+
+ var item3 = new SolidPublishedContent(contentType2)
+ {
+ Id = 3,
+ SortOrder = 0,
+ Name = "Content 3",
+ UrlSegment = "content-3",
+ Path = "/1/2/3",
+ Level = 3,
+ Url = "/content-1/content-2/content-3",
+ ParentId = 2,
+ ChildIds = new int[] { },
+ Properties = new Collection
+ {
+ prop4
+ }
+ };
+
item1.Children = new List { item2 };
item2.Parent = item1;
+ item2.Children = new List { item3 };
+ item3.Parent = item2;
+
cache.Add(item1);
cache.Add(item2);
+ cache.Add(item3);
}
[Test]
@@ -212,6 +255,34 @@ namespace Umbraco.Tests.PublishedContent
Assert.AreEqual("Welcome", value);
}
+ [Test]
+ public void Do_Not_Get_Content_Recursively_Unless_Requested2()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First().Children().First();
+ Assert.IsNull(content.GetProperty("welcomeText2"));
+ var value = content.Value("welcomeText2");
+ Assert.IsNull(value);
+ }
+
+ [Test]
+ public void Can_Get_Content_Recursively2()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First().Children().First();
+ Assert.IsNull(content.GetProperty("welcomeText2"));
+ var value = content.Value("welcomeText2", fallback: Fallback.ToAncestors);
+ Assert.AreEqual("Welcome", value);
+ }
+
+ [Test]
+ public void Can_Get_Content_Recursively3()
+ {
+ var content = UmbracoContext.Current.ContentCache.GetAtRoot().First().Children.First().Children().First();
+ Assert.IsNull(content.GetProperty("noprop"));
+ var value = content.Value("noprop", fallback: Fallback.ToAncestors);
+ // property has no value but we still get the value (ie, the converter would do something)
+ Assert.AreEqual("xxx", value);
+ }
+
[Test]
public void Can_Get_Content_With_Recursive_Priority()
{
diff --git a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs
index bdd446693e..ae011c01f9 100644
--- a/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs
+++ b/src/Umbraco.Web/Models/PublishedContent/PublishedValueFallback.cs
@@ -172,7 +172,7 @@ namespace Umbraco.Web.Models.PublishedContent
}
property = content?.GetProperty(alias);
- if (property != null && noValueProperty != null)
+ if (property != null && noValueProperty == null)
{
noValueProperty = property;
}