diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs index 2f7d247b36..80f118b58e 100644 --- a/src/Umbraco.Core/Constants-PropertyEditors.cs +++ b/src/Umbraco.Core/Constants-PropertyEditors.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core { /// /// Used to prefix generic properties that are internal content properties - /// + /// public const string InternalGenericPropertiesPrefix = "_umb_"; /// @@ -74,7 +74,7 @@ namespace Umbraco.Core /// [Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")] public const string DictionaryPicker = "17B70066-F764-407D-AB05-3717F1E1C513"; - + /// /// Guid for the Dropdown list datatype. /// @@ -352,7 +352,7 @@ namespace Umbraco.Core /// [Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")] public const string UltimatePicker = "CDBF0B5D-5CB2-445F-BC12-FCAAEC07CF2C"; - + /// /// Guid for the UltraSimpleEditor datatype. /// @@ -369,7 +369,7 @@ namespace Umbraco.Core /// [Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")] public const string UmbracoUserControlWrapper = "D15E1281-E456-4B24-AA86-1DDA3E4299D5"; - + /// /// Guid for the Upload field datatype. /// @@ -419,6 +419,15 @@ namespace Umbraco.Core /// Alias for the email address property editor /// public const string EmailAddressAlias = "Umbraco.EmailAddress"; + + public static class PreValueKeys + { + /// + /// 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 + /// + public const string DataValueType = "umbracoDataValueType"; + } } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/DataTypeDatabaseType.cs b/src/Umbraco.Core/Models/DataTypeDatabaseType.cs index 1db8ac65cb..2fccdc0645 100644 --- a/src/Umbraco.Core/Models/DataTypeDatabaseType.cs +++ b/src/Umbraco.Core/Models/DataTypeDatabaseType.cs @@ -6,10 +6,6 @@ namespace Umbraco.Core.Models /// /// Enum of the various DbTypes for which the Property values are stored /// - /// - /// Object is added to support complex values from PropertyEditors, - /// but will be saved under the Ntext column. - /// [Serializable] [DataContract] public enum DataTypeDatabaseType diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index d7c2eb92a8..f3fbefda4f 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -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 => { diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 0649801a0c..846b907197 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -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; } + /// + /// 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. + /// + /// + internal bool CanHaveDataValueTypeChanged() + { + var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias); + return propertyEditor.PreValueEditor.Fields + .SingleOrDefault(x => x.Key == Constants.PropertyEditors.PreValueKeys.DataValueType) != null; + } + /// /// Validates the Value from a Property according to the validation settings /// diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs index 2f225f1764..d120753185 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.PropertyEditors EditorView = editorView; //defaults - ValueType = "string"; + ValueType = PropertyEditorValueTypes.String; Icon = Constants.Icons.PropertyEditor; Group = "common"; } @@ -33,7 +33,7 @@ namespace Umbraco.Core.PropertyEditors Name = name; //defaults - ValueType = "string"; + ValueType = PropertyEditorValueTypes.String; Icon = Constants.Icons.PropertyEditor; Group = "common"; } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorValueTypes.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorValueTypes.cs new file mode 100644 index 0000000000..0ece42d82b --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorValueTypes.cs @@ -0,0 +1,34 @@ +using System; + +namespace Umbraco.Core.PropertyEditors +{ + public static class PropertyEditorValueTypes + { + // mapped to DataTypeDatabaseType in DataTypeService.OverrideDatabaseTypeIfProvidedInPreValues + // BUT what about those that are not mapped? + // + // also mapped to DataTypeDatabaseType in PropertyValueEditor + // and this time the "+" are mapped + + public const string Date = "DATE"; // +Date + + public const string DateTime = "DATETIME"; // Date + + public const string Decimal = "DECIMAL"; // Decimal + + public const string Integer = "INT"; // Integer + + [Obsolete("Use Integer.", false)] + public const string IntegerAlternative = "INTEGER"; // +Integer + + public const string Json = "JSON"; // +NText + + public const string Text = "TEXT"; // NText + + public const string Time = "TIME"; // +Date + + public const string String = "STRING"; // NVarchar + + public const string Xml = "XML"; // +NText + } +} diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs index af3a560837..0b083025f7 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.PropertyEditors /// public PropertyValueEditor() { - ValueType = "string"; + ValueType = PropertyEditorValueTypes.String; //set a default for validators Validators = new List(); } @@ -123,30 +123,35 @@ namespace Umbraco.Core.PropertyEditors } /// - /// Returns the true DataTypeDatabaseType from the string representation ValueType + /// Returns the true DataTypeDatabaseType from the string representation ValueType. /// /// public DataTypeDatabaseType GetDatabaseType() { - switch (ValueType.ToUpper(CultureInfo.InvariantCulture)) + return GetDatabaseType(ValueType); + } + + public static DataTypeDatabaseType GetDatabaseType(string valueType) + { + switch (valueType.ToUpperInvariant()) { - case "INT": - case "INTEGER": + case PropertyEditorValueTypes.Integer: + case PropertyEditorValueTypes.IntegerAlternative: return DataTypeDatabaseType.Integer; - case "DECIMAL": + case PropertyEditorValueTypes.Decimal: return DataTypeDatabaseType.Decimal; - case "STRING": + case PropertyEditorValueTypes.String: return DataTypeDatabaseType.Nvarchar; - case "TEXT": - case "JSON": - case "XML": + case PropertyEditorValueTypes.Text: + case PropertyEditorValueTypes.Json: + case PropertyEditorValueTypes.Xml: return DataTypeDatabaseType.Ntext; - case "DATETIME": - case "DATE": - case "TIME": + case PropertyEditorValueTypes.DateTime: + case PropertyEditorValueTypes.Date: + case PropertyEditorValueTypes.Time: return DataTypeDatabaseType.Date; default: - throw new FormatException("The ValueType does not match a known value type"); + throw new ArgumentException("Not a valid value type.", "valueType"); } } @@ -238,7 +243,7 @@ namespace Umbraco.Core.PropertyEditors public virtual object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue) { //if it's json but it's empty json, then return null - if (ValueType.InvariantEquals("JSON") && editorValue.Value != null && editorValue.Value.ToString().DetectIsEmptyJson()) + if (ValueType.InvariantEquals(PropertyEditorValueTypes.Json) && editorValue.Value != null && editorValue.Value.ToString().DetectIsEmptyJson()) { return null; } diff --git a/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs b/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs index 33c2c02cb6..b3e8ff1f6f 100644 --- a/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.PropertyEditors { var asString = value.ToString(); - if (editor.ValueEditor.ValueType.InvariantEquals("JSON")) + if (editor.ValueEditor.ValueType.InvariantEquals(PropertyEditorValueTypes.Json)) { if (asString.DetectIsEmptyJson()) { diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs index e5ed8b8b94..c5f0236a1a 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/JsonValueConverter.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters { var propertyEditor = PropertyEditorResolver.Current.GetByAlias(propertyType.PropertyEditorAlias); if (propertyEditor == null) return false; - return propertyEditor.ValueEditor.ValueType.InvariantEquals("json"); + return propertyEditor.ValueEditor.ValueType.InvariantEquals(PropertyEditorValueTypes.Json); } public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 035cfd0ab6..2cdcfc76d5 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -504,6 +504,10 @@ namespace Umbraco.Core.Services if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) return; + // if preValues contain the data type, override the data type definition accordingly + if (values != null && values.ContainsKey(Constants.PropertyEditors.PreValueKeys.DataValueType)) + dataTypeDefinition.DatabaseType = PropertyValueEditor.GetDatabaseType(values[Constants.PropertyEditors.PreValueKeys.DataValueType].Value); + var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) { @@ -523,7 +527,6 @@ namespace Umbraco.Core.Services Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } - /// /// Deletes an /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 055be82ff1..141622687e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -478,6 +478,7 @@ + diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs index 26f1665fe2..8fa332b108 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs @@ -41,8 +41,8 @@ namespace Umbraco.Tests.PropertyEditors var valueEditor = new PropertyValueEditor { - ValueType = "STRING" - }; + ValueType = PropertyEditorValueTypes.String + }; var result = valueEditor.ConvertDbToEditor(prop, prop.PropertyType, new Mock().Object); Assert.AreEqual(isOk, !(result is string)); @@ -73,7 +73,7 @@ namespace Umbraco.Tests.PropertyEditors { var valueEditor = new PropertyValueEditor { - ValueType = "DECIMAL" + ValueType = PropertyEditorValueTypes.Decimal }; var result = valueEditor.TryConvertValueToCrlType("12.34"); @@ -86,7 +86,7 @@ namespace Umbraco.Tests.PropertyEditors { var valueEditor = new PropertyValueEditor { - ValueType = "DECIMAL" + ValueType = PropertyEditorValueTypes.Decimal }; var result = valueEditor.TryConvertValueToCrlType("12,34"); @@ -99,7 +99,7 @@ namespace Umbraco.Tests.PropertyEditors { var valueEditor = new PropertyValueEditor { - ValueType = "DECIMAL" + ValueType = PropertyEditorValueTypes.Decimal }; var result = valueEditor.TryConvertValueToCrlType(string.Empty); @@ -112,20 +112,20 @@ namespace Umbraco.Tests.PropertyEditors { var valueEditor = new PropertyValueEditor { - ValueType = "DATE" - }; + ValueType = PropertyEditorValueTypes.Date + }; var result = valueEditor.TryConvertValueToCrlType("2010-02-05"); Assert.IsTrue(result.Success); Assert.AreEqual(new DateTime(2010, 2, 5), result.Result); } - [TestCase("STRING", "hello", "hello")] - [TestCase("TEXT", "hello", "hello")] - [TestCase("INT", 123, "123")] - [TestCase("INTEGER", 123, "123")] - [TestCase("INTEGER", "", "")] //test empty string for int - [TestCase("DATETIME", "", "")] //test empty string for date + [TestCase(PropertyEditorValueTypes.String, "hello", "hello")] + [TestCase(PropertyEditorValueTypes.Text, "hello", "hello")] + [TestCase(PropertyEditorValueTypes.Integer, 123, "123")] + [TestCase(PropertyEditorValueTypes.IntegerAlternative, 123, "123")] + [TestCase(PropertyEditorValueTypes.Integer, "", "")] //test empty string for int + [TestCase(PropertyEditorValueTypes.DateTime, "", "")] //test empty string for date public void Value_Editor_Can_Serialize_Value(string valueType, object val, string expected) { var prop = new Property(1, Guid.NewGuid(), new PropertyType("test", DataTypeDatabaseType.Nvarchar), val); @@ -145,8 +145,8 @@ namespace Umbraco.Tests.PropertyEditors var value = 12.34M; var valueEditor = new PropertyValueEditor { - ValueType = "DECIMAL" - }; + ValueType = PropertyEditorValueTypes.Decimal + }; var prop = new Property(1, Guid.NewGuid(), new PropertyType("test", DataTypeDatabaseType.Decimal), value); @@ -159,8 +159,8 @@ namespace Umbraco.Tests.PropertyEditors { var valueEditor = new PropertyValueEditor { - ValueType = "DECIMAL" - }; + ValueType = PropertyEditorValueTypes.Decimal + }; var prop = new Property(1, Guid.NewGuid(), new PropertyType("test", DataTypeDatabaseType.Decimal), string.Empty); @@ -174,8 +174,8 @@ namespace Umbraco.Tests.PropertyEditors var now = DateTime.Now; var valueEditor = new PropertyValueEditor { - ValueType = "DATE" - }; + ValueType = PropertyEditorValueTypes.Date + }; var prop = new Property(1, Guid.NewGuid(), new PropertyType("test", DataTypeDatabaseType.Date), now); diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/valuetype.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/valuetype.html new file mode 100644 index 0000000000..59b7c55c19 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/valuetype.html @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 518c27d110..3911ef7338 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -1,4 +1,4 @@ -function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper, userService) { +function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper, userService, navigationService, treeService) { //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content // that isn't created yet, if we continue this will use the parent id in the route params which isn't what @@ -357,7 +357,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie $scope.actionInProgress = true; $scope.bulkStatus = getStatusMsg(0, selected.length); - serial(selected, fn, getStatusMsg, 0).then(function (result) { + return serial(selected, fn, getStatusMsg, 0).then(function (result) { // executes once the whole selection has been processed // in case of an error (caught by serial), result will be the error if (!(result.data && angular.isArray(result.data.notifications))) @@ -414,10 +414,36 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie function performMove(target) { - applySelected( - function (selected, index) { return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }); }, - function (count, total) { return "Moved " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); }, - function (total) { return "Moved " + total + " item" + (total > 1 ? "s" : ""); }); + //NOTE: With the way this applySelected/serial works, I'm not sure there's a better way currently to return + // a specific value from one of the methods, so we'll have to try this way. Even though the first method + // will fire once per every node moved, the destination path will be the same and we need to use that to sync. + var newPath = null; + applySelected( + function(selected, index) { + return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }).then(function(path) { + newPath = path; + return path; + }); + }, + function(count, total) { + return "Moved " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); + }, + function(total) { return "Moved " + total + " item" + (total > 1 ? "s" : ""); }) + .then(function() { + //executes if all is successful, let's sync the tree + if (newPath) { + + //we need to do a double sync here: first refresh the node where the content was moved, + // then refresh the node where the content was moved from + navigationService.syncTree({ tree: target.nodeType, path: newPath, forceReload: true, activate: false }).then(function (args) { + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + if (activeNode) { + navigationService.reloadNode(activeNode); + } + }); + } + }); } $scope.copy = function () { diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index e44fc056da..97d81539d2 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -179,7 +179,7 @@ namespace Umbraco.Web.Models.Mapping Label = localizedText.Localize("content/releaseDate"), Value = display.ReleaseDate.HasValue ? display.ReleaseDate.Value.ToIsoString() : null, //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains('P') ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, + View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View Config = new Dictionary { {"offsetTime", "1"} @@ -192,7 +192,7 @@ namespace Umbraco.Web.Models.Mapping Label = localizedText.Localize("content/unpublishDate"), Value = display.ExpireDate.HasValue ? display.ExpireDate.Value.ToIsoString() : null, //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains('P') ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, + View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View Config = new Dictionary { {"offsetTime", "1"} diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs index 8e5bd20964..4d58060164 100644 --- a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs @@ -4,7 +4,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.ContentPickerAlias, "Content Picker", "INT", "contentpicker", IsParameterEditor = true, Group = "Pickers")] + [PropertyEditor(Constants.PropertyEditors.ContentPickerAlias, "Content Picker", PropertyEditorValueTypes.Integer, "contentpicker", IsParameterEditor = true, Group = "Pickers")] public class ContentPickerPropertyEditor : PropertyEditor { diff --git a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs index 56877a8b22..a98c96e759 100644 --- a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.DateAlias, "Date", "DATE", "datepicker", Icon="icon-calendar")] + [PropertyEditor(Constants.PropertyEditors.DateAlias, "Date", PropertyEditorValueTypes.Date, "datepicker", Icon="icon-calendar")] public class DatePropertyEditor : PropertyEditor { public DatePropertyEditor() diff --git a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs index 2a76c0d50a..64cdba4df6 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.DateTimeAlias, "Date/Time", "datepicker", ValueType = "DATETIME", Icon="icon-time")] + [PropertyEditor(Constants.PropertyEditors.DateTimeAlias, "Date/Time", "datepicker", ValueType = PropertyEditorValueTypes.DateTime, Icon="icon-time")] public class DateTimePropertyEditor : PropertyEditor { public DateTimePropertyEditor() diff --git a/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs index f636e8cdb2..63c1964668 100644 --- a/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DecimalPropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.DecimalAlias, "Decimal", "decimal", "decimal", IsParameterEditor = true)] + [PropertyEditor(Constants.PropertyEditors.DecimalAlias, "Decimal", PropertyEditorValueTypes.Decimal, "decimal", IsParameterEditor = true)] public class DecimalPropertyEditor : PropertyEditor { /// diff --git a/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs index 4b8812b622..115b99dea4 100644 --- a/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web.PropertyEditors /// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the string value is published /// in cache and not the int ID. /// - [PropertyEditor(Constants.PropertyEditors.DropDownListAlias, "Dropdown list", "dropdown", ValueType = "STRING", Group = "lists", Icon = "icon-indent")] + [PropertyEditor(Constants.PropertyEditors.DropDownListAlias, "Dropdown list", "dropdown", ValueType = PropertyEditorValueTypes.String, Group = "lists", Icon = "icon-indent")] public class DropDownPropertyEditor : DropDownWithKeysPropertyEditor { /// diff --git a/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs index cc590a0402..a33115003c 100644 --- a/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.PropertyEditors /// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the INT ID value is published /// in cache and not the string value. /// - [PropertyEditor(Constants.PropertyEditors.DropdownlistPublishingKeysAlias, "Dropdown list, publishing keys", "dropdown", ValueType = "INT", Group = "lists", Icon = "icon-indent")] + [PropertyEditor(Constants.PropertyEditors.DropdownlistPublishingKeysAlias, "Dropdown list, publishing keys", "dropdown", ValueType = PropertyEditorValueTypes.Integer, Group = "lists", Icon = "icon-indent")] public class DropDownWithKeysPropertyEditor : PropertyEditor { diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index e69c3ae94a..6178a4f9a4 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -16,7 +16,7 @@ using UmbracoExamine; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Core.Constants.PropertyEditors.GridAlias, "Grid layout", "grid", HideLabel = true, IsParameterEditor = false, ValueType = "JSON", Group="rich content", Icon="icon-layout")] + [PropertyEditor(Core.Constants.PropertyEditors.GridAlias, "Grid layout", "grid", HideLabel = true, IsParameterEditor = false, ValueType = PropertyEditorValueTypes.Json, Group="rich content", Icon="icon-layout")] public class GridPropertyEditor : PropertyEditor { /// diff --git a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs index 9858217a5b..1072448628 100644 --- a/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ImageCropperPropertyEditor.cs @@ -15,7 +15,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.ImageCropperAlias, "Image Cropper", "imagecropper", ValueType = "JSON", HideLabel = false, Group="media", Icon="icon-crop")] + [PropertyEditor(Constants.PropertyEditors.ImageCropperAlias, "Image Cropper", "imagecropper", ValueType = PropertyEditorValueTypes.Json, HideLabel = false, Group="media", Icon="icon-crop")] public class ImageCropperPropertyEditor : PropertyEditor { diff --git a/src/Umbraco.Web/PropertyEditors/IntegerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/IntegerPropertyEditor.cs index 7e0ac8443f..026aaf115f 100644 --- a/src/Umbraco.Web/PropertyEditors/IntegerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/IntegerPropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.IntegerAlias, "Numeric", "integer", IsParameterEditor = true, ValueType = "integer")] + [PropertyEditor(Constants.PropertyEditors.IntegerAlias, "Numeric", "integer", IsParameterEditor = true, ValueType = PropertyEditorValueTypes.IntegerAlternative)] public class IntegerPropertyEditor : PropertyEditor { /// diff --git a/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs index 325c570344..c5482695a3 100644 --- a/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs @@ -1,17 +1,15 @@ using System.Collections.Generic; -using System.ComponentModel; using System.Linq; -using System.Web.Mvc; +using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.NoEditAlias, "Label", "readonlyvalue", Icon="icon-readonly")] + [PropertyEditor(Constants.PropertyEditors.NoEditAlias, "Label", "readonlyvalue", Icon = "icon-readonly")] public class LabelPropertyEditor : PropertyEditor { - protected override PropertyValueEditor CreateValueEditor() { return new LabelPropertyValueEditor(base.CreateValueEditor()); @@ -21,7 +19,7 @@ namespace Umbraco.Web.PropertyEditors { return new LabelPreValueEditor(); } - + /// /// Custom value editor to mark it as readonly /// @@ -43,19 +41,26 @@ namespace Umbraco.Web.PropertyEditors internal class LabelPreValueEditor : PreValueEditor { + private const string LegacyPropertyEditorValuesKey = "values"; + public LabelPreValueEditor() { Fields.Add(new PreValueField() { HideLabel = true, View = "readonlykeyvalues", - Key = "values" + Key = LegacyPropertyEditorValuesKey }); + + ValueType = PropertyEditorValueTypes.String; } + [PreValueField(Constants.PropertyEditors.PreValueKeys.DataValueType, "Value type", "valuetype")] + public string ValueType { get; set; } + /// - /// Chuck all the values into one field so devs can see what is stored there - we want this in case we've converted a legacy proeprty editor over to a label - /// we should still show the pre-values stored for the data type. + /// Other than for the pre-value fields defined on this property editor, chuck all the values into one field so devs can see what is stored there. + /// We want this in case we've converted a legacy property editor over to a label as we should still show the pre-values stored for the data type. /// /// /// @@ -63,12 +68,81 @@ namespace Umbraco.Web.PropertyEditors public override IDictionary ConvertDbToEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals) { var existing = base.ConvertDbToEditor(defaultPreVals, persistedPreVals); - //convert to a list, easier to enumerate on the editor - var asList = existing.Select(e => new KeyValuePair(e.Key, e.Value)).ToList(); - var result = new Dictionary { { "values", asList } }; + + // Check for a saved value type. If not found set to default string type. + var valueType = PropertyEditorValueTypes.String; + if (existing.ContainsKey(Constants.PropertyEditors.PreValueKeys.DataValueType)) + { + valueType = (string)existing[Constants.PropertyEditors.PreValueKeys.DataValueType]; + } + + // 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(e.Key, e.Value)) + .Where(e => e.Key != Constants.PropertyEditors.PreValueKeys.DataValueType) + .ToList(); + + var result = new Dictionary { { Constants.PropertyEditors.PreValueKeys.DataValueType, valueType } }; + if (asList.Any()) + { + result.Add(LegacyPropertyEditorValuesKey, asList); + } + return result; } - } + /// + /// When saving we want to avoid saving an empty "legacy property editor values" field if there are none. + /// + /// + /// + /// + public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) + { + // notes (from the PR): + // + // "All stemmed from the fact that even though the label property editor could have pre-values (from a legacy type), + // they couldn't up to now be edited and saved through the UI. Now that a "true" pre-value has been added, it can, + // which led to some odd behaviour. + // + // Firstly there would be a pre-value record saved for legacy values even if there aren't any (the key would exist + // but with no value). In that case I remove that pre-value so it's not saved(likely does no harm, but it's not + // necessary - we only need this legacy values pre-value record if there are any). + // + // Secondly if there are legacy values, I found on each save the JSON structure containing them would get repeatedly + // nested (an outer JSON wrapper would be added each time). So what I'm doing is if there are legacy pre-values, + // I'm converting what comes in "wrapped" like (below) into the legacy property editor values." + + if (editorValue.ContainsKey(LegacyPropertyEditorValuesKey)) + { + // If provided value contains an empty legacy property editor values, don't save it + if (editorValue[LegacyPropertyEditorValuesKey] == null) + { + editorValue.Remove(LegacyPropertyEditorValuesKey); + } + else + { + // If provided value contains legacy property editor values, unwrap the value to save so it doesn't get repeatedly nested on saves. + // This is a bit funky - but basically needing to parse out the original value from a JSON structure that is passed in + // looking like: + // Value = {[ + // { + // "Key": "values", + // "Value": { + // + // }} + // ]} + var values = editorValue[LegacyPropertyEditorValuesKey] as JArray; + if (values != null && values.Count == 1 && values.First.Values().Count() == 2) + { + editorValue[LegacyPropertyEditorValuesKey] = values.First.Values().Last(); + } + } + } + + return base.ConvertEditorToDb(editorValue, currentValue); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/MacroContainerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MacroContainerPropertyEditor.cs index 7a8e26aa56..3ac5788dd2 100644 --- a/src/Umbraco.Web/PropertyEditors/MacroContainerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MacroContainerPropertyEditor.cs @@ -9,7 +9,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MacroContainerAlias, "Macro container", "macrocontainer", ValueType = "TEXT", Group="rich content", Icon="icon-settings-alt")] + [PropertyEditor(Constants.PropertyEditors.MacroContainerAlias, "Macro container", "macrocontainer", ValueType = PropertyEditorValueTypes.Text, Group="rich content", Icon="icon-settings-alt")] public class MacroContainerPropertyEditor : PropertyEditor { /// diff --git a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs index 3260d1d1f4..9fa8f2d588 100644 --- a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MarkdownEditorAlias, "Markdown editor", "markdowneditor", ValueType = "TEXT", Icon="icon-code", Group="rich content")] + [PropertyEditor(Constants.PropertyEditors.MarkdownEditorAlias, "Markdown editor", "markdowneditor", ValueType = PropertyEditorValueTypes.Text, Icon="icon-code", Group="rich content")] public class MarkdownPropertyEditor : PropertyEditor { protected override PreValueEditor CreatePreValueEditor() diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs index 1ace077a32..7e966d31ad 100644 --- a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs @@ -9,7 +9,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MediaPickerAlias, "Legacy Media Picker", "INT", "mediapicker", Group="media", Icon="icon-picture")] + [PropertyEditor(Constants.PropertyEditors.MediaPickerAlias, "Legacy Media Picker", PropertyEditorValueTypes.Integer, "mediapicker", Group="media", Icon="icon-picture")] public class MediaPickerPropertyEditor : PropertyEditor { public MediaPickerPropertyEditor() diff --git a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs index 3f4674e636..7dadbfe24c 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs @@ -8,7 +8,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MemberPickerAlias, "Member Picker", "INT", "memberpicker", Group = "People", Icon = "icon-user")] + [PropertyEditor(Constants.PropertyEditors.MemberPickerAlias, "Member Picker", PropertyEditorValueTypes.Integer, "memberpicker", Group = "People", Icon = "icon-user")] public class MemberPickerPropertyEditor : PropertyEditor { } diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs index 1e48529ccb..96288c9857 100644 --- a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -14,7 +14,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MultipleTextstringAlias, "Repeatable textstrings", "multipletextbox", ValueType = "TEXT", Icon="icon-ordered-list", Group="lists")] + [PropertyEditor(Constants.PropertyEditors.MultipleTextstringAlias, "Repeatable textstrings", "multipletextbox", ValueType = PropertyEditorValueTypes.Text, Icon="icon-ordered-list", Group="lists")] public class MultipleTextStringPropertyEditor : PropertyEditor { protected override PropertyValueEditor CreateValueEditor() diff --git a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs index 99fd992fff..2bcd8fcbd8 100644 --- a/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RadioButtonsPropertyEditor.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.PropertyEditors /// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the INT ID value is published /// in cache and not the string value. /// - [PropertyEditor(Constants.PropertyEditors.RadioButtonListAlias, "Radio button list", "radiobuttons", ValueType = "INT", Group="lists", Icon="icon-target")] + [PropertyEditor(Constants.PropertyEditors.RadioButtonListAlias, "Radio button list", "radiobuttons", ValueType = PropertyEditorValueTypes.Integer, Group="lists", Icon="icon-target")] public class RadioButtonsPropertyEditor : DropDownWithKeysPropertyEditor { diff --git a/src/Umbraco.Web/PropertyEditors/RelatedLinksPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RelatedLinksPropertyEditor.cs index 1fc4d7f471..cf9d4100a4 100644 --- a/src/Umbraco.Web/PropertyEditors/RelatedLinksPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RelatedLinksPropertyEditor.cs @@ -8,7 +8,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.RelatedLinksAlias, "Related links", "relatedlinks", ValueType ="JSON", Icon="icon-thumbnail-list", Group="pickers")] + [PropertyEditor(Constants.PropertyEditors.RelatedLinksAlias, "Related links", "relatedlinks", ValueType = PropertyEditorValueTypes.Json, Icon="icon-thumbnail-list", Group="pickers")] public class RelatedLinksPropertyEditor : PropertyEditor { protected override PreValueEditor CreatePreValueEditor() diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index fb6c813bf9..de65a326f7 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.TinyMCEAlias, "Rich Text Editor", "rte", ValueType = "TEXT", HideLabel = false, Group="Rich Content", Icon="icon-browser-window")] + [PropertyEditor(Constants.PropertyEditors.TinyMCEAlias, "Rich Text Editor", "rte", ValueType = PropertyEditorValueTypes.Text, HideLabel = false, Group="Rich Content", Icon="icon-browser-window")] public class RichTextPropertyEditor : PropertyEditor { /// diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs index 33dea851ab..3bec23f004 100644 --- a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.TextboxMultipleAlias, "Textarea", "textarea", IsParameterEditor = true, ValueType = "TEXT", Icon="icon-application-window-alt")] + [PropertyEditor(Constants.PropertyEditors.TextboxMultipleAlias, "Textarea", "textarea", IsParameterEditor = true, ValueType = PropertyEditorValueTypes.Text, Icon="icon-application-window-alt")] public class TextAreaPropertyEditor : PropertyEditor { } diff --git a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs index ab9e906911..0dc181cbc3 100644 --- a/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.TrueFalseAlias, "True/False", "INT", "boolean", IsParameterEditor = true, Group = "Common", Icon="icon-checkbox")] + [PropertyEditor(Constants.PropertyEditors.TrueFalseAlias, "True/False", PropertyEditorValueTypes.Integer, "boolean", IsParameterEditor = true, Group = "Common", Icon="icon-checkbox")] public class TrueFalsePropertyEditor : PropertyEditor { protected override PreValueEditor CreatePreValueEditor() diff --git a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs index 8f66d24425..e4a070cb29 100644 --- a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs @@ -6,7 +6,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.UserPickerAlias, "User picker", "INT", "entitypicker", Group="People", Icon="icon-user")] + [PropertyEditor(Constants.PropertyEditors.UserPickerAlias, "User picker", PropertyEditorValueTypes.Integer, "entitypicker", Group="People", Icon="icon-user")] public class UserPickerPropertyEditor : PropertyEditor { private IDictionary _defaultPreValues;