Fix IPropertyValueConverter determining HasValue
This commit is contained in:
@@ -194,17 +194,19 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a source value is an actual value, or not a value.
|
||||
/// Determines whether a value is an actual value, or not a value.
|
||||
/// </summary>
|
||||
/// <remarks>Used by property.HasValue and, for instance, in fallback scenarios.</remarks>
|
||||
public bool IsValue(object value)
|
||||
public bool? IsValue(object value, PropertyValueLevel level)
|
||||
{
|
||||
// if we have a converter, use the converter,
|
||||
// otherwise use the old magic null & string comparisons
|
||||
if (!_initialized) Initialize();
|
||||
|
||||
return _converter == null
|
||||
? value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false)
|
||||
: _converter.IsValue(value);
|
||||
// if we have a converter, use the converter
|
||||
if (_converter != null)
|
||||
return _converter.IsValue(value, level);
|
||||
|
||||
// otherwise use the old magic null & string comparisons
|
||||
return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,9 +18,16 @@ namespace Umbraco.Core.PropertyEditors
|
||||
bool IsConverter(PublishedPropertyType propertyType);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a source value is an actual value, or not a value.
|
||||
/// Determines whether a value is an actual value, or not a value.
|
||||
/// </summary>
|
||||
bool IsValue(object value);
|
||||
/// <remarks>
|
||||
/// <para>Called for Source, Inter and Object levels, until one does not return null.</para>
|
||||
/// <para>Can return true (is a value), false (is not a value), or null to indicate that it
|
||||
/// cannot be determined at the specified level. For instance, if source is a string that
|
||||
/// could contain JSON, the decision could be made on the intermediate value. Or, if it is
|
||||
/// a picker, it could be made on the object value (the actual picked object).</para>
|
||||
/// </remarks>
|
||||
bool? IsValue(object value, PropertyValueLevel level);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of values returned by the converter.
|
||||
|
||||
@@ -11,8 +11,25 @@ namespace Umbraco.Core.PropertyEditors
|
||||
public virtual bool IsConverter(PublishedPropertyType propertyType)
|
||||
=> false;
|
||||
|
||||
public bool IsValue(object value)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
public virtual bool? IsValue(object value, PropertyValueLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case PropertyValueLevel.Source:
|
||||
return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
default:
|
||||
throw new NotSupportedException($"Invalid level: {level}.");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool HasValue(IPublishedProperty property, string culture, string segment)
|
||||
{
|
||||
// the default implementation uses the old magic null & string comparisons,
|
||||
// other implementations may be more clever, and/or test the final converted object values
|
||||
// fixme - cannot access the intermediate value here?
|
||||
var value = property.GetSourceValue(culture, segment);
|
||||
return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
}
|
||||
|
||||
public virtual Type GetPropertyValueType(PublishedPropertyType propertyType)
|
||||
=> typeof (object);
|
||||
|
||||
23
src/Umbraco.Core/PropertyEditors/PropertyValueLevel.cs
Normal file
23
src/Umbraco.Core/PropertyEditors/PropertyValueLevel.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the level of a value.
|
||||
/// </summary>
|
||||
public enum PropertyValueLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// The source value, i.e. what is in the database.
|
||||
/// </summary>
|
||||
Source,
|
||||
|
||||
/// <summary>
|
||||
/// The conversion intermediate value.
|
||||
/// </summary>
|
||||
Inter,
|
||||
|
||||
/// <summary>
|
||||
/// The converted value.
|
||||
/// </summary>
|
||||
Object
|
||||
}
|
||||
}
|
||||
@@ -402,6 +402,7 @@
|
||||
<Compile Include="PropertyEditors\ParameterEditorCollection.cs" />
|
||||
<Compile Include="PropertyEditors\PropertyEditorCollection.cs" />
|
||||
<Compile Include="PropertyEditors\PropertyEditorTagsExtensions.cs" />
|
||||
<Compile Include="PropertyEditors\PropertyValueLevel.cs" />
|
||||
<Compile Include="PropertyEditors\SliderPropertyEditorConfiguration.cs" />
|
||||
<Compile Include="PropertyEditors\TagConfiguration.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValue.cs" />
|
||||
|
||||
@@ -43,12 +43,30 @@ namespace Umbraco.Tests.Published
|
||||
var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary<string, object> { { "prop1", "1234" } }, false);
|
||||
|
||||
Assert.AreEqual(1234, element1.Value("prop1"));
|
||||
|
||||
// 'null' would be considered a 'missing' value by the default, magic logic
|
||||
var e = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary<string, object> { { "prop1", null } }, false);
|
||||
Assert.IsFalse(e.HasValue("prop1"));
|
||||
|
||||
// '0' would not - it's a valid integer - but the converter knows better
|
||||
e = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary<string, object> { { "prop1", "0" } }, false);
|
||||
Assert.IsFalse(e.HasValue("prop1"));
|
||||
}
|
||||
|
||||
private class SimpleConverter1 : IPropertyValueConverter
|
||||
{
|
||||
public bool IsValue(object value)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
public bool? IsValue(object value, PropertyValueLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case PropertyValueLevel.Source:
|
||||
return null;
|
||||
case PropertyValueLevel.Inter:
|
||||
return value is int ivalue && ivalue != 0;
|
||||
default:
|
||||
throw new NotSupportedException($"Invalid level: {level}.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsConverter(PublishedPropertyType propertyType)
|
||||
=> propertyType.EditorAlias.InvariantEquals("Umbraco.Void");
|
||||
@@ -120,7 +138,7 @@ namespace Umbraco.Tests.Published
|
||||
_cacheLevel = cacheLevel;
|
||||
}
|
||||
|
||||
public bool IsValue(object value)
|
||||
public bool? IsValue(object value, PropertyValueLevel level)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
|
||||
public bool IsConverter(PublishedPropertyType propertyType)
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace Umbraco.Tests.Published
|
||||
public int SourceConverts { get; private set; }
|
||||
public int InterConverts { get; private set; }
|
||||
|
||||
public bool IsValue(object value)
|
||||
public bool? IsValue(object value, PropertyValueLevel level)
|
||||
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
|
||||
|
||||
public bool IsConverter(PublishedPropertyType propertyType)
|
||||
|
||||
@@ -87,10 +87,34 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_variations = origin._variations;
|
||||
}
|
||||
|
||||
// determines whether a property has value
|
||||
public override bool HasValue(string culture = null, string segment = null)
|
||||
{
|
||||
ContextualizeVariation(ref culture, ref segment);
|
||||
return PropertyType.IsValue(GetSourceValue(culture, segment));
|
||||
|
||||
var value = GetSourceValue(culture, segment);
|
||||
var hasValue = PropertyType.IsValue(value, PropertyValueLevel.Source);
|
||||
if (hasValue.HasValue) return hasValue.Value;
|
||||
|
||||
lock (_locko)
|
||||
{
|
||||
value = GetInterValue(culture, segment);
|
||||
hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter);
|
||||
if (hasValue.HasValue) return hasValue.Value;
|
||||
|
||||
var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
|
||||
|
||||
// initial reference cache level always is .Content
|
||||
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
|
||||
|
||||
if (!cacheValues.ObjectInitialized)
|
||||
{
|
||||
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, value, _isPreviewing);
|
||||
cacheValues.ObjectInitialized = true;
|
||||
}
|
||||
value = cacheValues.ObjectValue;
|
||||
return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
// used to cache the CacheValues of this property
|
||||
|
||||
@@ -37,7 +37,28 @@ namespace Umbraco.Web.PublishedCache
|
||||
}
|
||||
|
||||
public override bool HasValue(string culture = null, string segment = null)
|
||||
=> _sourceValue != null && (!(_sourceValue is string s) || !string.IsNullOrWhiteSpace(s));
|
||||
{
|
||||
var hasValue = PropertyType.IsValue(_sourceValue, PropertyValueLevel.Source);
|
||||
if (hasValue.HasValue) return hasValue.Value;
|
||||
|
||||
GetCacheLevels(out var cacheLevel, out var referenceCacheLevel);
|
||||
|
||||
lock (_locko)
|
||||
{
|
||||
var value = GetInterValue();
|
||||
hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter);
|
||||
if (hasValue.HasValue) return hasValue.Value;
|
||||
|
||||
var cacheValues = GetCacheValues(cacheLevel);
|
||||
if (!cacheValues.ObjectInitialized)
|
||||
{
|
||||
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(Element, referenceCacheLevel, value, IsPreviewing);
|
||||
cacheValues.ObjectInitialized = true;
|
||||
}
|
||||
value = cacheValues.ObjectValue;
|
||||
return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
// used to cache the CacheValues of this property
|
||||
// ReSharper disable InconsistentlySynchronizedField
|
||||
|
||||
Reference in New Issue
Block a user