From c5ff9ddec9d1db53fe939e89dea50ba738702fdc Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 28 May 2013 18:27:11 -1000 Subject: [PATCH] Updated property editors deserialization method to accept more information and original values, updated the upload property editor to now save files in accordance with exactly how we are saving them currently and it is all working... expcept the new uploader can do multiple files. It is now persisting to the database as well. --- .../Models/Editors/ContentPropertyData.cs | 36 +++++++++++ .../PropertyEditors/ValueEditor.cs | 9 ++- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../FileUploadPropertyEditor.cs | 14 +--- .../PropertyEditors/Js/FileUploadEditor.js | 64 +------------------ src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 1 + .../Models/ContentEditing/ContentItemFile.cs | 7 +- .../ContentEditing/ContentPropertyDto.cs | 4 +- .../Models/Mapping/ContentModelMapper.cs | 3 +- .../WebApi/ContentEditorApiController.cs | 25 ++++++-- src/Umbraco.Web/WebApi/ContentItemBinder.cs | 10 ++- .../ContentItemValidationFilterAttribute.cs | 2 +- .../FileUploadCleanupFilterAttribute.cs | 2 +- 13 files changed, 91 insertions(+), 87 deletions(-) create mode 100644 src/Umbraco.Core/Models/Editors/ContentPropertyData.cs diff --git a/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs new file mode 100644 index 0000000000..98afb691e8 --- /dev/null +++ b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Models.Editors +{ + /// + /// Represents data that has been submitted to be saved for a content property + /// + /// + /// This object exists because we may need to save additional data for each property, more than just + /// the string representation of the value being submitted. An example of this is uploaded files. + /// + public class ContentPropertyData + { + public ContentPropertyData(string value, IDictionary additionalData) + { + Value = value; + AdditionalData = new ReadOnlyDictionary(additionalData); + } + + /// + /// The string value submitted for the property + /// + public string Value { get; private set; } + + /// + /// A dictionary containing any additional objects that are related to this property when saving + /// + public ReadOnlyDictionary AdditionalData { get; private set; } + + } +} diff --git a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs index 69a5f83a3b..2b8757357f 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; namespace Umbraco.Core.PropertyEditors { @@ -105,6 +106,10 @@ namespace Umbraco.Core.PropertyEditors /// to an object to be stored in the database. /// /// + /// + /// The current value that has been persisted to the database for this editor. This value may be usesful for + /// how the value then get's deserialized again to be re-persisted. In most cases it will probably not be used. + /// /// /// /// By default this will attempt to automatically convert the string value to the value type supplied by ValueType. @@ -112,9 +117,9 @@ namespace Umbraco.Core.PropertyEditors /// If overridden then the object returned must match the type supplied in the ValueType, otherwise persisting the /// value to the DB will fail when it tries to validate the value type. /// - public virtual object DeserializeValue(string editorValue) + public virtual object DeserializeValue(ContentPropertyData editorValue, object currentValue) { - var result = TryConvertValueToCrlType(editorValue); + var result = TryConvertValueToCrlType(editorValue.Value); if (result.Success == false) { throw new InvalidOperationException("The value " + editorValue + " cannot be converted to the type " + GetDatabaseType()); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1f603f43af..e4f17fdd48 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -197,6 +197,7 @@ + diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/FileUploadPropertyEditor.cs index a1d9a731e4..ca62ca46a0 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/FileUploadPropertyEditor.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.UI.App_Plugins.MyPackage.PropertyEditors { @@ -13,16 +12,9 @@ namespace Umbraco.Web.UI.App_Plugins.MyPackage.PropertyEditors /// protected override ValueEditor CreateValueEditor() { - var editor = base.CreateValueEditor(); + //TODO: Ensure we assign custom validation for uploaded file types! - editor.Validators = new List { new PostcodeValidator() }; - - return editor; + return new FileUploadValueEditor(); } } - - internal class FileUploadValueEditor : ValueEditor - { - - } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/FileUploadEditor.js b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/FileUploadEditor.js index be3f41df08..fc1e73a27d 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/FileUploadEditor.js +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/FileUploadEditor.js @@ -9,7 +9,7 @@ define(['namespaceMgr'], function () { $scope.$on("fileSelected", function(event, args) { $scope.$apply(function() { //assign the file name to the model property - $scope.model.value = args.file.name; + $//scope.model.value = args.file.name; //save the file object to the scope's files collection $scope.files.push({ id: $scope.model.id, file: args.file }); }); @@ -17,64 +17,4 @@ define(['namespaceMgr'], function () { }; -}); - -function Ctrl($scope, $http) { - - //a simple model to bind to and send to the server - $scope.model = { - name: "", - comments: "" - }; - - //an array of files selected - $scope.files = []; - - //listen for the file selected event - $scope.$on("fileSelected", function (event, args) { - $scope.$apply(function () { - //add the file object to the scope's files collection - $scope.files.push(args.file); - }); - }); - - //the save method - $scope.save = function() { - $http({ - method: 'POST', - url: "/Api/SaveStuff", - //IMPORTANT!!! You might think this should be set to 'multipart/form-data' - // but this is not true because when we are sending up files the request - // needs to include a 'boundary' parameter which identifies the boundary - // name between parts in this multi-part request and setting the Content-type - // manually will not set this boundary parameter. For whatever reason, - // setting the Content-type to 'false' will force the request to automatically - // populate the headers properly including the boundary parameter. - headers: { 'Content-Type': false }, - //This method will allow us to change how the data is sent up to the server - // for which we'll need to encapsulate the model data in 'FormData' - transformRequest: function (data) { - var formData = new FormData(); - //need to convert our json object to a string version of json otherwise - // the browser will do a 'toString()' on the object which will result - // in the value '[Object object]' on the server. - formData.append("model", angular.toJson(data.model)); - //now add all of the assigned files - for (var i = 0; i < data.files; i++) { - //add each file to the form data and iteratively name them - formData.append("file" + i, data.files[i]); - } - return formData; - }, - //Create an object that contains the model and files which will be transformed - // in the above transformRequest method - data: { model: $scope.model, files: $scope.files } - }). - success(function (data, status, headers, config) { - alert("success!"); - }). - error(function (data, status, headers, config) { - alert("failed!"); - }); - }; -}; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index a88f88ddca..051ff0cf5e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -286,6 +286,7 @@ + diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemFile.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemFile.cs index 5e7a241897..666d6a5553 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemFile.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemFile.cs @@ -10,9 +10,14 @@ /// public int PropertyId { get; set; } + /// + /// The original file name + /// + public string FileName { get; set; } + /// /// The file path for the uploaded file for where the MultipartFormDataStreamProvider has saved the temp file /// - public string FilePath { get; set; } + public string TempFilePath { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs index e3f8650689..01b9bc1152 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Models.ContentEditing { @@ -11,6 +12,7 @@ namespace Umbraco.Web.Models.ContentEditing public IDataTypeDefinition DataType { get; set; } public string Label { get; set; } public string Alias { get; set; } - public string Description { get; set; } + public string Description { get; set; } + public PropertyEditor PropertyEditor { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index 066e44a4b7..f23c6a2f27 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -58,7 +58,8 @@ namespace Umbraco.Web.Models.Mapping Description = p.PropertyType.Description, Label = p.PropertyType.Name, Id = p.Id, - DataType = _applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(p.PropertyType.DataTypeDefinitionId) + DataType = _applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(p.PropertyType.DataTypeDefinitionId), + PropertyEditor = PropertyEditorResolver.Current.GetById(p.PropertyType.DataTypeId) }).ToList() }; } diff --git a/src/Umbraco.Web/WebApi/ContentEditorApiController.cs b/src/Umbraco.Web/WebApi/ContentEditorApiController.cs index 7a10e52a38..147bd7b421 100644 --- a/src/Umbraco.Web/WebApi/ContentEditorApiController.cs +++ b/src/Umbraco.Web/WebApi/ContentEditorApiController.cs @@ -1,8 +1,12 @@ -using System.Net; +using System.Collections.Generic; +using System.Linq; +using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Http.ModelBinding; using Umbraco.Core; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Mapping; using Umbraco.Web.Mvc; @@ -88,11 +92,22 @@ namespace Umbraco.Web.WebApi //Save the property values foreach (var p in contentItem.ContentDto.Properties) - { - var prop = contentItem.PersistedContent.Properties[p.Alias]; + { + //get the dbo property + var dboProperty = contentItem.PersistedContent.Properties[p.Alias]; - //TODO: We need to get the persistable value from the property editor, not just the posted RAW string value. - prop.Value = p.Value; + //create the property data to send to the property editor + var d = new Dictionary(); + //add the files if any + var files = contentItem.UploadedFiles.Where(x => x.PropertyId == p.Id).ToArray(); + if (files.Any()) + { + d.Add("files", files); + } + var data = new ContentPropertyData(p.Value, d); + + //get the deserialized value from the property editor + dboProperty.Value = p.PropertyEditor.ValueEditor.DeserializeValue(data, dboProperty.Value); } //save the item diff --git a/src/Umbraco.Web/WebApi/ContentItemBinder.cs b/src/Umbraco.Web/WebApi/ContentItemBinder.cs index f7c50ac9d7..eea8d97ddd 100644 --- a/src/Umbraco.Web/WebApi/ContentItemBinder.cs +++ b/src/Umbraco.Web/WebApi/ContentItemBinder.cs @@ -97,6 +97,8 @@ namespace Umbraco.Web.WebApi //get the files foreach (var file in result.FileData) { + //The name that has been assigned in JS has 2 parts and the second part indicates the property id + // for which the file belongs. var parts = file.Headers.ContentDisposition.Name.Trim(new char[] { '\"' }).Split('_'); if (parts.Length != 2) { @@ -115,10 +117,14 @@ namespace Umbraco.Web.WebApi ReasonPhrase = "The request was not formatted correctly the file name's 2nd part must be an integer" }); } + + var fileName = file.Headers.ContentDisposition.FileName.Trim(new char[] {'\"'}); + model.UploadedFiles.Add(new ContentItemFile { - FilePath = file.LocalFileName, - PropertyId = propertyId + TempFilePath = file.LocalFileName, + PropertyId = propertyId, + FileName = fileName }); } diff --git a/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs index a61caccdde..1b7dda29fd 100644 --- a/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs @@ -109,7 +109,7 @@ namespace Umbraco.Web.WebApi.Filters { foreach (var p in postedItem.ContentDto.Properties) { - var editor = PropertyEditorResolver.Current.GetById(p.DataType.ControlId); + var editor = p.PropertyEditor; if (editor == null) { var message = string.Format("The property editor with id: {0} was not found for property with id {1}", p.DataType.ControlId, p.Id); diff --git a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs index 4ec28473d8..6b909b9c33 100644 --- a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs @@ -30,7 +30,7 @@ namespace Umbraco.Web.WebApi.Filters //cleanup any files associated foreach (var f in contentItem.UploadedFiles) { - File.Delete(f.FilePath); + File.Delete(f.TempFilePath); } } }