Change field to which data is saved for a data type if provided in prevalues and handle potential mis-matched types on display

This commit is contained in:
AndyButland
2016-05-10 10:50:59 +02:00
parent 7b6bcd24ae
commit 4f3d82cf10
7 changed files with 115 additions and 24 deletions

View File

@@ -419,6 +419,12 @@ namespace Umbraco.Core
/// Alias for the email address property editor
/// </summary>
public const string EmailAddressAlias = "Umbraco.EmailAddress";
/// <summary>
/// Pre-value name used to indicate a field that can be used to override the database field to which data for the associated
/// property is saved
/// </summary>
public const string DataValueTypePreValueKey = "umbracoDataValueType";
}
}
}

View File

@@ -6,10 +6,6 @@ namespace Umbraco.Core.Models
/// <summary>
/// Enum of the various DbTypes for which the Property values are stored
/// </summary>
/// <remarks>
/// Object is added to support complex values from PropertyEditors,
/// but will be saved under the Ntext column.
/// </remarks>
[Serializable]
[DataContract]
public enum DataTypeDatabaseType

View File

@@ -124,10 +124,54 @@ namespace Umbraco.Core.Models
bool typeValidation = _propertyType.IsPropertyTypeValid(value);
if (typeValidation == false)
throw new Exception(
string.Format(
"Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'",
value == null ? "null" : value.GetType().Name, Alias));
{
// Normally we'll throw an exception here. However if the property is of a type that can have it's data field (dataInt, dataVarchar etc.)
// changed, we might have a value of the now "wrong" type. As of May 2016 Label is the only built-in property editor that supports this.
// In that case we should try to parse the value and return null if that's not possible rather than throwing an exception.
if (value != null && _propertyType.CanHaveDataValueTypeChanged())
{
var stringValue = value.ToString();
switch (_propertyType.DataTypeDatabaseType)
{
case DataTypeDatabaseType.Nvarchar:
case DataTypeDatabaseType.Ntext:
value = stringValue;
break;
case DataTypeDatabaseType.Integer:
int integerValue;
if (int.TryParse(stringValue, out integerValue) == false)
{
// Edge case, but if changed from decimal --> integer, the above tryparse will fail. So we'll try going
// via decimal too to return the integer value rather than zero.
decimal decimalForIntegerValue;
if (decimal.TryParse(stringValue, out decimalForIntegerValue))
{
integerValue = (int)decimalForIntegerValue;
}
}
value = integerValue;
break;
case DataTypeDatabaseType.Decimal:
decimal decimalValue;
decimal.TryParse(stringValue, out decimalValue);
value = decimalValue;
break;
case DataTypeDatabaseType.Date:
DateTime dateValue;
DateTime.TryParse(stringValue, out dateValue);
value = dateValue;
break;
}
}
else
{
throw new Exception(
string.Format(
"Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'",
value == null ? "null" : value.GetType().Name, Alias));
}
}
SetPropertyValueAndDetectChanges(o =>
{

View File

@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
@@ -425,6 +426,19 @@ namespace Umbraco.Core.Models
return false;
}
/// <summary>
/// Checks the underlying property editor prevalues to see if the one that allows changing of the database field
/// to which data is saved (dataInt, dataVarchar etc.) is included. If so that means the field could be changed when the data
/// type is saved.
/// </summary>
/// <returns></returns>
internal bool CanHaveDataValueTypeChanged()
{
var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias);
return propertyEditor.PreValueEditor.Fields
.SingleOrDefault(x => x.Key == Constants.PropertyEditors.DataValueTypePreValueKey) != null;
}
/// <summary>
/// Validates the Value from a Property according to the validation settings
/// </summary>

View File

@@ -130,20 +130,20 @@ namespace Umbraco.Core.PropertyEditors
{
switch (ValueType.ToUpper(CultureInfo.InvariantCulture))
{
case "INT":
case "INTEGER":
case PropertyEditorValueTypes.IntegerType:
case PropertyEditorValueTypes.IntegerTypeAlternative:
return DataTypeDatabaseType.Integer;
case "DECIMAL":
case PropertyEditorValueTypes.DecimalType:
return DataTypeDatabaseType.Decimal;
case "STRING":
case PropertyEditorValueTypes.StringType:
return DataTypeDatabaseType.Nvarchar;
case "TEXT":
case "JSON":
case "XML":
case PropertyEditorValueTypes.TextType:
case PropertyEditorValueTypes.JsonType:
case PropertyEditorValueTypes.XmlType:
return DataTypeDatabaseType.Ntext;
case "DATETIME":
case "DATE":
case "TIME":
case PropertyEditorValueTypes.DateTimeType:
case PropertyEditorValueTypes.DateType:
case PropertyEditorValueTypes.TimeType:
return DataTypeDatabaseType.Date;
default:
throw new FormatException("The ValueType does not match a known value type");

View File

@@ -504,6 +504,8 @@ namespace Umbraco.Core.Services
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(dataTypeDefinition), this))
return;
OverrideDatabaseTypeIfProvidedInPreValues(dataTypeDefinition, values);
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow))
{
@@ -523,6 +525,36 @@ namespace Umbraco.Core.Services
Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id);
}
/// <summary>
/// If the database data field is provided in the pre-values update the data type definition to that instead of the
/// default for the property editor
/// </summary>
/// <param name="dataTypeDefinition"></param>
/// <param name="values"></param>
private static void OverrideDatabaseTypeIfProvidedInPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary<string, PreValue> values)
{
if (values != null && values.ContainsKey(Constants.PropertyEditors.DataValueTypePreValueKey))
{
switch (values[Constants.PropertyEditors.DataValueTypePreValueKey].Value)
{
case PropertyEditorValueTypes.StringType:
dataTypeDefinition.DatabaseType = DataTypeDatabaseType.Nvarchar;
break;
case PropertyEditorValueTypes.IntegerType:
dataTypeDefinition.DatabaseType = DataTypeDatabaseType.Integer;
break;
case PropertyEditorValueTypes.DecimalType:
dataTypeDefinition.DatabaseType = DataTypeDatabaseType.Decimal;
break;
case PropertyEditorValueTypes.DateTimeType:
dataTypeDefinition.DatabaseType = DataTypeDatabaseType.Date;
break;
case PropertyEditorValueTypes.TextType:
dataTypeDefinition.DatabaseType = DataTypeDatabaseType.Ntext;
break;
}
}
}
/// <summary>
/// Deletes an <see cref="IDataTypeDefinition"/>

View File

@@ -41,7 +41,6 @@ namespace Umbraco.Web.PropertyEditors
internal class LabelPreValueEditor : PreValueEditor
{
private const string ValueTypeKey = "valueType";
private const string LegacyPropertyEditorValuesKey = "values";
public LabelPreValueEditor()
@@ -56,7 +55,7 @@ namespace Umbraco.Web.PropertyEditors
ValueType = PropertyEditorValueTypes.StringType;
}
[PreValueField(ValueTypeKey, "Value type", "valuetype")]
[PreValueField(Constants.PropertyEditors.DataValueTypePreValueKey, "Value type", "valuetype")]
public string ValueType { get; set; }
/// <summary>
@@ -72,19 +71,19 @@ namespace Umbraco.Web.PropertyEditors
// Check for a saved value type. If not found set to default string type.
var valueType = PropertyEditorValueTypes.StringType;
if (existing.ContainsKey(ValueTypeKey))
if (existing.ContainsKey(Constants.PropertyEditors.DataValueTypePreValueKey))
{
valueType = (string)existing[ValueTypeKey];
valueType = (string)existing[Constants.PropertyEditors.DataValueTypePreValueKey];
}
// Convert any other values from a legacy property editor to a list, easier to enumerate on the editor.
// Make sure to exclude values defined on the label property editor itself.
var asList = existing
.Select(e => new KeyValuePair<string, object>(e.Key, e.Value))
.Where(e => e.Key != ValueTypeKey)
.Where(e => e.Key != Constants.PropertyEditors.DataValueTypePreValueKey)
.ToList();
var result = new Dictionary<string, object> { { ValueTypeKey, valueType } };
var result = new Dictionary<string, object> { { Constants.PropertyEditors.DataValueTypePreValueKey, valueType } };
if (asList.Any())
{
result.Add("values", asList);