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; }