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 @@ + + + +