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); } } }