diff --git a/src/Umbraco.Abstractions/Models/IPropertyType.cs b/src/Umbraco.Abstractions/Models/IPropertyType.cs index 7c455d3ba5..6ed5dbd1cb 100644 --- a/src/Umbraco.Abstractions/Models/IPropertyType.cs +++ b/src/Umbraco.Abstractions/Models/IPropertyType.cs @@ -75,14 +75,5 @@ namespace Umbraco.Core.Models /// A value indicating whether wildcards are valid. bool SupportsVariation(string culture, string segment, bool wildcards = false); - /// - /// Converts a value assigned to a property. - /// - /// - /// The input value can be pretty much anything, and is converted to the actual CLR type - /// expected by the property (eg an integer if the property values are integers). - /// Throws if the value cannot be converted. - /// - object ConvertAssignedValue(object value); } } diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 50a4b9416a..36efbad404 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -295,7 +295,7 @@ namespace Umbraco.Core.Models if (!PropertyType.SupportsPublishing) throw new NotSupportedException("Property type does not support publishing."); var origValue = pvalue.PublishedValue; - pvalue.PublishedValue = PropertyType.ConvertAssignedValue(pvalue.EditedValue); + pvalue.PublishedValue = ConvertAssignedValue(pvalue.EditedValue); DetectChanges(pvalue.EditedValue, origValue, nameof(Values), PropertyValueComparer, false); } @@ -306,7 +306,7 @@ namespace Umbraco.Core.Models if (!PropertyType.SupportsPublishing) throw new NotSupportedException("Property type does not support publishing."); var origValue = pvalue.PublishedValue; - pvalue.PublishedValue = PropertyType.ConvertAssignedValue(null); + pvalue.PublishedValue = ConvertAssignedValue(null); DetectChanges(pvalue.EditedValue, origValue, nameof(Values), PropertyValueComparer, false); } @@ -324,7 +324,7 @@ namespace Umbraco.Core.Models var (pvalue, change) = GetPValue(culture, segment, true); var origValue = pvalue.EditedValue; - var setValue = PropertyType.ConvertAssignedValue(value); + var setValue = ConvertAssignedValue(value); pvalue.EditedValue = setValue; @@ -380,6 +380,128 @@ namespace Umbraco.Core.Models return (pvalue, change); } + /// + public object ConvertAssignedValue(object value) => TryConvertAssignedValue(value, true, out var converted) ? converted : null; + + /// + /// Tries to convert a value assigned to a property. + /// + /// + /// + /// + private bool TryConvertAssignedValue(object value, bool throwOnError, out object converted) + { + var isOfExpectedType = IsOfExpectedPropertyType(value); + if (isOfExpectedType) + { + converted = value; + return true; + } + + // isOfExpectedType is true if value is null - so if false, value is *not* null + // "garbage-in", accept what we can & convert + // throw only if conversion is not possible + + var s = value.ToString(); + converted = null; + + switch (ValueStorageType) + { + case ValueStorageType.Nvarchar: + case ValueStorageType.Ntext: + { + converted = s; + return true; + } + + case ValueStorageType.Integer: + if (s.IsNullOrWhiteSpace()) + return true; // assume empty means null + var convInt = value.TryConvertTo(); + if (convInt) + { + converted = convInt.Result; + return true; + } + + if (throwOnError) + ThrowTypeException(value, typeof(int), Alias); + return false; + + case ValueStorageType.Decimal: + if (s.IsNullOrWhiteSpace()) + return true; // assume empty means null + var convDecimal = value.TryConvertTo(); + if (convDecimal) + { + // need to normalize the value (change the scaling factor and remove trailing zeros) + // because the underlying database is going to mess with the scaling factor anyways. + converted = convDecimal.Result.Normalize(); + return true; + } + + if (throwOnError) + ThrowTypeException(value, typeof(decimal), Alias); + return false; + + case ValueStorageType.Date: + if (s.IsNullOrWhiteSpace()) + return true; // assume empty means null + var convDateTime = value.TryConvertTo(); + if (convDateTime) + { + converted = convDateTime.Result; + return true; + } + + if (throwOnError) + ThrowTypeException(value, typeof(DateTime), Alias); + return false; + + default: + throw new NotSupportedException($"Not supported storage type \"{ValueStorageType}\"."); + } + } + + private static void ThrowTypeException(object value, Type expected, string alias) + { + throw new InvalidOperationException($"Cannot assign value \"{value}\" of type \"{value.GetType()}\" to property \"{alias}\" expecting type \"{expected}\"."); + } + + /// + /// Determines whether a value is of the expected type for this property type. + /// + /// + /// If the value is of the expected type, it can be directly assigned to the property. + /// Otherwise, some conversion is required. + /// + private bool IsOfExpectedPropertyType(object value) + { + // null values are assumed to be ok + if (value == null) + return true; + + // check if the type of the value matches the type from the DataType/PropertyEditor + // then it can be directly assigned, anything else requires conversion + var valueType = value.GetType(); + switch (ValueStorageType) + { + case ValueStorageType.Integer: + return valueType == typeof(int); + case ValueStorageType.Decimal: + return valueType == typeof(decimal); + case ValueStorageType.Date: + return valueType == typeof(DateTime); + case ValueStorageType.Nvarchar: + return valueType == typeof(string); + case ValueStorageType.Ntext: + return valueType == typeof(string); + default: + throw new NotSupportedException($"Not supported storage type \"{ValueStorageType}\"."); + } + } + + protected override void PerformDeepClone(object clone) { base.PerformDeepClone(clone); diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 2848caf791..65c0ffeade 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -210,128 +210,6 @@ namespace Umbraco.Core.Models return Variations.ValidateVariation(culture, segment, true, wildcards, false); } - /// - /// Determines whether a value is of the expected type for this property type. - /// - /// - /// If the value is of the expected type, it can be directly assigned to the property. - /// Otherwise, some conversion is required. - /// - private bool IsOfExpectedPropertyType(object value) - { - // null values are assumed to be ok - if (value == null) - return true; - - // check if the type of the value matches the type from the DataType/PropertyEditor - // then it can be directly assigned, anything else requires conversion - var valueType = value.GetType(); - switch (ValueStorageType) - { - case ValueStorageType.Integer: - return valueType == typeof(int); - case ValueStorageType.Decimal: - return valueType == typeof(decimal); - case ValueStorageType.Date: - return valueType == typeof(DateTime); - case ValueStorageType.Nvarchar: - return valueType == typeof(string); - case ValueStorageType.Ntext: - return valueType == typeof(string); - default: - throw new NotSupportedException($"Not supported storage type \"{ValueStorageType}\"."); - } - } - - /// - public object ConvertAssignedValue(object value) => TryConvertAssignedValue(value, true, out var converted) ? converted : null; - - /// - /// Tries to convert a value assigned to a property. - /// - /// - /// - /// - private bool TryConvertAssignedValue(object value, bool throwOnError, out object converted) - { - var isOfExpectedType = IsOfExpectedPropertyType(value); - if (isOfExpectedType) - { - converted = value; - return true; - } - - // isOfExpectedType is true if value is null - so if false, value is *not* null - // "garbage-in", accept what we can & convert - // throw only if conversion is not possible - - var s = value.ToString(); - converted = null; - - switch (ValueStorageType) - { - case ValueStorageType.Nvarchar: - case ValueStorageType.Ntext: - { - converted = s; - return true; - } - - case ValueStorageType.Integer: - if (s.IsNullOrWhiteSpace()) - return true; // assume empty means null - var convInt = value.TryConvertTo(); - if (convInt) - { - converted = convInt.Result; - return true; - } - - if (throwOnError) - ThrowTypeException(value, typeof(int), Alias); - return false; - - case ValueStorageType.Decimal: - if (s.IsNullOrWhiteSpace()) - return true; // assume empty means null - var convDecimal = value.TryConvertTo(); - if (convDecimal) - { - // need to normalize the value (change the scaling factor and remove trailing zeros) - // because the underlying database is going to mess with the scaling factor anyways. - converted = convDecimal.Result.Normalize(); - return true; - } - - if (throwOnError) - ThrowTypeException(value, typeof(decimal), Alias); - return false; - - case ValueStorageType.Date: - if (s.IsNullOrWhiteSpace()) - return true; // assume empty means null - var convDateTime = value.TryConvertTo(); - if (convDateTime) - { - converted = convDateTime.Result; - return true; - } - - if (throwOnError) - ThrowTypeException(value, typeof(DateTime), Alias); - return false; - - default: - throw new NotSupportedException($"Not supported storage type \"{ValueStorageType}\"."); - } - } - - private static void ThrowTypeException(object value, Type expected, string alias) - { - throw new InvalidOperationException($"Cannot assign value \"{value}\" of type \"{value.GetType()}\" to property \"{alias}\" expecting type \"{expected}\"."); - } - - /// /// Sanitizes a property type alias. ///