From 5b289a4971ccb0f6d431377e9e199c6d27bb2839 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Aug 2013 17:24:08 +1000 Subject: [PATCH] Implemented much more for the new drop down property editors to ensure backwards compatibility, had to implement a new required method for property editors to format their data for cache storage. Changed how the drop down angular list is generated, now it uses a dictionary which is much more elegant with less code. Got drop down (normal) and drop down publishing keys working and saving the correct data types, nearly have drop down list multiple working but going to change the property editors to be able to post object (json) instead of just a string for values. --- src/Umbraco.Core/Models/PreValue.cs | 25 +++++++ src/Umbraco.Core/Models/PreValueCollection.cs | 18 ++--- src/Umbraco.Core/Models/PropertyExtensions.cs | 32 ++------- .../PropertyEditors/ValueEditor.cs | 37 ++++++++++ src/Umbraco.Core/Services/DataTypeService.cs | 26 +++---- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../DropDownPropertyPreValueEditorTests.cs | 36 ++++++++++ .../PropertyEditorValueConverterTests.cs | 1 - .../Services/PreValueConverterTests.cs | 47 ++++++------- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../src/common/mocks/resources/_utils.js | 4 +- .../src/views/prevalueeditors/boolean.html | 4 +- .../src/views/prevalueeditors/hidden.html | 1 + .../views/prevalueeditors/requiredfield.html | 6 +- .../src/views/prevalueeditors/textarea.html | 2 +- .../dropdown/dropdown.controller.js | 31 +++------ .../propertyeditors/dropdown/dropdown.html | 21 ++++-- .../dropdown/dropdown.prevalue.controller.js | 19 +++++- .../dropdown-controller.spec.js | 31 +++------ .../markdowneditor.controller.js | 16 +++-- src/Umbraco.Web/Editors/DataTypeController.cs | 5 +- .../PropertyEditors/DatePropertyEditor.cs | 11 +-- .../DropDownMultiplePreValueEditor.cs | 43 ++++++++++++ .../DropDownMultiplePropertyEditor.cs | 39 +++++++++++ .../PropertyEditors/DropDownPreValueEditor.cs | 33 ++++++++- .../PropertyEditors/DropDownPropertyEditor.cs | 68 ++++++++++++------- .../PropertyEditors/DropDownValueEditor.cs | 59 ++++++++++++++++ .../DropDownWithKeysPropertyEditor.cs | 27 ++++++++ .../PropertyEditors/LabelPropertyEditor.cs | 7 +- .../PropertyEditors/LabelValueEditor.cs | 6 +- .../PropertyEditors/ValueEditorWrapper.cs | 18 +++++ src/Umbraco.Web/Umbraco.Web.csproj | 4 ++ 32 files changed, 501 insertions(+), 178 deletions(-) create mode 100644 src/Umbraco.Core/Models/PreValue.cs create mode 100644 src/Umbraco.Tests/PropertyEditors/DropDownPropertyPreValueEditorTests.cs create mode 100644 src/Umbraco.Web.UI.Client/src/views/prevalueeditors/hidden.html create mode 100644 src/Umbraco.Web/PropertyEditors/DropDownMultiplePreValueEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/DropDownValueEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/ValueEditorWrapper.cs diff --git a/src/Umbraco.Core/Models/PreValue.cs b/src/Umbraco.Core/Models/PreValue.cs new file mode 100644 index 0000000000..4292caf59d --- /dev/null +++ b/src/Umbraco.Core/Models/PreValue.cs @@ -0,0 +1,25 @@ +namespace Umbraco.Core.Models +{ + /// + /// Represents a stored pre-value field value + /// + public class PreValue + { + public PreValue(int id, string value) + { + Value = value; + Id = id; + + } + + /// + /// The value stored for the pre-value field + /// + public string Value { get; private set; } + + /// + /// The database id for the pre-value field value + /// + public int Id { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PreValueCollection.cs b/src/Umbraco.Core/Models/PreValueCollection.cs index 9741869a50..5e27e69594 100644 --- a/src/Umbraco.Core/Models/PreValueCollection.cs +++ b/src/Umbraco.Core/Models/PreValueCollection.cs @@ -16,9 +16,9 @@ namespace Umbraco.Core.Models /// public class PreValueCollection { - private IDictionary _preValuesAsDictionary; - private IEnumerable _preValuesAsArray; - public IEnumerable PreValuesAsArray + private IDictionary _preValuesAsDictionary; + private IEnumerable _preValuesAsArray; + public IEnumerable PreValuesAsArray { get { @@ -31,7 +31,7 @@ namespace Umbraco.Core.Models set { _preValuesAsArray = value; } } - public IDictionary PreValuesAsDictionary + public IDictionary PreValuesAsDictionary { get { @@ -52,25 +52,25 @@ namespace Umbraco.Core.Models get { return _preValuesAsDictionary != null; } } - public PreValueCollection(IEnumerable preVals) + public PreValueCollection(IEnumerable preVals) { _preValuesAsArray = preVals; } - public PreValueCollection(IDictionary preVals) + public PreValueCollection(IDictionary preVals) { _preValuesAsDictionary = preVals; } - internal static IDictionary AsDictionary(PreValueCollection persistedPreVals) + internal static IDictionary AsDictionary(PreValueCollection persistedPreVals) { if (persistedPreVals.IsDictionaryBased) { return persistedPreVals.PreValuesAsDictionary; } - //it's an array so need to format it - var result = new Dictionary(); + //it's an array so need to format it, the alias will just be an iteration + var result = new Dictionary(); var asArray = persistedPreVals.PreValuesAsArray.ToArray(); for (var i = 0; i < asArray.Length; i++) { diff --git a/src/Umbraco.Core/Models/PropertyExtensions.cs b/src/Umbraco.Core/Models/PropertyExtensions.cs index 5e3328de56..8edf32bb41 100644 --- a/src/Umbraco.Core/Models/PropertyExtensions.cs +++ b/src/Umbraco.Core/Models/PropertyExtensions.cs @@ -44,37 +44,19 @@ namespace Umbraco.Core.Models var propertyEditor = PropertyEditorResolver.Current.GetById(property.PropertyType.DataTypeId); if (propertyEditor != null) { + var cacheValue = propertyEditor.ValueEditor.FormatValueForCache(property); + switch (property.PropertyType.DataTypeDatabaseType) { - case DataTypeDatabaseType.Nvarchar: - xmlNode.AppendChild(xd.CreateTextNode(property.Value.ToXmlString())); - break; + case DataTypeDatabaseType.Nvarchar: + case DataTypeDatabaseType.Date: case DataTypeDatabaseType.Integer: - xmlNode.AppendChild(xd.CreateTextNode(property.Value.ToXmlString(property.Value.GetType()))); + xmlNode.AppendChild(xd.CreateTextNode(cacheValue.ToString())); break; case DataTypeDatabaseType.Ntext: //put text in cdata - xmlNode.AppendChild(xd.CreateCDataSection(property.Value.ToXmlString())); - break; - case DataTypeDatabaseType.Date: - //treat dates differently, output the format as xml format - if (property.Value == null) - { - xmlNode.AppendChild(xd.CreateTextNode(string.Empty)); - } - else - { - var date = property.Value.TryConvertTo(); - if (date.Success == false || date.Result == null) - { - xmlNode.AppendChild(xd.CreateTextNode(string.Empty)); - } - else - { - xmlNode.AppendChild(xd.CreateTextNode(date.Result.ToXmlString())); - } - } - break; + xmlNode.AppendChild(xd.CreateCDataSection(cacheValue.ToString())); + break; default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs index ef4b711b66..aba131ab9b 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs @@ -202,5 +202,42 @@ namespace Umbraco.Core.PropertyEditors throw new ArgumentOutOfRangeException(); } } + + /// + /// Converts the property value for use in the front-end cache + /// + /// + /// + public virtual object FormatValueForCache(Property property) + { + if (property.Value == null) + { + return string.Empty; + } + + switch (GetDatabaseType()) + { + case DataTypeDatabaseType.Nvarchar: + case DataTypeDatabaseType.Ntext: + property.Value.ToXmlString(); + return property.Value.ToXmlString(); + case DataTypeDatabaseType.Integer: + return property.Value.ToXmlString(property.Value.GetType()); + case DataTypeDatabaseType.Date: + //treat dates differently, output the format as xml format + if (property.Value == null) + { + return string.Empty; + } + var date = property.Value.TryConvertTo(); + if (date.Success == false || date.Result == null) + { + return string.Empty; + } + return date.Result.ToXmlString(); + default: + throw new ArgumentOutOfRangeException(); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index abfb4255c7..9d0c9a493b 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -143,7 +143,7 @@ namespace Umbraco.Core.Services using (var uow = _uowProvider.GetUnitOfWork()) { var dtos = uow.Database.Fetch("WHERE datatypeNodeId = @Id", new { Id = id }); - var list = dtos.Select(x => new Tuple(x.Id, x.Alias, x.SortOrder, x.Value)).ToList(); + var list = dtos.Select(x => new Tuple(new PreValue(x.Id, x.Value), x.Alias, x.SortOrder)).ToList(); return PreValueConverter.ConvertToPreValuesCollection(list); } @@ -262,7 +262,7 @@ namespace Umbraco.Core.Services /// /// We will actually just remove all pre-values and re-insert them in one transaction /// - internal void SavePreValues(int id, PreValueCollection values) + internal void SavePreValues(int id, IDictionary values) { //TODO: Should we raise an event here since we are really saving values for the data type? @@ -275,12 +275,12 @@ namespace Umbraco.Core.Services uow.Database.Execute("DELETE FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = id }); var sortOrder = 1; - foreach (var value in PreValueCollection.AsDictionary(values)) + foreach (var value in values) { var dto = new DataTypePreValueDto { DataTypeNodeId = id, - Value = (string)value.Value, + Value = value.Value, SortOrder = sortOrder, Alias = value.Key }; @@ -300,7 +300,7 @@ namespace Umbraco.Core.Services /// /// /// - internal void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, PreValueCollection values, int userId = 0) + internal void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary values, int userId = 0) { if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) return; @@ -320,12 +320,12 @@ namespace Umbraco.Core.Services database.Execute("DELETE FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = dataTypeDefinition.Id }); var sortOrder = 1; - foreach (var value in PreValueCollection.AsDictionary(values)) + foreach (var value in values) { var dto = new DataTypePreValueDto { DataTypeNodeId = dataTypeDefinition.Id, - Value = (string)value.Value, + Value = value.Value, SortOrder = sortOrder, Alias = value.Key }; @@ -440,29 +440,29 @@ namespace Umbraco.Core.Services /// /// /// - internal static PreValueCollection ConvertToPreValuesCollection(IEnumerable> list) + internal static PreValueCollection ConvertToPreValuesCollection(IEnumerable> list) { //now we need to determine if they are dictionary based, otherwise they have to be array based - var dictionary = new Dictionary(); + var dictionary = new Dictionary(); //need to check all of the keys, if there's only one and it is empty then it's an array var keys = list.Select(x => x.Item2).Distinct().ToArray(); if (keys.Length == 1 && keys[0].IsNullOrWhiteSpace()) { - return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item4)); + return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item1)); } foreach (var item in list .OrderBy(x => x.Item3) //we'll order them first so we maintain the order index in the dictionary - .GroupBy(x => x.Item2)) + .GroupBy(x => x.Item2)) //group by alias { if (item.Count() > 1) { //if there's more than 1 item per key, then it cannot be a dictionary, just return the array - return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item4)); + return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item1)); } - dictionary.Add(item.Key, item.First().Item4); + dictionary.Add(item.Key, item.First().Item1); } return new PreValueCollection(dictionary); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ba2e662304..47365e2ea1 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -225,6 +225,7 @@ + diff --git a/src/Umbraco.Tests/PropertyEditors/DropDownPropertyPreValueEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/DropDownPropertyPreValueEditorTests.cs new file mode 100644 index 0000000000..833417865c --- /dev/null +++ b/src/Umbraco.Tests/PropertyEditors/DropDownPropertyPreValueEditorTests.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Web.PropertyEditors; + +namespace Umbraco.Tests.PropertyEditors +{ + [TestFixture] + public class DropDownPropertyPreValueEditorTests + { + [Test] + public void Format_Data_For_Editor() + { + + var defaultVals = new Dictionary(); + var persisted = new PreValueCollection(new Dictionary + { + {"item1", new PreValue(1, "Item 1")}, + {"item2", new PreValue(2, "Item 2")}, + {"item3", new PreValue(3, "Item 3")} + }); + + var editor = new DropDownPreValueEditor(); + + var result = editor.FormatDataForEditor(defaultVals, persisted); + + Assert.AreEqual(1, result.Count); + Assert.IsTrue(result.ContainsKey("items")); + var items = result["items"] as IDictionary; + Assert.IsNotNull(items); + Assert.AreEqual("Item 1", items[1]); + Assert.AreEqual("Item 2", items[2]); + Assert.AreEqual("Item 3", items[3]); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs index 989895f50d..c151ccc7a8 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; diff --git a/src/Umbraco.Tests/Services/PreValueConverterTests.cs b/src/Umbraco.Tests/Services/PreValueConverterTests.cs index d02544052b..7a326b7fd9 100644 --- a/src/Umbraco.Tests/Services/PreValueConverterTests.cs +++ b/src/Umbraco.Tests/Services/PreValueConverterTests.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using Umbraco.Core.Models; using Umbraco.Core.Services; namespace Umbraco.Tests.Services @@ -12,12 +13,12 @@ namespace Umbraco.Tests.Services [Test] public void Can_Convert_To_Dictionary_Pre_Value_Collection() { - var list = new List> + var list = new List> { - new Tuple(10, "key1", 0, "value1"), - new Tuple(11, "key2", 3, "value2"), - new Tuple(12, "key3", 2, "value3"), - new Tuple(13, "key4", 1, "value4") + new Tuple(new PreValue(10, "value1"), "key1", 0), + new Tuple(new PreValue(11, "value2"), "key2", 3), + new Tuple(new PreValue(12, "value3"), "key3", 2), + new Tuple(new PreValue(13, "value4"), "key4", 1) }; var result = DataTypeService.PreValueConverter.ConvertToPreValuesCollection(list); @@ -38,12 +39,12 @@ namespace Umbraco.Tests.Services [Test] public void Can_Convert_To_Array_Pre_Value_Collection_When_Empty_Key() { - var list = new List> + var list = new List> { - new Tuple(10, "", 0, "value1"), - new Tuple(11, "", 3, "value2"), - new Tuple(12, "", 2, "value3"), - new Tuple(13, "", 1, "value4") + new Tuple(new PreValue(10, "value1"), "", 0), + new Tuple(new PreValue(11, "value2"), "", 3), + new Tuple(new PreValue(12, "value3"), "", 2), + new Tuple(new PreValue(13, "value4"), "", 1) }; var result = DataTypeService.PreValueConverter.ConvertToPreValuesCollection(list); @@ -54,22 +55,22 @@ namespace Umbraco.Tests.Services }); Assert.AreEqual(4, result.PreValuesAsArray.Count()); - Assert.AreEqual("value1", result.PreValuesAsArray.ElementAt(0)); - Assert.AreEqual("value4", result.PreValuesAsArray.ElementAt(1)); - Assert.AreEqual("value3", result.PreValuesAsArray.ElementAt(2)); - Assert.AreEqual("value2", result.PreValuesAsArray.ElementAt(3)); + Assert.AreEqual("value1", result.PreValuesAsArray.ElementAt(0).Value); + Assert.AreEqual("value4", result.PreValuesAsArray.ElementAt(1).Value); + Assert.AreEqual("value3", result.PreValuesAsArray.ElementAt(2).Value); + Assert.AreEqual("value2", result.PreValuesAsArray.ElementAt(3).Value); } [Test] public void Can_Convert_To_Array_Pre_Value_Collection() { - var list = new List> + var list = new List> { - new Tuple(10, "key1", 0, "value1"), - new Tuple(11, "key1", 3, "value2"), - new Tuple(12, "key3", 2, "value3"), - new Tuple(13, "key4", 1, "value4") + new Tuple(new PreValue(10, "value1"), "key1", 0), + new Tuple(new PreValue(11, "value2"), "key1", 3), + new Tuple(new PreValue(12, "value3"), "key3", 2), + new Tuple(new PreValue(13, "value4"), "key4", 1) }; var result = DataTypeService.PreValueConverter.ConvertToPreValuesCollection(list); @@ -80,10 +81,10 @@ namespace Umbraco.Tests.Services }); Assert.AreEqual(4, result.PreValuesAsArray.Count()); - Assert.AreEqual("value1", result.PreValuesAsArray.ElementAt(0)); - Assert.AreEqual("value4", result.PreValuesAsArray.ElementAt(1)); - Assert.AreEqual("value3", result.PreValuesAsArray.ElementAt(2)); - Assert.AreEqual("value2", result.PreValuesAsArray.ElementAt(3)); + Assert.AreEqual("value1", result.PreValuesAsArray.ElementAt(0).Value); + Assert.AreEqual("value4", result.PreValuesAsArray.ElementAt(1).Value); + Assert.AreEqual("value3", result.PreValuesAsArray.ElementAt(2).Value); + Assert.AreEqual("value2", result.PreValuesAsArray.ElementAt(3).Value); } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index a05aeee774..2fedf23c7e 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -213,6 +213,7 @@ + diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js index 675203cd0e..d5f9e264ff 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js @@ -24,13 +24,13 @@ angular.module('umbraco.mocks'). { label: "Custom pre value 1 for editor " + selectedId, description: "Enter a value for this pre-value", - key: "myPreVal", + key: "myPreVal1", view: "requiredfield" }, { label: "Custom pre value 2 for editor " + selectedId, description: "Enter a value for this pre-value", - key: "myPreVal", + key: "myPreVal2", view: "requiredfield" } ] diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.html index c709cfe232..8af17a18ea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/boolean.html @@ -1,3 +1 @@ - - -{{model | json}} \ No newline at end of file + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/hidden.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/hidden.html new file mode 100644 index 0000000000..758ba68fd7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/hidden.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/requiredfield.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/requiredfield.html index 14c06d29d6..2d1f3dde42 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/requiredfield.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/requiredfield.html @@ -1,9 +1,9 @@ 
- - Required - {{propertyForm.requiredField.errorMsg}} + Required + {{propertyForm.requiredField.errorMsg}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/textarea.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/textarea.html index e6c0177924..2321f3dd8d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/textarea.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/textarea.html @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js index ce75a5ab04..483f1ef5cd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.controller.js @@ -4,9 +4,7 @@ angular.module("umbraco").controller("Umbraco.Editors.DropdownController", //setup the default config var config = { items: [], - multiple: false, - keyName: "alias", - valueName: "name" + multiple: false }; //map the user config @@ -14,25 +12,16 @@ angular.module("umbraco").controller("Umbraco.Editors.DropdownController", //map back to the model $scope.model.config = config; - $scope.selectExpression = "e." + config.keyName + " as e." + config.valueName + " for e in model.config.items"; - - //now we need to format the items in the array because we always want to have a dictionary - for (var i = 0; i < $scope.model.config.items.length; i++) { - if (angular.isString($scope.model.config.items[i])) { - //convert to key/value - var keyVal = {}; - keyVal[$scope.model.config.keyName] = $scope.model.config.items[i]; - keyVal[$scope.model.config.valueName] = $scope.model.config.items[i]; - $scope.model.config.items[i] = keyVal; - } - else if (angular.isObject($scope.model.config.items[i])) { - if ($scope.model.config.items[i][$scope.model.config.keyName] === undefined || $scope.model.config.items[i][$scope.model.config.valueName] === undefined) { - throw "All objects in the items array must have a key with a name " + $scope.model.config.keyName + " and a value with a name " + $scope.model.config.valueName; - } - } - else { - throw "The items array must contain either a key value pair or a string"; + if (angular.isArray($scope.model.config.items)) { + //now we need to format the items in the array because we always want to have a dictionary + var newItems = {}; + for (var i = 0; i < $scope.model.config.items.length; i++) { + newItems[$scope.model.config.items[i]] = $scope.model.config.items[i]; } + $scope.model.config.items = newItems; + } + else if (!angular.isObject($scope.model.config.items)) { + throw "The items property must be either an array or a dictionary"; } //now we need to check if the value is null/undefined, if it is we need to set it to "" so that any value that is set diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html index a4e7079c1b..f27183c052 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.html @@ -1,6 +1,19 @@ -
+
+ + ng-switch-default + ng-model="model.value" + ng-options="key as val for (key, val) in model.config.items"> + + + + +
- \ No newline at end of file + +{{model.value | json}} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.controller.js index 6f10385cfb..2d3d208a35 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/dropdown/dropdown.prevalue.controller.js @@ -3,14 +3,31 @@ angular.module("umbraco").controller("Umbraco.Editors.DropdownPreValueController $scope.newItem = ""; $scope.hasError = false; + + if (!angular.isArray($scope.model.value)) { + //make an array from the dictionary + var items = []; + for (var i in $scope.model.value) { + items.push($scope.model.value[i]); + } + //now make the editor model the array + $scope.model.value = items; + } $scope.remove = function(item, evt) { evt.preventDefault(); - $scope.model.value = _.reject($scope.model.value, function(i) { + $scope.model.value = _.reject($scope.model.value, function (i) { return i === item; }); + + //setup the dictionary from array + $scope.model.value = {}; + for (var i = 0; i < $scope.model.value.length; i++) { + //just make the key the iteration + $scope.model.value[i] = $scope.model.value[i]; + } }; $scope.add = function (evt) { diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js index 017571354a..e12d3686c7 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/dropdown-controller.spec.js @@ -22,8 +22,6 @@ describe('Drop down controller tests', function () { expect(scope.model.config).toBeDefined(); expect(scope.model.config.items).toBeDefined(); expect(scope.model.config.multiple).toBeDefined(); - expect(scope.model.config.keyName).toBeDefined(); - expect(scope.model.config.valueName).toBeDefined(); }); it("should convert simple array to dictionary", function () { @@ -39,14 +37,9 @@ describe('Drop down controller tests', function () { $routeParams: routeParams }); - expect(scope.model.config.items.length).toBe(3); - for (var i = 0; i < scope.model.config.items.length; i++) { - expect(scope.model.config.items[i].alias).toBeDefined(); - expect(scope.model.config.items[i].name).toBeDefined(); - //name and alias will be the same in this case - expect(scope.model.config.items[i].alias).toBe("value" + i); - expect(scope.model.config.items[i].name).toBe("value" + i); - } + expect(scope.model.config.items["value0"]).toBe("value0"); + expect(scope.model.config.items["value1"]).toBe("value1"); + expect(scope.model.config.items["value2"]).toBe("value2"); }); @@ -54,11 +47,11 @@ describe('Drop down controller tests', function () { scope.model = { config: { - items: [ - { alias: "value0", name: "Value 0" }, - { alias: "value1", name: "Value 1" }, - { alias: "value2", name: "Value 2" } - ] + items: { + "value0" : "Value 0", + "value1" : "Value 1", + "value2" : "Value 2" + } } }; @@ -73,15 +66,11 @@ describe('Drop down controller tests', function () { }); - it("should not allow an existing invalid dictionary", function () { + it("should not allow a non-array or non-dictionary", function () { scope.model = { config: { - items: [ - { blah: "value0", name: "Value 0" }, - { blah: "value1", name: "Value 1" }, - { blah: "value2", name: "Value 2" } - ] + items: true } }; diff --git a/src/Umbraco.Web.UI/App_Plugins/MarkdownEditor/markdowneditor.controller.js b/src/Umbraco.Web.UI/App_Plugins/MarkdownEditor/markdowneditor.controller.js index 23d54869ca..7d8d2faf9f 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MarkdownEditor/markdowneditor.controller.js +++ b/src/Umbraco.Web.UI/App_Plugins/MarkdownEditor/markdowneditor.controller.js @@ -1,7 +1,7 @@ angular.module("umbraco") .controller("My.MarkdownEditorController", //inject umbracos assetsServce and dialog service -function ($scope, assetsService, dialogService, $log, umbImageHelper) { +function ($scope, assetsService, dialogService, $log, imageHelper) { //tell the assets service to load the markdown.editor libs from the markdown editors //plugin folder @@ -17,7 +17,7 @@ function ($scope, assetsService, dialogService, $log, umbImageHelper) { "/app_plugins/markdowneditor/lib/markdown.editor.js" ]) .then(function () { - + //this function will execute when all dependencies have loaded var converter2 = new Markdown.Converter(); var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias); @@ -28,16 +28,18 @@ function ($scope, assetsService, dialogService, $log, umbImageHelper) { dialogService.mediaPicker({ callback: function (data) { - $(data.selection).each(function (i, item) { - var imagePropVal = umbImageHelper.getImagePropertyVaue({ imageModel: item, scope: $scope }); - callback(imagePropVal); - }); - } + $(data.selection).each(function (i, item) { + var imagePropVal = imageHelper.getImagePropertyVaue({ imageModel: item, scope: $scope }); + callback(imagePropVal); + }); + } }); return true; // tell the editor that we'll take care of getting the image url }); + }); + //load the seperat css for the editor to avoid it blocking our js loading TEMP HACK assetsService.loadCss("/app_plugins/markdowneditor/lib/markdown.css"); }); \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 3246f8b86a..ba87ed07c3 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -118,11 +118,8 @@ namespace Umbraco.Web.Editors preValDictionary, currVal); - //create the pre-value collection to be saved - var preVals = new PreValueCollection(formattedVal.ToDictionary(x => x.Key, x => x.Value)); - //save the data type - dtService.SaveDataTypeAndPreValues(dataType.PersistedDataType, preVals, (int)Security.CurrentUser.Id); + dtService.SaveDataTypeAndPreValues(dataType.PersistedDataType, formattedVal, (int)Security.CurrentUser.Id); var display = Mapper.Map(dataType.PersistedDataType); display.AddSuccessNotification(ui.Text("speechBubbles", "dataTypeSaved"), ""); diff --git a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs index 324b9d6b4c..aaa3c86c10 100644 --- a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -33,21 +33,16 @@ namespace Umbraco.Web.PropertyEditors protected override ValueEditor CreateValueEditor() { - var baseEditor = base.CreateValueEditor(); - - return new DateValueEditor - { - View = baseEditor.View - }; + return new DateValueEditor(base.CreateValueEditor()); } /// /// CUstom value editor so we can serialize with the correct date format (excluding time) /// and includes the date validator /// - private class DateValueEditor : ValueEditor + private class DateValueEditor : ValueEditorWrapper { - public DateValueEditor() + public DateValueEditor(ValueEditor wrapped) : base(wrapped) { Validators = new List { new DateTimeValidator() }; } diff --git a/src/Umbraco.Web/PropertyEditors/DropDownMultiplePreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownMultiplePreValueEditor.cs new file mode 100644 index 0000000000..11c03b1051 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DropDownMultiplePreValueEditor.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// A pre-value editor for the 'drop down list multiple' property editor that ensures that 'multiple' is saved for the config in the db but is not + /// rendered as a pre-value field. + /// + /// + /// This is mostly to maintain backwards compatibility with old property editors. Devs can now simply use the Drop down property editor and check the multiple pre-value checkbox + /// + internal class DropDownMultiplePreValueEditor : DropDownPreValueEditor + { + public DropDownMultiplePreValueEditor() + { + var fields = CreatePreValueFields(); + //add the multiple field, we'll make it hidden so it is not seen in the pre-value editor + fields.Add(new PreValueField + { + Key = "multiple", + Name = "multiple", + View = "hidden" + }); + Fields = fields; + } + + /// + /// Always + /// + /// + /// + /// + public override IDictionary FormatDataForEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals) + { + var returnVal = base.FormatDataForEditor(defaultPreVals, persistedPreVals); + //always add the multiple param to true + returnVal["multiple"] = "1"; + return returnVal; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs new file mode 100644 index 0000000000..ffe1882682 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DropDownMultiplePropertyEditor.cs @@ -0,0 +1,39 @@ +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// A property editor to allow multiple selection of pre-defined items + /// + /// + /// Due to maintaining backwards compatibility this data type stores the value as a string which is a comma separated value of the + /// ids of the individual items so we have logic in here to deal with that. + /// + [PropertyEditor(Constants.PropertyEditors.DropDownListMultiple, "Dropdown list multiple", "dropdown")] + public class DropDownMultiplePropertyEditor : DropDownPropertyEditor + { + protected override ValueEditor CreateValueEditor() + { + return new DropDownMultipleValueEditor(base.CreateValueEditor()); + } + + protected override PreValueEditor CreatePreValueEditor() + { + return new DropDownMultiplePreValueEditor(); + } + + } + + internal class DropDownMultipleValueEditor : ValueEditorWrapper + { + public DropDownMultipleValueEditor(ValueEditor wrapped) : base(wrapped) + { + } + + public override object FormatDataForPersistence(Core.Models.Editors.ContentPropertyData editorValue, object currentValue) + { + return base.FormatDataForPersistence(editorValue, currentValue); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DropDownPreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownPreValueEditor.cs index 1db5cc9c3d..b9d6f3fd67 100644 --- a/src/Umbraco.Web/PropertyEditors/DropDownPreValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DropDownPreValueEditor.cs @@ -7,12 +7,42 @@ using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; +using umbraco; namespace Umbraco.Web.PropertyEditors { internal class DropDownPreValueEditor : PreValueEditor { + public DropDownPreValueEditor() + { + Fields = CreatePreValueFields(); + } + + /// + /// Creates the pre-value fields + /// + /// + protected List CreatePreValueFields() + { + return new List + { + new PreValueField + { + Description = "Add and remove values for the drop down list", + //we're going to call this 'items' because we are going to override the + //serialization of the pre-values to ensure that each one gets saved with it's own key + //(new db row per pre-value, thus to maintain backwards compatibility) + + //It's also important to note that by default the dropdown angular controller is expecting the + // config options to come in with a property called 'items' + Key = "items", + Name = ui.Text("editdatatype", "addPrevalue"), + View = "Views/PropertyEditors/dropdown/dropdown.prevalue.html" + } + }; + } + /// /// The editor is expecting a json array for a field with a key named "items" so we need to format the persisted values /// to this format to be used in the editor. @@ -25,7 +55,8 @@ namespace Umbraco.Web.PropertyEditors var dictionary = PreValueCollection.AsDictionary(persistedPreVals); var arrayOfVals = dictionary.Select(item => item.Value).ToList(); - return new Dictionary { { "items", arrayOfVals } }; + //the items list will be a dictionary of it's id -> value we need to use the id for persistence for backwards compatibility + return new Dictionary { { "items", arrayOfVals.ToDictionary(x => x.Id, x => x.Value) } }; } /// diff --git a/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs index 6f8a8429a6..956233dd98 100644 --- a/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DropDownPropertyEditor.cs @@ -1,36 +1,56 @@ -using System.Collections.Generic; +using System; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using umbraco; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.DropDownList, "Dropdown list", "dropdown")] - public class DropDownPropertyEditor : PropertyEditor + /// + /// A property editor to allow the individual selection of pre-defined items. + /// + /// + /// Due to remaining backwards compatible, this stores the id of the drop down item in the database which is why it is marked + /// 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.DropDownList, "Dropdown list", "dropdown", ValueType = "INT")] + public class DropDownPropertyEditor : DropDownWithKeysPropertyEditor { + + /// + /// We need to override the value editor so that we can ensure the string value is published in cache and not the integer ID value. + /// + /// + protected override ValueEditor CreateValueEditor() + { + return new DropDownValueEditor(base.CreateValueEditor()); + } + + } + + /// + /// A property editor to allow the individual selection of pre-defined items. + /// + /// + /// Due to remaining backwards compatible, this stores the id of the drop down item in the database which is why it is marked + /// 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.DropdownlistPublishingKeys, "Dropdown list, publishing keys", "dropdown", ValueType = "INT")] + public class DropDownWithKeysPropertyEditor : PropertyEditor + { + + /// + /// Return a custom pre-value editor + /// + /// protected override PreValueEditor CreatePreValueEditor() { - var editor = new DropDownPreValueEditor - { - Fields = new List - { - new PreValueField - { - Description = "Add and remove values for the drop down list", - //we're going to call this 'items' because we are going to override the - //serialization of the pre-values to ensure that each one gets saved with it's own key - //(new db row per pre-value, thus to maintain backwards compatibility) - - //It's also important to note that by default the dropdown angular controller is expecting the - // config options to come in with a property called 'items' - Key = "items", - Name = ui.Text("editdatatype", "addPrevalue"), - View = "Views/PropertyEditors/dropdown/dropdown.prevalue.html" - } - } - }; - - return editor; + return new DropDownPreValueEditor(); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DropDownValueEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownValueEditor.cs new file mode 100644 index 0000000000..4d6a951c47 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DropDownValueEditor.cs @@ -0,0 +1,59 @@ +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using System.Linq; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// A custom value editor for the drop down so that we can ensure that the 'value' not the ID get's put into cache + /// + /// + /// This is required for legacy/backwards compatibility, otherwise we'd just store the string version and cache the string version without + /// needing additional lookups. + /// + internal class DropDownValueEditor : ValueEditorWrapper + { + private readonly DataTypeService _dataTypeService; + + internal DropDownValueEditor(IDataTypeService dataTypeService, ValueEditor wrapped) + : base(wrapped) + { + _dataTypeService = (DataTypeService)dataTypeService; + } + + public DropDownValueEditor(ValueEditor wrapped) + : this(ApplicationContext.Current.Services.DataTypeService, wrapped) + { + } + + /// + /// Need to lookup the pre-values and put the string version in cache, not the ID (which is what is stored in the db) + /// + /// + /// + public override object FormatValueForCache(Property property) + { + var preValId = property.Value.TryConvertTo(); + if (preValId.Success) + { + var preVals = _dataTypeService.GetPreValuesCollectionByDataTypeId(property.PropertyType.DataTypeDefinitionId); + if (preVals != null) + { + var dictionary = PreValueCollection.AsDictionary(preVals); + if (dictionary.Any(x => x.Value.Id == preValId.Result)) + { + return dictionary.Single(x => x.Value.Id == preValId.Result).Value.Value; + } + + LogHelper.Warn("Could not find a pre value with ID " + preValId + " for property alias " + property.Alias); + } + } + + return base.FormatValueForCache(property); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs new file mode 100644 index 0000000000..d320190b67 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DropDownWithKeysPropertyEditor.cs @@ -0,0 +1,27 @@ +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// A property editor to allow the individual selection of pre-defined items. + /// + /// + /// Due to remaining backwards compatible, this stores the id of the drop down item in the database which is why it is marked + /// 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.DropdownlistPublishingKeys, "Dropdown list, publishing keys", "dropdown", ValueType = "INT")] + public class DropDownWithKeysPropertyEditor : PropertyEditor + { + + /// + /// Return a custom pre-value editor + /// + /// + protected override PreValueEditor CreatePreValueEditor() + { + return new DropDownPreValueEditor(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs index 53c5ee94ad..7d15eedd34 100644 --- a/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/LabelPropertyEditor.cs @@ -11,12 +11,7 @@ namespace Umbraco.Web.PropertyEditors protected override ValueEditor CreateValueEditor() { - var baseEditor = base.CreateValueEditor(); - - return new LabelValueEditor - { - View = baseEditor.View - }; + return new LabelValueEditor(base.CreateValueEditor()); } } diff --git a/src/Umbraco.Web/PropertyEditors/LabelValueEditor.cs b/src/Umbraco.Web/PropertyEditors/LabelValueEditor.cs index 2e514c4463..04d41ff6b1 100644 --- a/src/Umbraco.Web/PropertyEditors/LabelValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/LabelValueEditor.cs @@ -5,8 +5,12 @@ namespace Umbraco.Web.PropertyEditors /// /// Custom value editor to mark it as readonly /// - internal class LabelValueEditor : ValueEditor + internal class LabelValueEditor : ValueEditorWrapper { + public LabelValueEditor(ValueEditor wrapped) : base(wrapped) + { + } + /// /// This editor is for display purposes only, any values bound to it will not be saved back to the database /// diff --git a/src/Umbraco.Web/PropertyEditors/ValueEditorWrapper.cs b/src/Umbraco.Web/PropertyEditors/ValueEditorWrapper.cs new file mode 100644 index 0000000000..06419aa336 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/ValueEditorWrapper.cs @@ -0,0 +1,18 @@ +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Useful when returning a custom value editor when your property editor is attributed, it ensures the attribute + /// values are copied across to your custom value editor. + /// + public class ValueEditorWrapper : ValueEditor + { + public ValueEditorWrapper(ValueEditor wrapped) + { + this.View = wrapped.View; + this.ValueType = wrapped.ValueType; + this.Validators = wrapped.Validators; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 268b6396ee..966d3a52b1 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -318,10 +318,14 @@ + + + +