From 36ffd41007b22ab27195eb32556d85e89cc4bc31 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 24 Oct 2013 16:38:00 +1100 Subject: [PATCH] Updates data type service for the way it saves pre-values so we can maintain ids if necessary - updates the pre-value editors to except a different dictionary which includes an id if necessary. This all fixes: U4-2751 Check that when publishing keys for drop down list prop eds that if we change a value that the id remains constistent --- src/Umbraco.Core/Models/PreValue.cs | 4 + .../PropertyEditors/PreValueEditor.cs | 4 +- src/Umbraco.Core/Services/DataTypeService.cs | 95 +++++++++++-------- src/Umbraco.Core/Services/IDataTypeService.cs | 18 +++- .../prevalueeditors/multivalues.controller.js | 5 +- .../ContentEditing/PreValueFieldDisplay.cs | 5 - .../FileUploadPropertyEditor.cs | 4 +- .../MultipleTextStringPropertyEditor.cs | 4 +- .../ValueListPreValueEditor.cs | 18 ++-- 9 files changed, 99 insertions(+), 58 deletions(-) diff --git a/src/Umbraco.Core/Models/PreValue.cs b/src/Umbraco.Core/Models/PreValue.cs index 3d5f13f5ee..05b7a5f5df 100644 --- a/src/Umbraco.Core/Models/PreValue.cs +++ b/src/Umbraco.Core/Models/PreValue.cs @@ -9,7 +9,11 @@ { Value = value; Id = id; + } + public PreValue(string value) + { + Value = value; } /// diff --git a/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs index 3f25171063..4639b1d068 100644 --- a/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs @@ -112,10 +112,10 @@ namespace Umbraco.Core.PropertyEditors /// This can be overridden if perhaps you have a comma delimited string posted value but want to convert those to individual rows, or to convert /// a json structure to multiple rows. /// - public virtual IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) + public virtual IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) { //convert to a string based value to be saved in the db - return editorValue.ToDictionary(x => x.Key, x => x.Value == null ? null : x.Value.ToString()); + return editorValue.ToDictionary(x => x.Key, x => new PreValue(x.Value == null ? null : x.Value.ToString())); } /// diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 230d01cbde..0ea5250ea8 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -217,6 +217,7 @@ namespace Umbraco.Core.Services /// /// Id of the DataTypeDefinition to save PreValues for /// List of string values to save + [Obsolete("This should no longer be used, use the alternative SavePreValues or SaveDataTypeAndPreValues methods instead. This will only insert pre-values without keys")] public void SavePreValues(int id, IEnumerable values) { //TODO: Should we raise an event here since we are really saving values for the data type? @@ -255,9 +256,10 @@ namespace Umbraco.Core.Services /// /// /// - /// We will actually just remove all pre-values and re-insert them in one transaction + /// We need to actually look up each pre-value and maintain it's id if possible - this is because of silly property editors + /// like 'dropdown list publishing keys' /// - internal void SavePreValues(int id, IDictionary values) + public void SavePreValues(int id, IDictionary values) { //TODO: Should we raise an event here since we are really saving values for the data type? @@ -267,22 +269,7 @@ namespace Umbraco.Core.Services { using (var transaction = uow.Database.GetTransaction()) { - uow.Database.Execute("DELETE FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = id }); - - var sortOrder = 1; - foreach (var value in values) - { - var dto = new DataTypePreValueDto - { - DataTypeNodeId = id, - Value = value.Value, - SortOrder = sortOrder, - Alias = value.Key - }; - uow.Database.Insert(dto); - sortOrder++; - } - + AddOrUpdatePreValues(id, values, uow); transaction.Complete(); } } @@ -295,7 +282,7 @@ namespace Umbraco.Core.Services /// /// /// - public void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary values, int userId = 0) + public void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary values, int userId = 0) { if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) return; @@ -309,25 +296,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdate(dataTypeDefinition); //complete the transaction, but run the delegate before the db transaction is finalized - uow.Commit(database => - { - //Execute this before the transaction is completed! - database.Execute("DELETE FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = dataTypeDefinition.Id }); - - var sortOrder = 1; - foreach (var value in values) - { - var dto = new DataTypePreValueDto - { - DataTypeNodeId = dataTypeDefinition.Id, - Value = value.Value, - SortOrder = sortOrder, - Alias = value.Key - }; - database.Insert(dto); - sortOrder++; - } - }); + uow.Commit(database => AddOrUpdatePreValues(dataTypeDefinition.Id, values, uow)); Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); } @@ -336,6 +305,56 @@ namespace Umbraco.Core.Services Audit.Add(AuditTypes.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } + private void AddOrUpdatePreValues(int id, IDictionary preValueCollection, IDatabaseUnitOfWork uow) + { + //first just get all pre-values for this data type so we can compare them to see if we need to insert or update or replace + var sql = new Sql().Select("*") + .From() + .Where(dto => dto.DataTypeNodeId == id) + .OrderBy(dto => dto.SortOrder); + var currentVals = uow.Database.Fetch(sql).ToArray(); + + //already existing, need to be updated + var valueIds = preValueCollection.Where(x => x.Value.Id > 0).Select(x => x.Value.Id).ToArray(); + var existingByIds = currentVals.Where(x => valueIds.Contains(x.Id)).ToArray(); + + //These ones need to be removed from the db, they no longer exist in the new values + var deleteById = currentVals.Where(x => valueIds.Contains(x.Id) == false); + + foreach (var d in deleteById) + { + uow.Database.Execute( + "DELETE FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId AND id=@Id", + new { DataTypeId = id, Id = d.Id }); + } + + var sortOrder = 1; + + foreach (var pre in preValueCollection) + { + var existing = existingByIds.FirstOrDefault(valueDto => valueDto.Id == pre.Value.Id); + if (existing != null) + { + existing.Value = pre.Value.Value; + existing.SortOrder = sortOrder; + uow.Database.Update(existing); + } + else + { + var dto = new DataTypePreValueDto + { + DataTypeNodeId = id, + Value = pre.Value.Value, + SortOrder = sortOrder, + Alias = pre.Key + }; + uow.Database.Insert(dto); + } + + sortOrder++; + } + } + /// /// Deletes an /// diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index 9b6f23bce3..e57d9016e4 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -10,8 +10,6 @@ namespace Umbraco.Core.Services /// public interface IDataTypeService : IService { - void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary values, int userId = 0); - /// /// Gets a by its Id /// @@ -107,8 +105,24 @@ namespace Umbraco.Core.Services /// /// Id of the DataTypeDefinition to save PreValues for /// List of string values to save + [Obsolete("This should no longer be used, use the alternative SavePreValues or SaveDataTypeAndPreValues methods instead. This will only insert pre-values without keys")] void SavePreValues(int id, IEnumerable values); + /// + /// Saves a list of PreValues for a given DataTypeDefinition + /// + /// Id of the DataTypeDefinition to save PreValues for + /// List of key/value pairs to save + void SavePreValues(int id, IDictionary values); + + /// + /// Saves the data type and it's prevalues + /// + /// + /// + /// + void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary values, int userId = 0); + /// /// Gets a specific PreValue by its Id /// diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.controller.js index bdddef9af4..c8d3c3c4a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/multivalues.controller.js @@ -10,7 +10,10 @@ angular.module("umbraco").controller("Umbraco.Editors.MultiValuesController", //make an array from the dictionary var items = []; for (var i in $scope.model.value) { - items.push({value: $scope.model.value[i]}); + items.push({ + value: $scope.model.value[i], + id: i + }); } //now make the editor model the array $scope.model.value = items; diff --git a/src/Umbraco.Web/Models/ContentEditing/PreValueFieldDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/PreValueFieldDisplay.cs index 64a88af37f..1e8a7ba088 100644 --- a/src/Umbraco.Web/Models/ContentEditing/PreValueFieldDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/PreValueFieldDisplay.cs @@ -8,11 +8,6 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "preValue", Namespace = "")] public class PreValueFieldDisplay : PreValueFieldSave { - ///// - ///// The id of the pre-value field - ///// - //[DataMember(Name = "id", IsRequired = true)] - //public int Id { get; set; } /// /// The name to display for this pre-value field diff --git a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs index 8f230cd9c5..4e90c9c6c0 100644 --- a/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FileUploadPropertyEditor.cs @@ -164,7 +164,7 @@ namespace Umbraco.Web.PropertyEditors /// /// /// - public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) + public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) { var result = base.ConvertEditorToDb(editorValue, currentValue); @@ -173,7 +173,7 @@ namespace Umbraco.Web.PropertyEditors var values = result.Select(item => item.Value).ToList(); result.Clear(); - result.Add("thumbs", string.Join(";", values)); + result.Add("thumbs", new PreValue(string.Join(";", values))); return result; } } diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs index 879b232d3c..8b476adba0 100644 --- a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -56,7 +56,7 @@ namespace Umbraco.Web.PropertyEditors /// /// /// - public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) + public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) { //the values from the editor will be min/max fieds and we need to format to json in one field var min = (editorValue.ContainsKey("min") ? editorValue["min"].ToString() : "0").TryConvertTo(); @@ -64,7 +64,7 @@ namespace Umbraco.Web.PropertyEditors var json = JObject.FromObject(new {Minimum = min.Success ? min.Result : 0, Maximum = max.Success ? max.Result : 0}); - return new Dictionary {{"0", json.ToString(Formatting.None)}}; + return new Dictionary { { "0", new PreValue(json.ToString(Formatting.None)) } }; } /// diff --git a/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs index 717dd4e53f..437e7d4a1b 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs @@ -78,10 +78,10 @@ namespace Umbraco.Web.PropertyEditors /// /// This is mostly because we want to maintain compatibility with v6 drop down property editors that store their prevalues in different db rows. /// - public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) + public override IDictionary ConvertEditorToDb(IDictionary editorValue, PreValueCollection currentValue) { var val = editorValue["items"] as JArray; - var result = new Dictionary(); + var result = new Dictionary(); if (val == null) { @@ -93,12 +93,18 @@ namespace Umbraco.Web.PropertyEditors var index = 0; //get all values in the array that are not empty - foreach (var asString in val.OfType() + foreach (var item in val.OfType() .Where(jItem => jItem["value"] != null) - .Select(jItem => jItem["value"].ToString()) - .Where(asString => asString.IsNullOrWhiteSpace() == false)) + .Select(jItem => new + { + idAsString = jItem["id"] == null ? "0" : jItem["id"].ToString(), + valAsString = jItem["value"].ToString() + }) + .Where(x => x.valAsString.IsNullOrWhiteSpace() == false)) { - result.Add(index.ToInvariantString(), asString); + var id = 0; + int.TryParse(item.idAsString, out id); + result.Add(index.ToInvariantString(), new PreValue(id, item.valAsString)); index++; } }