diff --git a/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.mocks.js b/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.mocks.js
index 1c5e60be6a..1a108f7ae3 100644
--- a/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.mocks.js
+++ b/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.mocks.js
@@ -1,4 +1,4 @@
-/*! umbraco - v0.0.1-SNAPSHOT - 2013-06-10
+/*! umbraco - v0.0.1-SNAPSHOT - 2013-06-11
* http://umbraco.github.io/Belle
* Copyright (c) 2013 Per Ploug, Anders Stenteberg & Shannon Deminick;
* Licensed MIT
diff --git a/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.resources.js b/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.resources.js
index 5e13492793..fee6daa5e8 100644
--- a/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.resources.js
+++ b/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.resources.js
@@ -1,4 +1,4 @@
-/*! umbraco - v0.0.1-SNAPSHOT - 2013-06-10
+/*! umbraco - v0.0.1-SNAPSHOT - 2013-06-11
* http://umbraco.github.io/Belle
* Copyright (c) 2013 Per Ploug, Anders Stenteberg & Shannon Deminick;
* Licensed MIT
@@ -26,7 +26,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
return Umbraco.Sys.ServerVariables.contentApiBaseUrl + "PostSave";
}
/** internal method process the saving of data and post processing the result */
- function saveContentItem(content, action) {
+ function saveContentItem(content, action, files) {
var deferred = $q.defer();
//save the active tab id so we can set it when the data is returned.
@@ -39,8 +39,14 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
umbRequestHelper.postMultiPartRequest(
getSaveUrl(content.id),
{ key: "contentItem", value: umbDataFormatter.formatContentPostData(content, action) },
- function (data) {
- //TODO: transform the request callback and add the files associated with the request
+ function (data, formData) {
+ //now add all of the assigned files
+ for (var f in files) {
+ //each item has a property id and the file object, we'll ensure that the id is suffixed to the key
+ // so we know which property it belongs to on the server side
+ formData.append("file_" + files[f].id, files[f].file);
+ }
+
},
function (data, status, headers, config) {
//success callback
@@ -153,13 +159,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
},
/** saves or updates a content object */
- saveContent: function (content, isNew) {
- return saveContentItem(content, "save" + (isNew ? "New" : ""));
+ saveContent: function (content, isNew, files) {
+ return saveContentItem(content, "save" + (isNew ? "New" : ""), files);
},
/** saves and publishes a content object */
- publishContent: function (content, isNew) {
- return saveContentItem(content, "publish" + (isNew ? "New" : ""));
+ publishContent: function (content, isNew, files) {
+ return saveContentItem(content, "publish" + (isNew ? "New" : ""), files);
}
};
@@ -248,6 +254,55 @@ function mediaResource($q, $http) {
return Umbraco.Sys.ServerVariables.mediaApiBaseUrl + "GetChildren?parentId=" + parentId;
}
+ /** internal method to get the api url for publishing */
+ function getSaveUrl() {
+ return Umbraco.Sys.ServerVariables.mediaApiBaseUrl + "PostSave";
+ }
+
+ /** internal method process the saving of data and post processing the result */
+ function saveMediaItem(content, action, files) {
+ var deferred = $q.defer();
+
+ //save the active tab id so we can set it when the data is returned.
+ var activeTab = _.find(content.tabs, function (item) {
+ return item.active;
+ });
+ var activeTabIndex = (activeTab === undefined ? 0 : _.indexOf(content.tabs, activeTab));
+
+ //save the data
+ umbRequestHelper.postMultiPartRequest(
+ getSaveUrl(content.id),
+ { key: "mediaItem", value: umbDataFormatter.formatContentPostData(content, action) },
+ function (data, formData) {
+ //now add all of the assigned files
+ for (var f in files) {
+ //each item has a property id and the file object, we'll ensure that the id is suffixed to the key
+ // so we know which property it belongs to on the server side
+ formData.append("file_" + files[f].id, files[f].file);
+ }
+
+ },
+ function (data, status, headers, config) {
+ //success callback
+
+ //reset the tabs and set the active one
+ _.each(data.tabs, function (item) {
+ item.active = false;
+ });
+ data.tabs[activeTabIndex].active = true;
+
+ //the data returned is the up-to-date data so the UI will refresh
+ deferred.resolve(data);
+ },
+ function (data, status, headers, config) {
+ //failure callback
+
+ deferred.reject('Failed to save data for media id ' + content.id);
+ });
+
+ return deferred.promise;
+ }
+
return {
getById: function (id) {
@@ -303,7 +358,12 @@ function mediaResource($q, $http) {
});
return deferred.promise;
- }
+ },
+
+ /** saves or updates a media object */
+ saveMedia: function (media, isNew, files) {
+ return saveMediaItem(media, "save" + (isNew ? "New" : ""), files);
+ },
};
}
diff --git a/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.services.js b/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.services.js
index 4f483dbf97..249ceeb849 100644
--- a/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.services.js
+++ b/src/Umbraco.Web.UI.Client/build/belle/js/umbraco.services.js
@@ -628,7 +628,7 @@ function umbRequestHelper($http) {
//call the callback
if (transformCallback) {
- transformCallback.apply(this, [formData]);
+ transformCallback.apply(this, [data, formData]);
}
return formData;
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js
index 98cc086be3..4a4f541b16 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js
@@ -18,7 +18,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
return Umbraco.Sys.ServerVariables.contentApiBaseUrl + "PostSave";
}
/** internal method process the saving of data and post processing the result */
- function saveContentItem(content, action) {
+ function saveContentItem(content, action, files) {
var deferred = $q.defer();
//save the active tab id so we can set it when the data is returned.
@@ -31,8 +31,14 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
umbRequestHelper.postMultiPartRequest(
getSaveUrl(content.id),
{ key: "contentItem", value: umbDataFormatter.formatContentPostData(content, action) },
- function (data) {
- //TODO: transform the request callback and add the files associated with the request
+ function (data, formData) {
+ //now add all of the assigned files
+ for (var f in files) {
+ //each item has a property id and the file object, we'll ensure that the id is suffixed to the key
+ // so we know which property it belongs to on the server side
+ formData.append("file_" + files[f].id, files[f].file);
+ }
+
},
function (data, status, headers, config) {
//success callback
@@ -145,13 +151,13 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
},
/** saves or updates a content object */
- saveContent: function (content, isNew) {
- return saveContentItem(content, "save" + (isNew ? "New" : ""));
+ saveContent: function (content, isNew, files) {
+ return saveContentItem(content, "save" + (isNew ? "New" : ""), files);
},
/** saves and publishes a content object */
- publishContent: function (content, isNew) {
- return saveContentItem(content, "publish" + (isNew ? "New" : ""));
+ publishContent: function (content, isNew, files) {
+ return saveContentItem(content, "publish" + (isNew ? "New" : ""), files);
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js
index f56df531f0..35ecf79223 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js
@@ -19,6 +19,55 @@ function mediaResource($q, $http) {
return Umbraco.Sys.ServerVariables.mediaApiBaseUrl + "GetChildren?parentId=" + parentId;
}
+ /** internal method to get the api url for publishing */
+ function getSaveUrl() {
+ return Umbraco.Sys.ServerVariables.mediaApiBaseUrl + "PostSave";
+ }
+
+ /** internal method process the saving of data and post processing the result */
+ function saveMediaItem(content, action, files) {
+ var deferred = $q.defer();
+
+ //save the active tab id so we can set it when the data is returned.
+ var activeTab = _.find(content.tabs, function (item) {
+ return item.active;
+ });
+ var activeTabIndex = (activeTab === undefined ? 0 : _.indexOf(content.tabs, activeTab));
+
+ //save the data
+ umbRequestHelper.postMultiPartRequest(
+ getSaveUrl(content.id),
+ { key: "mediaItem", value: umbDataFormatter.formatContentPostData(content, action) },
+ function (data, formData) {
+ //now add all of the assigned files
+ for (var f in files) {
+ //each item has a property id and the file object, we'll ensure that the id is suffixed to the key
+ // so we know which property it belongs to on the server side
+ formData.append("file_" + files[f].id, files[f].file);
+ }
+
+ },
+ function (data, status, headers, config) {
+ //success callback
+
+ //reset the tabs and set the active one
+ _.each(data.tabs, function (item) {
+ item.active = false;
+ });
+ data.tabs[activeTabIndex].active = true;
+
+ //the data returned is the up-to-date data so the UI will refresh
+ deferred.resolve(data);
+ },
+ function (data, status, headers, config) {
+ //failure callback
+
+ deferred.reject('Failed to save data for media id ' + content.id);
+ });
+
+ return deferred.promise;
+ }
+
return {
getById: function (id) {
@@ -74,7 +123,12 @@ function mediaResource($q, $http) {
});
return deferred.promise;
- }
+ },
+
+ /** saves or updates a media object */
+ saveMedia: function (media, isNew, files) {
+ return saveMediaItem(media, "save" + (isNew ? "New" : ""), files);
+ },
};
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js b/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js
index 1882b9f7b3..5821914796 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/utill.service.js
@@ -43,7 +43,7 @@ function umbRequestHelper($http) {
//call the callback
if (transformCallback) {
- transformCallback.apply(this, [formData]);
+ transformCallback.apply(this, [data, formData]);
}
return formData;
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/contentedit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/contentedit.controller.js
index aa8ba1ff1a..7c03acb194 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/contentedit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/content/contentedit.controller.js
@@ -16,16 +16,27 @@ angular.module("umbraco")
});
}
+ $scope.files = [];
+ $scope.addFiles = function (propertyId, files) {
+ //this will clear the files for the current property and then add the new ones for the current property
+ $scope.files = _.reject($scope.files, function (item) {
+ return item.id == propertyId;
+ });
+ for (var i = 0; i < files.length; i++) {
+ //save the file object to the scope's files collection
+ $scope.files.push({ id: propertyId, file: files[i] });
+ }
+ };
$scope.saveAndPublish = function (cnt) {
cnt.publishDate = new Date();
- contentResource.publishContent(cnt);
+ contentResource.publishContent(cnt, $routeParams.create, $scope.files);
notificationsService.success("Published", "Content has been saved and published");
};
$scope.save = function (cnt) {
cnt.updateDate = new Date();
- contentResource.saveContent(cnt);
+ contentResource.saveContent(cnt, $routeParams.create, $scope.files);
notificationsService.success("Saved", "Content has been saved");
};
});
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/mediaedit.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/mediaedit.controller.js
index 81255adfe4..dc3c0a9a9c 100644
--- a/src/Umbraco.Web.UI.Client/src/views/media/mediaedit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/media/mediaedit.controller.js
@@ -14,9 +14,21 @@ function mediaEditController($scope, $routeParams, mediaResource, notificationsS
});
}
+ $scope.files = [];
+ $scope.addFiles = function (propertyId, files) {
+ //this will clear the files for the current property and then add the new ones for the current property
+ $scope.files = _.reject($scope.files, function (item) {
+ return item.id == propertyId;
+ });
+ for (var i = 0; i < files.length; i++) {
+ //save the file object to the scope's files collection
+ $scope.files.push({ id: propertyId, file: files[i] });
+ }
+ };
+
$scope.save = function (cnt) {
cnt.updateDate = new Date();
- contentResource.saveContent(cnt);
+ mediaResource.saveMedia(cnt, $routeParams.create, $scope.files);
notificationsService.success("Saved", "Media has been saved");
};
}
diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest
index 745d7c7814..17f9cb6aff 100644
--- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest
+++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/Package.manifest
@@ -1,7 +1,7 @@
{
propertyEditors: [
{
- id: "30CA72BD-A349-4386-B935-FA532DF24B4B",
+ id: "5e9b75ae-face-41c8-b47e-5f4b0fd82f83",
name: "Rich Text Editor",
editor: {
view: "~/umbraco/Views/propertyeditors/umbraco/rte/editor.html"
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 223060b141..585948c2cf 100644
--- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/FileUploadPropertyEditor.cs
+++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/FileUploadPropertyEditor.cs
@@ -2,7 +2,7 @@
namespace Umbraco.Web.UI.App_Plugins.MyPackage.PropertyEditors
{
- [PropertyEditor("23A66468-30E2-4537-8039-625F8BC5CA1E", "File upload",
+ [PropertyEditor("5032a6e6-69e3-491d-bb28-cd31cd11086c", "File upload",
"~/App_Plugins/MyPackage/PropertyEditors/Views/FileUploadEditor.html")]
public class FileUploadPropertyEditor : PropertyEditor
{
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 c841478b52..cef520f6c2 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
@@ -8,9 +8,21 @@ define(['namespaceMgr'], function () {
MyPackage.PropertyEditors.FileUploadEditor = function ($scope, $element, $compile) {
//create the property to show the list of files currently saved
- $scope.persistedFiles = _.map(angular.fromJson($scope.model.value), function(item) {
- return item.file;
- });
+ if ($scope.model.value != "") {
+
+ //for legacy data, this will not be an array, just a string so convert to an array
+ if (!$scope.model.value.startsWith('[')) {
+ $scope.model.value = "[file: '" + $scope.model.value + "']";
+ }
+
+ $scope.persistedFiles = _.map(angular.fromJson($scope.model.value), function (item) {
+ return item.file;
+ });
+ }
+ else {
+ $scope.persistedFiles = [];
+ }
+
$scope.clearFiles = false;
@@ -29,6 +41,7 @@ define(['namespaceMgr'], function () {
//if clear files is selected then we'll clear all the files that are about
// to be uploaded
if ($scope.clearFiles) {
+ //TODO: There should be a better way! We don't want to have to know about the parent scope
//clear the parent files collection (we don't want to upload any!)
$scope.$parent.addFiles($scope.model.id, []);
//clear the current files
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 72db8d51a3..9bb6ac0cd9 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -94,11 +94,11 @@ namespace Umbraco.Web.Editors
/// Saves content
///
///
- [ContentItemValidationFilter]
+ [ContentItemValidationFilter(typeof(ContentItemValidationHelper))]
[FileUploadCleanupFilter]
public ContentItemDisplay PostSave(
[ModelBinder(typeof(ContentItemBinder))]
- ContentItemSave contentItem)
+ ContentItemSave contentItem)
{
//If we've reached here it means:
// * Our model has been bound
@@ -123,7 +123,7 @@ namespace Umbraco.Web.Editors
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);
}
diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs
index 1ce23d9cfa..2856b97d56 100644
--- a/src/Umbraco.Web/Editors/MediaController.cs
+++ b/src/Umbraco.Web/Editors/MediaController.cs
@@ -2,11 +2,16 @@
using System.Net;
using System.Net.Http;
using System.Web.Http;
+using System.Web.Http.ModelBinding;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Editors;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using System.Linq;
+using Umbraco.Web.WebApi.Binders;
+using Umbraco.Web.WebApi.Filters;
namespace Umbraco.Web.Editors
{
@@ -68,7 +73,7 @@ namespace Umbraco.Web.Editors
///
/// Returns the root media objects
///
- public IEnumerable> GetRootMedia()
+ public IEnumerable> GetRootMedia()
{
return Services.MediaService.GetRootMedia()
.Select(x => _mediaModelMapper.ToMediaItemSimple(x));
@@ -77,10 +82,55 @@ namespace Umbraco.Web.Editors
///
/// Returns the child media objects
///
- public IEnumerable> GetChildren(int parentId)
+ public IEnumerable> GetChildren(int parentId)
{
return Services.MediaService.GetChildren(parentId)
.Select(x => _mediaModelMapper.ToMediaItemSimple(x));
}
+
+ ///
+ /// Saves content
+ ///
+ ///
+ [ContentItemValidationFilter(typeof(ContentItemValidationHelper))]
+ [FileUploadCleanupFilter]
+ public MediaItemDisplay PostSave(
+ [ModelBinder(typeof(ContentItemBinder))]
+ ContentItemSave mediaItem)
+ {
+ //If we've reached here it means:
+ // * Our model has been bound
+ // * and validated
+ // * any file attachments have been saved to their temporary location for us to use
+ // * we have a reference to the DTO object and the persisted object
+
+ //Now, we just need to save the data
+
+ //Save the property values
+ foreach (var p in mediaItem.ContentDto.Properties)
+ {
+ //get the dbo property
+ var dboProperty = mediaItem.PersistedContent.Properties[p.Alias];
+
+ //create the property data to send to the property editor
+ var d = new Dictionary();
+ //add the files if any
+ var files = mediaItem.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
+ Services.MediaService.Save(mediaItem.PersistedContent);
+
+ //return the updated model
+ return _mediaModelMapper.ToMediaItemDisplay(mediaItem.PersistedContent);
+ }
}
}
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs
index 2b9bc11d6e..be12bab4fb 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemBasic.cs
@@ -11,8 +11,9 @@ namespace Umbraco.Web.Models.ContentEditing
/// A model representing a basic content item
///
[DataContract(Name = "content", Namespace = "")]
- public class ContentItemBasic
+ public class ContentItemBasic
where T: ContentPropertyBasic
+ where TPersisted : IContentBase
{
public ContentItemBasic()
{
@@ -64,7 +65,7 @@ namespace Umbraco.Web.Models.ContentEditing
/// The real persisted content object
///
[JsonIgnore]
- internal IContent PersistedContent { get; set; }
+ internal TPersisted PersistedContent { get; set; }
///
/// The DTO object used to gather all required content data including data type information etc... for use with validation
@@ -74,9 +75,9 @@ namespace Umbraco.Web.Models.ContentEditing
/// instead of having to look up all the data individually.
///
[JsonIgnore]
- internal ContentItemDto ContentDto { get; set; }
+ internal ContentItemDto ContentDto { get; set; }
- protected bool Equals(ContentItemBasic other)
+ protected bool Equals(ContentItemBasic other)
{
return Id == other.Id;
}
@@ -85,7 +86,7 @@ namespace Umbraco.Web.Models.ContentEditing
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
- var other = obj as ContentItemBasic;
+ var other = obj as ContentItemBasic;
return other != null && Equals(other);
}
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs
index 2a68e1caf7..0fc93d9502 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs
@@ -1,13 +1,14 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
+using Umbraco.Core.Models;
namespace Umbraco.Web.Models.ContentEditing
{
///
/// A model representing a content item to be displayed in the back office
///
- public class ContentItemDisplay : TabbedContentItem
+ public class ContentItemDisplay : TabbedContentItem
{
[DataMember(Name = "publishDate")]
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs
index 9e39578fb9..00fb91da2b 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs
@@ -1,9 +1,12 @@
-namespace Umbraco.Web.Models.ContentEditing
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web.Models.ContentEditing
{
///
/// Represents a content item from the database including all of the required data that we need to work with such as data type data
///
- internal class ContentItemDto : ContentItemBasic
+ internal class ContentItemDto : ContentItemBasic
+ where TPersisted : IContentBase
{
}
diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs
index e4efea60b0..6d160cbeb8 100644
--- a/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemSave.cs
@@ -2,13 +2,20 @@
using System.Runtime.Serialization;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
+using Umbraco.Core.Models;
namespace Umbraco.Web.Models.ContentEditing
{
+ public interface IHaveUploadedFiles
+ {
+ List UploadedFiles { get; }
+ }
+
///
/// A model representing a content item to be saved
///
- public class ContentItemSave : ContentItemBasic
+ public class ContentItemSave : ContentItemBasic, IHaveUploadedFiles
+ where TPersisted : IContentBase
{
public ContentItemSave()
{
diff --git a/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs
index 49f1f2bd02..5900aa756d 100644
--- a/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/MediaItemDisplay.cs
@@ -1,12 +1,13 @@
using System;
using System.Runtime.Serialization;
+using Umbraco.Core.Models;
namespace Umbraco.Web.Models.ContentEditing
{
///
/// A model representing a content item to be displayed in the back office
///
- public class MediaItemDisplay : TabbedContentItem
+ public class MediaItemDisplay : TabbedContentItem
{
}
diff --git a/src/Umbraco.Web/Models/ContentEditing/TabbedContentItem.cs b/src/Umbraco.Web/Models/ContentEditing/TabbedContentItem.cs
index 646cc58842..6e1411f62e 100644
--- a/src/Umbraco.Web/Models/ContentEditing/TabbedContentItem.cs
+++ b/src/Umbraco.Web/Models/ContentEditing/TabbedContentItem.cs
@@ -3,11 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;
+using Umbraco.Core.Models;
namespace Umbraco.Web.Models.ContentEditing
{
- public abstract class TabbedContentItem : ContentItemBasic
- where T : ContentPropertyBasic
+ public abstract class TabbedContentItem : ContentItemBasic
+ where T : ContentPropertyBasic where TPersisted : IContentBase
{
protected TabbedContentItem()
{
diff --git a/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs
index b716c39c4f..ca23f95664 100644
--- a/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/BaseContentModelMapper.cs
@@ -20,9 +20,10 @@ namespace Umbraco.Web.Models.Mapping
ProfileMapper = profileMapper;
}
- protected ContentItemDto ToContentItemDtoBase(IContentBase content)
+ protected ContentItemDto ToContentItemDtoBase(IContentBase content)
+ where TPersisted : IContentBase
{
- return CreateContent(content, null, (propertyDto, originalProperty, propEditor) =>
+ return CreateContent, ContentPropertyDto, TPersisted>(content, null, (propertyDto, originalProperty, propEditor) =>
{
propertyDto.Alias = originalProperty.Alias;
propertyDto.Description = originalProperty.PropertyType.Description;
@@ -32,9 +33,10 @@ namespace Umbraco.Web.Models.Mapping
});
}
- protected ContentItemBasic ToContentItemSimpleBase(IContentBase content)
+ protected ContentItemBasic ToContentItemSimpleBase(IContentBase content)
+ where TPersisted : IContentBase
{
- return CreateContent, ContentPropertyBasic>(content, null, null);
+ return CreateContent, ContentPropertyBasic, TPersisted>(content, null, null);
}
protected IList> GetTabs(IContentBase content)
@@ -71,14 +73,15 @@ namespace Umbraco.Web.Models.Mapping
});
return tabs;
- }
+ }
- protected TContent CreateContent(IContentBase content,
- Action contentCreatedCallback = null,
- Action propertyCreatedCallback = null,
- bool createProperties = true)
- where TContent : ContentItemBasic, new()
- where TContentProperty : ContentPropertyBasic, new()
+ protected TContent CreateContent(IContentBase content,
+ Action contentCreatedCallback = null,
+ Action propertyCreatedCallback = null,
+ bool createProperties = true)
+ where TContent : ContentItemBasic, new()
+ where TContentProperty : ContentPropertyBasic, new()
+ where TPersisted : IContentBase
{
var result = new TContent
{
@@ -110,7 +113,12 @@ namespace Umbraco.Web.Models.Mapping
{
//if there is no property editor it means that it is a legacy data type
// we cannot support editing with that so we'll just render the readonly value view.
- display.View = GlobalSettings.Path.EnsureEndsWith('/') + "views/propertyeditors/umbraco/readonlyvalue/readonlyvalue.html";
+ display.View = GlobalSettings.Path.EnsureEndsWith('/') +
+ "views/propertyeditors/umbraco/readonlyvalue/readonlyvalue.html";
+ }
+ else
+ {
+ display.View = propEditor.ValueEditor.View;
}
});
diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
index 5acaaa8156..e3cb35608e 100644
--- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs
@@ -15,9 +15,9 @@ namespace Umbraco.Web.Models.Mapping
{
}
- public ContentItemDto ToContentItemDto(IContent content)
+ public ContentItemDto ToContentItemDto(IContent content)
{
- var result = base.ToContentItemDtoBase(content);
+ var result = base.ToContentItemDtoBase(content);
//NOTE: we don't need this for the dto and it's an extra lookup
//result.ContentTypeAlias = content.ContentType.Alias;
//result.Icon = content.ContentType.Icon;
@@ -25,9 +25,9 @@ namespace Umbraco.Web.Models.Mapping
return result;
}
- public ContentItemBasic ToContentItemSimple(IContent content)
+ public ContentItemBasic ToContentItemSimple(IContent content)
{
- var result = base.ToContentItemSimpleBase(content);
+ var result = base.ToContentItemSimpleBase(content);
result.ContentTypeAlias = content.ContentType.Alias;
result.Icon = content.ContentType.Icon;
result.Updator = ProfileMapper.ToBasicUser(content.GetWriterProfile());
@@ -39,7 +39,7 @@ namespace Umbraco.Web.Models.Mapping
//create the list of tabs for properties assigned to tabs.
var tabs = GetTabs(content);
- var result = CreateContent(content, (display, originalContent) =>
+ var result = CreateContent(content, (display, originalContent) =>
{
//fill in the rest
display.Updator = ProfileMapper.ToBasicUser(content.GetWriterProfile());
diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
index 3d8fc071d1..a97c249315 100644
--- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs
@@ -17,9 +17,9 @@ namespace Umbraco.Web.Models.Mapping
{
}
- public ContentItemDto ToMediaItemDto(IMedia content)
+ public ContentItemDto ToMediaItemDto(IMedia content)
{
- var result = base.ToContentItemDtoBase(content);
+ var result = base.ToContentItemDtoBase(content);
//NOTE: we don't need this for the dto and it's an extra lookup
//result.ContentTypeAlias = content.ContentType.Alias;
//result.Icon = content.ContentType.Icon;
@@ -27,9 +27,9 @@ namespace Umbraco.Web.Models.Mapping
return result;
}
- public ContentItemBasic ToMediaItemSimple(IMedia content)
+ public ContentItemBasic ToMediaItemSimple(IMedia content)
{
- var result = base.ToContentItemSimpleBase(content);
+ var result = base.ToContentItemSimpleBase(content);
result.ContentTypeAlias = content.ContentType.Alias;
result.Icon = content.ContentType.Icon;
return result;
@@ -40,7 +40,7 @@ namespace Umbraco.Web.Models.Mapping
//create the list of tabs for properties assigned to tabs.
var tabs = GetTabs(media);
- var result = CreateContent(media, (display, originalContent) =>
+ var result = CreateContent(media, (display, originalContent) =>
{
//fill in the rest
display.ContentTypeAlias = media.ContentType.Alias;
diff --git a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs
index c3be767a7b..074dbaf37d 100644
--- a/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs
+++ b/src/Umbraco.Web/WebApi/Binders/ContentItemBinder.cs
@@ -78,7 +78,7 @@ namespace Umbraco.Web.WebApi.Binders
///
///
///
- private async Task GetModel(HttpRequestMessage request, MultipartFormDataStreamProvider provider)
+ private async Task> GetModel(HttpRequestMessage request, MultipartFormDataStreamProvider provider)
{
//IMPORTANT!!! We need to ensure the umbraco context here because this is running in an async thread
UmbracoContext.EnsureContext(request.Properties["MS_HttpContext"] as HttpContextBase, ApplicationContext.Current);
@@ -100,7 +100,7 @@ namespace Umbraco.Web.WebApi.Binders
var contentItem = result.FormData["contentItem"];
//transform the json into an object
- var model = JsonConvert.DeserializeObject(contentItem);
+ var model = JsonConvert.DeserializeObject>(contentItem);
//get the files
foreach (var file in result.FileData)
diff --git a/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs
index 1b7dda29fd..37dde4c8fd 100644
--- a/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs
+++ b/src/Umbraco.Web/WebApi/Filters/ContentItemValidationFilterAttribute.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -12,53 +13,35 @@ using Umbraco.Web.Models.Mapping;
namespace Umbraco.Web.WebApi.Filters
{
- ///
- /// Validates the content item
- ///
- ///
- /// There's various validation happening here both value validation and structure validation
- /// to ensure that malicious folks are not trying to post invalid values or to invalid properties.
- ///
- internal class ContentItemValidationFilterAttribute : ActionFilterAttribute
+ internal class ContentItemValidationHelper
+ where TPersisted : IContentBase
{
private readonly ApplicationContext _applicationContext;
- public ContentItemValidationFilterAttribute(ApplicationContext applicationContext)
+ public ContentItemValidationHelper(ApplicationContext applicationContext)
{
_applicationContext = applicationContext;
}
- public ContentItemValidationFilterAttribute()
+ public ContentItemValidationHelper()
: this(ApplicationContext.Current)
{
}
- ///
- /// Returns true so that other filters can execute along with this one
- ///
- public override bool AllowMultiple
+ public void ValidateItem(HttpActionContext actionContext)
{
- get { return true; }
- }
-
- ///
- /// Performs the validation
- ///
- ///
- public override void OnActionExecuting(HttpActionContext actionContext)
- {
- var contentItem = actionContext.ActionArguments["contentItem"] as ContentItemSave;
+ var contentItem = actionContext.ActionArguments["contentItem"] as ContentItemSave;
if (contentItem == null)
{
- actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "No " + typeof(ContentItemSave) + " found in request");
+ actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "No " + typeof(ContentItemSave) + " found in request");
return;
}
//now do each validation step
- if (!ValidateExistingContent(contentItem, actionContext)) return;
- if (!ValidateProperties(contentItem, actionContext)) return;
- if (!ValidateData(contentItem, actionContext)) return;
+ if (ValidateExistingContent(contentItem, actionContext) == false) return;
+ if (ValidateProperties(contentItem, actionContext) == false) return;
+ if (ValidateData(contentItem, actionContext) == false) return;
}
///
@@ -67,8 +50,8 @@ namespace Umbraco.Web.WebApi.Filters
///
///
///
- private bool ValidateExistingContent(ContentItemSave postedItem, HttpActionContext actionContext)
- {
+ private bool ValidateExistingContent(ContentItemBasic postedItem, HttpActionContext actionContext)
+ {
if (postedItem.PersistedContent == null)
{
var message = string.Format("content with id: {0} was not found", postedItem.Id);
@@ -86,7 +69,7 @@ namespace Umbraco.Web.WebApi.Filters
///
///
//private bool ValidateProperties(ContentItemSave postedItem, ContentItemDto realItem, HttpActionContext actionContext)
- private bool ValidateProperties(ContentItemSave postedItem, HttpActionContext actionContext)
+ private bool ValidateProperties(ContentItemBasic postedItem, HttpActionContext actionContext)
{
foreach (var p in postedItem.Properties)
{
@@ -105,7 +88,7 @@ namespace Umbraco.Web.WebApi.Filters
}
//TODO: Validate the property type data
- private bool ValidateData(ContentItemSave postedItem, HttpActionContext actionContext)
+ private bool ValidateData(ContentItemBasic postedItem, HttpActionContext actionContext)
{
foreach (var p in postedItem.ContentDto.Properties)
{
@@ -159,6 +142,44 @@ namespace Umbraco.Web.WebApi.Filters
return actionContext.ModelState.IsValid;
}
+ }
+
+ ///
+ /// Validates the content item
+ ///
+ ///
+ /// There's various validation happening here both value validation and structure validation
+ /// to ensure that malicious folks are not trying to post invalid values or to invalid properties.
+ ///
+ internal class ContentItemValidationFilterAttribute : ActionFilterAttribute
+ {
+ private readonly Type _helperType;
+ private dynamic _helper;
+
+ public ContentItemValidationFilterAttribute(Type helperType)
+ {
+ _helperType = helperType;
+ _helper = Activator.CreateInstance(helperType);
+ }
+
+ ///
+ /// Returns true so that other filters can execute along with this one
+ ///
+ public override bool AllowMultiple
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Performs the validation
+ ///
+ ///
+ public override void OnActionExecuting(HttpActionContext actionContext)
+ {
+ _helper.ValidateItem(actionContext);
+ }
+
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs
index 6b909b9c33..91d42fa123 100644
--- a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs
+++ b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs
@@ -1,7 +1,8 @@
-using System.IO;
-using System.Linq;
+using System.Linq;
using System.Web.Http.Filters;
+using Umbraco.Core.Models;
using Umbraco.Web.Models.ContentEditing;
+using File = System.IO.File;
namespace Umbraco.Web.WebApi.Filters
{
@@ -24,7 +25,7 @@ namespace Umbraco.Web.WebApi.Filters
if (actionExecutedContext.ActionContext.ActionArguments.Any())
{
- var contentItem = actionExecutedContext.ActionContext.ActionArguments.First().Value as ContentItemSave;
+ var contentItem = actionExecutedContext.ActionContext.ActionArguments.First().Value as IHaveUploadedFiles;
if (contentItem != null)
{
//cleanup any files associated