From 0ebe9ec0fa89b32ac48cb240bf7606531d50dcb6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Aug 2013 17:53:31 +1000 Subject: [PATCH] Updated some of the property editor API to ensure that when formatting data for persistence we have access to the pre-values as well since these might need to be used to format the persisted data. Completed the new property editor: Multiple text box and it is saving the data in a backwards compatible format. Changed the internal AsDictionary method of the pre value collection to be a public method called FormatAsDictionary. --- .../Models/Editors/ContentPropertyData.cs | 5 +- src/Umbraco.Core/Models/PreValueCollection.cs | 12 +- .../DelimitedManifestValueValidator.cs | 3 +- .../PropertyEditors/IPropertyValidator.cs | 5 +- .../PropertyEditors/IntegerValidator.cs | 5 +- .../ManifestPropertyValidator.cs | 3 +- .../PropertyEditors/ManifestValueValidator.cs | 3 +- .../PropertyEditors/RegexValidator.cs | 5 +- .../RequiredManifestValueValidator.cs | 3 +- .../PropertyEditors/ColorListValidatorTest.cs | 6 +- .../EnsureUniqueValuesValidatorTest.cs | 12 +- .../colorpicker/colorpicker.controller.js | 13 +- .../multipletextbox.controller.js | 34 ++++ .../multipletextbox/multipletextbox.html | 17 ++ .../PropertyEditors/PostcodeValidator.cs | 12 +- .../Editors/ContentControllerBase.cs | 2 +- .../Editors/ContentPostValidateAttribute.cs | 66 ------- .../Editors/DataTypeValidateAttribute.cs | 2 +- .../Editors/MediaPostValidateAttribute.cs | 79 ++++++++ .../Models/ContentEditing/ContentItemDto.cs | 3 +- .../ContentEditing/ContentPropertyDto.cs | 7 + .../Models/Mapping/ContentModelMapper.cs | 4 +- .../Mapping/ContentPropertyDtoConverter.cs | 3 + .../Models/Mapping/PreValueDisplayResolver.cs | 2 +- .../ColorPickerPropertyEditor.cs | 27 +-- .../PropertyEditors/DateTimeValidator.cs | 5 +- .../MultipleTextStringPropertyEditor.cs | 180 ++++++++++++++++++ .../PublishValueValueEditor.cs | 2 +- .../ValueListPreValueEditor.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 + .../Filters/ContentItemValidationHelper.cs | 2 +- 31 files changed, 390 insertions(+), 138 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.html create mode 100644 src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs create mode 100644 src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs diff --git a/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs index 30693834ef..c60174ec9e 100644 --- a/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs +++ b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs @@ -16,9 +16,10 @@ namespace Umbraco.Core.Models.Editors /// public class ContentPropertyData { - public ContentPropertyData(object value, IDictionary additionalData) + public ContentPropertyData(object value, PreValueCollection preValues, IDictionary additionalData) { Value = value; + PreValues = preValues; AdditionalData = new ReadOnlyDictionary(additionalData); } @@ -29,6 +30,8 @@ namespace Umbraco.Core.Models.Editors /// public object Value { get; private set; } + public PreValueCollection PreValues { get; private set; } + /// /// A dictionary containing any additional objects that are related to this property when saving /// diff --git a/src/Umbraco.Core/Models/PreValueCollection.cs b/src/Umbraco.Core/Models/PreValueCollection.cs index 5e27e69594..c6e07d6e34 100644 --- a/src/Umbraco.Core/Models/PreValueCollection.cs +++ b/src/Umbraco.Core/Models/PreValueCollection.cs @@ -62,16 +62,20 @@ namespace Umbraco.Core.Models _preValuesAsDictionary = preVals; } - internal static IDictionary AsDictionary(PreValueCollection persistedPreVals) + /// + /// Regardless of how the pre-values are stored this will return as a dictionary, it will convert an array based to a dictionary + /// + /// + public IDictionary FormatAsDictionary() { - if (persistedPreVals.IsDictionaryBased) + if (IsDictionaryBased) { - return persistedPreVals.PreValuesAsDictionary; + return PreValuesAsDictionary; } //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(); + var asArray = PreValuesAsArray.ToArray(); for (var i = 0; i < asArray.Length; i++) { result.Add(i.ToInvariantString(), asArray[i]); diff --git a/src/Umbraco.Core/PropertyEditors/DelimitedManifestValueValidator.cs b/src/Umbraco.Core/PropertyEditors/DelimitedManifestValueValidator.cs index 752668064b..5887a83524 100644 --- a/src/Umbraco.Core/PropertyEditors/DelimitedManifestValueValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/DelimitedManifestValueValidator.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { @@ -21,7 +22,7 @@ namespace Umbraco.Core.PropertyEditors /// The current pre-values stored for the data type /// /// - public override IEnumerable Validate(object value, string config, string preValues, PropertyEditor editor) + public override IEnumerable Validate(object value, string config, PreValueCollection preValues, PropertyEditor editor) { //TODO: localize these! if (value != null) diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyValidator.cs b/src/Umbraco.Core/PropertyEditors/IPropertyValidator.cs index 94e09d9bb1..43b86f485e 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyValidator.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { @@ -18,10 +19,10 @@ namespace Umbraco.Core.PropertyEditors /// /// /// When validating a property editor value (not a pre-value), this is the current pre-values stored for the data type. - /// When validating a pre-value field, this is the name of that field. + /// When validating a pre-value field this will be null. /// /// The property editor instance that we are validating for /// - IEnumerable Validate(object value, string preValues, PropertyEditor editor); + IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor); } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/IntegerValidator.cs b/src/Umbraco.Core/PropertyEditors/IntegerValidator.cs index 9573db27a5..df0ad0e601 100644 --- a/src/Umbraco.Core/PropertyEditors/IntegerValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/IntegerValidator.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { @@ -9,7 +10,7 @@ namespace Umbraco.Core.PropertyEditors [ValueValidator("Integer")] internal sealed class IntegerValidator : ManifestValueValidator, IPropertyValidator { - public override IEnumerable Validate(object value, string config, string preValues, PropertyEditor editor) + public override IEnumerable Validate(object value, string config, PreValueCollection preValues, PropertyEditor editor) { var result = value.TryConvertTo(); if (result.Success == false) @@ -18,7 +19,7 @@ namespace Umbraco.Core.PropertyEditors } } - public IEnumerable Validate(object value, string preValues, PropertyEditor editor) + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) { return Validate(value, "", preValues, editor); } diff --git a/src/Umbraco.Core/PropertyEditors/ManifestPropertyValidator.cs b/src/Umbraco.Core/PropertyEditors/ManifestPropertyValidator.cs index 768492695b..bf2815f502 100644 --- a/src/Umbraco.Core/PropertyEditors/ManifestPropertyValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/ManifestPropertyValidator.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; +using Umbraco.Core.Models; using Umbraco.Core.Serialization; namespace Umbraco.Core.PropertyEditors @@ -56,7 +57,7 @@ namespace Umbraco.Core.PropertyEditors /// The current pre-values stored for the data type /// The property editor instance that we are validating for /// - public IEnumerable Validate(object value, string preValues, PropertyEditor editor) + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) { return ValidatorInstance.Validate(value, Config, preValues, editor); } diff --git a/src/Umbraco.Core/PropertyEditors/ManifestValueValidator.cs b/src/Umbraco.Core/PropertyEditors/ManifestValueValidator.cs index 2b6348d491..2a82f68cbb 100644 --- a/src/Umbraco.Core/PropertyEditors/ManifestValueValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/ManifestValueValidator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { @@ -40,6 +41,6 @@ namespace Umbraco.Core.PropertyEditors /// the validation message applies to the entire property type being validated. If there is a field name applied to a /// validation result we will try to match that field name up with a field name on the item itself. /// - public abstract IEnumerable Validate(object value, string config, string preValues, PropertyEditor editor); + public abstract IEnumerable Validate(object value, string config, PreValueCollection preValues, PropertyEditor editor); } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/RegexValidator.cs b/src/Umbraco.Core/PropertyEditors/RegexValidator.cs index b04b979fdb..b009d59122 100644 --- a/src/Umbraco.Core/PropertyEditors/RegexValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/RegexValidator.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { @@ -30,7 +31,7 @@ namespace Umbraco.Core.PropertyEditors _regex = regex; } - public override IEnumerable Validate(object value, string config, string preValues, PropertyEditor editor) + public override IEnumerable Validate(object value, string config, PreValueCollection preValues, PropertyEditor editor) { //TODO: localize these! if (config.IsNullOrWhiteSpace() == false && value != null) @@ -54,7 +55,7 @@ namespace Umbraco.Core.PropertyEditors /// /// /// - public IEnumerable Validate(object value, string preValues, PropertyEditor editor) + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) { if (_regex == null) { diff --git a/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs b/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs index d226b3ae18..b71cf4236f 100644 --- a/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs +++ b/src/Umbraco.Core/PropertyEditors/RequiredManifestValueValidator.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { @@ -9,7 +10,7 @@ namespace Umbraco.Core.PropertyEditors [ValueValidator("Required")] internal sealed class RequiredManifestValueValidator : ManifestValueValidator { - public override IEnumerable Validate(object value, string config, string preValues, PropertyEditor editor) + public override IEnumerable Validate(object value, string config, PreValueCollection preValues, PropertyEditor editor) { //TODO: localize these! diff --git a/src/Umbraco.Tests/PropertyEditors/ColorListValidatorTest.cs b/src/Umbraco.Tests/PropertyEditors/ColorListValidatorTest.cs index 4e5db9dbf0..d4ee1eb8ef 100644 --- a/src/Umbraco.Tests/PropertyEditors/ColorListValidatorTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ColorListValidatorTest.cs @@ -12,7 +12,7 @@ namespace Umbraco.Tests.PropertyEditors public void Only_Tests_On_JArray() { var validator = new ColorListPreValueEditor.ColorListValidator(); - var result = validator.Validate("hello", "", new ColorPickerPropertyEditor()); + var result = validator.Validate("hello", null, new ColorPickerPropertyEditor()); Assert.AreEqual(0, result.Count()); } @@ -20,7 +20,7 @@ namespace Umbraco.Tests.PropertyEditors public void Only_Tests_On_JArray_Of_Item_JObject() { var validator = new ColorListPreValueEditor.ColorListValidator(); - var result = validator.Validate(new JArray("hello", "world"), "", new ColorPickerPropertyEditor()); + var result = validator.Validate(new JArray("hello", "world"), null, new ColorPickerPropertyEditor()); Assert.AreEqual(0, result.Count()); } @@ -33,7 +33,7 @@ namespace Umbraco.Tests.PropertyEditors JObject.FromObject(new { value = "zxcvzxcvxzcv" }), JObject.FromObject(new { value = "ABC" }), JObject.FromObject(new { value = "1234567" })), - "", new ColorPickerPropertyEditor()); + null, new ColorPickerPropertyEditor()); Assert.AreEqual(2, result.Count()); } } diff --git a/src/Umbraco.Tests/PropertyEditors/EnsureUniqueValuesValidatorTest.cs b/src/Umbraco.Tests/PropertyEditors/EnsureUniqueValuesValidatorTest.cs index 4dfa7f78d5..a910f8e84d 100644 --- a/src/Umbraco.Tests/PropertyEditors/EnsureUniqueValuesValidatorTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/EnsureUniqueValuesValidatorTest.cs @@ -12,7 +12,7 @@ namespace Umbraco.Tests.PropertyEditors public void Only_Tests_On_JArray() { var validator = new ValueListPreValueEditor.EnsureUniqueValuesValidator(); - var result = validator.Validate("hello", "", new ColorPickerPropertyEditor()); + var result = validator.Validate("hello", null, new ColorPickerPropertyEditor()); Assert.AreEqual(0, result.Count()); } @@ -20,7 +20,7 @@ namespace Umbraco.Tests.PropertyEditors public void Only_Tests_On_JArray_Of_Item_JObject() { var validator = new ValueListPreValueEditor.EnsureUniqueValuesValidator(); - var result = validator.Validate(new JArray("hello", "world"), "", new ColorPickerPropertyEditor()); + var result = validator.Validate(new JArray("hello", "world"), null, new ColorPickerPropertyEditor()); Assert.AreEqual(0, result.Count()); } @@ -28,7 +28,7 @@ namespace Umbraco.Tests.PropertyEditors public void Allows_Unique_Values() { var validator = new ValueListPreValueEditor.EnsureUniqueValuesValidator(); - var result = validator.Validate(new JArray(JObject.FromObject(new {value = "hello"}), JObject.FromObject(new {value = "world"})), "", new ColorPickerPropertyEditor()); + var result = validator.Validate(new JArray(JObject.FromObject(new { value = "hello" }), JObject.FromObject(new { value = "world" })), null, new ColorPickerPropertyEditor()); Assert.AreEqual(0, result.Count()); } @@ -36,8 +36,8 @@ namespace Umbraco.Tests.PropertyEditors public void Does_Not_Allow_Multiple_Values() { var validator = new ValueListPreValueEditor.EnsureUniqueValuesValidator(); - var result = validator.Validate(new JArray(JObject.FromObject(new { value = "hello" }), JObject.FromObject(new { value = "hello" })), - "", new ColorPickerPropertyEditor()); + var result = validator.Validate(new JArray(JObject.FromObject(new { value = "hello" }), JObject.FromObject(new { value = "hello" })), + null, new ColorPickerPropertyEditor()); Assert.AreEqual(1, result.Count()); } @@ -50,7 +50,7 @@ namespace Umbraco.Tests.PropertyEditors JObject.FromObject(new { value = "hello" }), JObject.FromObject(new { value = "world" }), JObject.FromObject(new { value = "world" })), - "", new ColorPickerPropertyEditor()); + null, new ColorPickerPropertyEditor()); Assert.AreEqual(2, result.Count()); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js index ddb392c910..b9395d8dc4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js @@ -1,8 +1,7 @@ -angular.module("umbraco").controller("Umbraco.Editors.ColorPickerController", - function($scope) { - - $scope.selectItem = function(color) { - $scope.model.value = color; - }; +function ColorPickerController($scope) { + $scope.selectItem = function (color) { + $scope.model.value = color; + }; +} - }); +angular.module("umbraco").controller("Umbraco.Editors.ColorPickerController", ColorPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.controller.js new file mode 100644 index 0000000000..ff06de42a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.controller.js @@ -0,0 +1,34 @@ +function MultipleTextBoxController($scope) { + + if (!$scope.model.value) { + $scope.model.value = []; + } + + //add any fields that there isn't values for + if ($scope.model.config.min.value > 0) { + for (var i = 0; i < $scope.model.config.min.value; i++) { + if ((i + 1) > $scope.model.value.length) { + $scope.model.value.push({ value: "" }); + } + } + } + + $scope.add = function () { + if ($scope.model.config.max.value <= 0 || $scope.model.value.length < $scope.model.config.max.value) { + $scope.model.value.push({ value: "" }); + } + }; + + $scope.remove = function(index) { + var remainder = []; + for (var x = 0; x < $scope.model.value.length; x++) { + if (x !== index) { + remainder.push($scope.model.value[x]); + } + } + $scope.model.value = remainder; + }; + +} + +angular.module("umbraco").controller("Umbraco.Editors.MultipleTextBoxController", MultipleTextBoxController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.html new file mode 100644 index 0000000000..290e4f06f1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.html @@ -0,0 +1,17 @@ +
+ +
+ + + + +
+ + + + +
diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/PostcodeValidator.cs b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/PostcodeValidator.cs index 0a1a638885..75b8f7649b 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/PostcodeValidator.cs +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/PostcodeValidator.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Linq; using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.UI.App_Plugins.MyPackage.PropertyEditors @@ -12,15 +14,17 @@ namespace Umbraco.Web.UI.App_Plugins.MyPackage.PropertyEditors /// internal class PostcodeValidator : IPropertyValidator { - - public IEnumerable Validate(object value, string preValues, PropertyEditor editor) + + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) { if (value != null) { var stringVal = value.ToString(); - if (preValues.IsNullOrWhiteSpace()) yield break; - var asJson = JObject.Parse(preValues); + if (preValues == null) yield break; + var preValDicionary = preValues.FormatAsDictionary(); + if (preValDicionary.Any() == false) yield break; + var asJson = JObject.Parse(preValDicionary.First().Value.ToString()); if (asJson["country"] == null) yield break; if (asJson["country"].ToString() == "Australia") diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index d40dfeb9fd..3b36c05992 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -94,7 +94,7 @@ namespace Umbraco.Web.Editors { d.Add("files", files); } - var data = new ContentPropertyData(p.Value, d); + var data = new ContentPropertyData(p.Value, p.PreValues, d); //get the deserialized value from the property editor if (p.PropertyEditor == null) diff --git a/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs b/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs index e54865366e..2f1646a5c0 100644 --- a/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs +++ b/src/Umbraco.Web/Editors/ContentPostValidateAttribute.cs @@ -12,72 +12,6 @@ using umbraco.BusinessLogic.Actions; namespace Umbraco.Web.Editors { - /// - /// Checks if the user has access to post a content item based on whether it's being created or saved. - /// - internal sealed class MediaPostValidateAttribute : ActionFilterAttribute - { - private readonly IMediaService _mediaService; - private readonly WebSecurity _security; - - public MediaPostValidateAttribute() - { - } - - public MediaPostValidateAttribute(IMediaService mediaService, WebSecurity security) - { - if (mediaService == null) throw new ArgumentNullException("mediaService"); - if (security == null) throw new ArgumentNullException("security"); - _mediaService = mediaService; - _security = security; - } - - private IMediaService MediaService - { - get { return _mediaService ?? ApplicationContext.Current.Services.MediaService; } - } - - private WebSecurity Security - { - get { return _security ?? UmbracoContext.Current.Security; } - } - - public override void OnActionExecuting(HttpActionContext actionContext) - { - var mediaItem = (ContentItemSave)actionContext.ActionArguments["contentItem"]; - - //We now need to validate that the user is allowed to be doing what they are doing. - //Then if it is new, we need to lookup those permissions on the parent. - IMedia contentToCheck; - switch (mediaItem.Action) - { - case ContentSaveAction.Save: - contentToCheck = mediaItem.PersistedContent; - break; - case ContentSaveAction.SaveNew: - contentToCheck = MediaService.GetById(mediaItem.ParentId); - break; - case ContentSaveAction.Publish: - case ContentSaveAction.PublishNew: - default: - //we don't support this for media - actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); - return; - } - - if (MediaController.CheckPermissions( - actionContext.Request.Properties, - Security.CurrentUser, - MediaService, - contentToCheck.Id, - contentToCheck) == false) - { - actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); - return; - } - } - } - /// /// Checks if the user has access to post a content item based on whether it's being created or saved. /// diff --git a/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs b/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs index 1d6d2f5b12..975eaeec8b 100644 --- a/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs +++ b/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs @@ -88,7 +88,7 @@ namespace Umbraco.Web.Editors foreach (var v in propertyEditor.PreValueEditor.Fields.SelectMany(x => x.Validators)) { - foreach (var result in v.Validate(postedValue, preVal.Key, propertyEditor)) + foreach (var result in v.Validate(postedValue, null, propertyEditor)) { //if there are no member names supplied then we assume that the validation message is for the overall property // not a sub field on the property editor diff --git a/src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs b/src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs new file mode 100644 index 0000000000..7b9c0062f0 --- /dev/null +++ b/src/Umbraco.Web/Editors/MediaPostValidateAttribute.cs @@ -0,0 +1,79 @@ +using System; +using System.Net; +using System.Net.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Filters; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Security; + +namespace Umbraco.Web.Editors +{ + /// + /// Checks if the user has access to post a content item based on whether it's being created or saved. + /// + internal sealed class MediaPostValidateAttribute : ActionFilterAttribute + { + private readonly IMediaService _mediaService; + private readonly WebSecurity _security; + + public MediaPostValidateAttribute() + { + } + + public MediaPostValidateAttribute(IMediaService mediaService, WebSecurity security) + { + if (mediaService == null) throw new ArgumentNullException("mediaService"); + if (security == null) throw new ArgumentNullException("security"); + _mediaService = mediaService; + _security = security; + } + + private IMediaService MediaService + { + get { return _mediaService ?? ApplicationContext.Current.Services.MediaService; } + } + + private WebSecurity Security + { + get { return _security ?? UmbracoContext.Current.Security; } + } + + public override void OnActionExecuting(HttpActionContext actionContext) + { + var mediaItem = (ContentItemSave)actionContext.ActionArguments["contentItem"]; + + //We now need to validate that the user is allowed to be doing what they are doing. + //Then if it is new, we need to lookup those permissions on the parent. + IMedia contentToCheck; + switch (mediaItem.Action) + { + case ContentSaveAction.Save: + contentToCheck = mediaItem.PersistedContent; + break; + case ContentSaveAction.SaveNew: + contentToCheck = MediaService.GetById(mediaItem.ParentId); + break; + case ContentSaveAction.Publish: + case ContentSaveAction.PublishNew: + default: + //we don't support this for media + actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); + return; + } + + if (MediaController.CheckPermissions( + actionContext.Request.Properties, + Security.CurrentUser, + MediaService, + contentToCheck.Id, + contentToCheck) == false) + { + actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); + return; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs index 00fb91da2b..2bd7f60660 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDto.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.Models; +using Newtonsoft.Json; +using Umbraco.Core.Models; namespace Umbraco.Web.Models.ContentEditing { diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs index 17f4f39680..6d2a7e6993 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Newtonsoft.Json; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -14,5 +15,11 @@ namespace Umbraco.Web.Models.ContentEditing public string Description { get; set; } public bool IsRequired { get; set; } public string ValidationRegExp { get; set; } + + /// + /// The current pre-values for this property + /// + [JsonIgnore] + internal PreValueCollection PreValues { 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 7caad86b1f..a73dd5cb49 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -9,13 +9,13 @@ using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Mapping; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using umbraco; using Umbraco.Web.Routing; namespace Umbraco.Web.Models.Mapping { - /// /// Declares how model mappings for content /// @@ -73,7 +73,7 @@ namespace Umbraco.Web.Models.Mapping //FROM IContent TO ContentItemDto config.CreateMap>() - .ForMember( + .ForMember( dto => dto.Owner, expression => expression.ResolveUsing>()); diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs index 7c2310375b..4a0edcf7f8 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs @@ -29,7 +29,10 @@ namespace Umbraco.Web.Models.Mapping propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp; propertyDto.Description = originalProperty.PropertyType.Description; propertyDto.Label = originalProperty.PropertyType.Name; + + //TODO: We should be able to look both of these up at the same time! propertyDto.DataType = dataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId); + propertyDto.PreValues = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProperty.PropertyType.DataTypeDefinitionId); return propertyDto; } diff --git a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs index 6557b00976..7862a2df98 100644 --- a/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs @@ -35,7 +35,7 @@ namespace Umbraco.Web.Models.Mapping //set up the defaults var dataTypeService = _dataTypeService.Value; var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(source.Id); - IDictionary dictionaryVals = PreValueCollection.AsDictionary(preVals).ToDictionary(x => x.Key, x => (object)x.Value); + IDictionary dictionaryVals = preVals.FormatAsDictionary().ToDictionary(x => x.Key, x => (object)x.Value); var result = Enumerable.Empty().ToArray(); //if we have a prop editor, then format the pre-values based on it and create it's fields. diff --git a/src/Umbraco.Web/PropertyEditors/ColorPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ColorPickerPropertyEditor.cs index 566a79ac07..51c9493c32 100644 --- a/src/Umbraco.Web/PropertyEditors/ColorPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ColorPickerPropertyEditor.cs @@ -4,34 +4,11 @@ using System.Linq; using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MultipleTextstring, "Multiple Textbox", "multipletextbox")] - public class MultipleTextStringPropertyEditor : PropertyEditor - { - protected override PreValueEditor CreatePreValueEditor() - { - var prevals = base.CreatePreValueEditor(); - prevals.Fields.Add(new PreValueField(new IntegerValidator()) - { - Description = "Enter the minimum amount of text boxes to be displayed", - Key = "min", - View = "requiredfield", - Name = "Minimum" - }); - prevals.Fields.Add(new PreValueField(new IntegerValidator()) - { - Description = "Enter the maximum amount of text boxes to be displayed, enter -1 for unlimited", - Key = "max", - View = "requiredfield", - Name = "Maximum" - }); - return prevals; - } - } - [PropertyEditor(Constants.PropertyEditors.ColorPicker, "Color Picker", "colorpicker")] public class ColorPickerPropertyEditor : PropertyEditor { @@ -64,7 +41,7 @@ namespace Umbraco.Web.PropertyEditors internal class ColorListValidator : IPropertyValidator { - public IEnumerable Validate(object value, string preValues, PropertyEditor editor) + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) { var json = value as JArray; if (json == null) yield break; diff --git a/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs b/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs index 50fb9057d8..ac1a56a8aa 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs +++ b/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core; @@ -10,8 +11,8 @@ namespace Umbraco.Web.PropertyEditors /// Used to validate if the value is a valid date/time /// internal class DateTimeValidator : IPropertyValidator - { - public IEnumerable Validate(object value, string preValues, PropertyEditor editor) + { + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) { DateTime dt; if (value != null && DateTime.TryParse(value.ToString(), out dt) == false) diff --git a/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs new file mode 100644 index 0000000000..7a5fac1d19 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/MultipleTextStringPropertyEditor.cs @@ -0,0 +1,180 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + [PropertyEditor(Constants.PropertyEditors.MultipleTextstring, "Multiple Textbox", "multipletextbox", ValueType = "TEXT")] + public class MultipleTextStringPropertyEditor : PropertyEditor + { + protected override ValueEditor CreateValueEditor() + { + return new MultipleTextStringValueEditor(base.CreateValueEditor()); + } + + protected override PreValueEditor CreatePreValueEditor() + { + return new MultipleTextStringPreValueEditor(); + } + + /// + /// A custom pre-value editor class to deal with the legacy way that the pre-value data is stored. + /// + internal class MultipleTextStringPreValueEditor : PreValueEditor + { + public MultipleTextStringPreValueEditor() + { + //create the fields + Fields.Add(new PreValueField(new IntegerValidator()) + { + Description = "Enter the minimum amount of text boxes to be displayed", + Key = "min", + View = "requiredfield", + Name = "Minimum" + }); + Fields.Add(new PreValueField(new IntegerValidator()) + { + Description = "Enter the maximum amount of text boxes to be displayed, enter -1 for unlimited", + Key = "max", + View = "requiredfield", + Name = "Maximum" + }); + } + + /// + /// Need to change how we persist the values so they are compatible with the legacy way we store values + /// + /// + /// + /// + public override IDictionary FormatDataForPersistence(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(); + var max = (editorValue.ContainsKey("max") ? editorValue["max"].ToString() : "0").TryConvertTo(); + + 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)}}; + } + + /// + /// Need to deal with the legacy way of storing pre-values and turn them into nice values for the editor + /// + /// + /// + /// + public override IDictionary FormatDataForEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals) + { + var preVals = persistedPreVals.FormatAsDictionary(); + var stringVal = preVals.Any() ? preVals.First().Value.Value : ""; + var returnVal = new Dictionary { { "min", 0 }, { "max", 0 } }; + if (stringVal.IsNullOrWhiteSpace() == false) + { + try + { + var json = JsonConvert.DeserializeObject(stringVal); + if (json["Minimum"] != null) + { + //by default pre-values are sent out with an id/value pair + returnVal["min"] = JObject.FromObject(new { id = 0, value = json["Minimum"].Value() }); + } + if (json["Maximum"] != null) + { + returnVal["max"] = JObject.FromObject(new { id = 0, value = json["Maximum"].Value() }); + } + } + catch (Exception e) + { + //this shouldn't happen unless there's already a bad formatted pre-value + LogHelper.WarnWithException("Could not deserialize value to json " + stringVal, e); + return returnVal; + } + } + + return returnVal; + } + } + + /// + /// Custom value editor so we can format the value for the editor and the database + /// + internal class MultipleTextStringValueEditor : ValueEditorWrapper + { + public MultipleTextStringValueEditor(ValueEditor wrapped) : base(wrapped) + { + } + + /// + /// The value passed in from the editor will be an array of simple objects so we'll need to parse them to get the string + /// + /// + /// + /// + /// + /// We will also check the pre-values here, if there are more items than what is allowed we'll just trim the end + /// + public override object FormatDataForPersistence(ContentPropertyData editorValue, object currentValue) + { + var asArray = editorValue.Value as JArray; + if (asArray == null) + { + return null; + } + + var preVals = editorValue.PreValues.FormatAsDictionary(); + var max = -1; + if (preVals.Any()) + { + try + { + var json = JsonConvert.DeserializeObject(preVals.First().Value.Value); + max = int.Parse(json["Maximum"].ToString()); + } + catch (Exception) + { + //swallow + max = -1; + } + } + + //The legacy property editor saved this data as new line delimited! strange but we have to maintain that. + var array = asArray.OfType() + .Where(x => x["value"] != null) + .Select(x => x["value"].Value()); + + //only allow the max + return string.Join(Environment.NewLine, array.Take(max)); + } + + /// + /// We are actually passing back an array of simple objects instead of an array of strings because in angular a primitive (string) value + /// cannot have 2 way binding, so to get around that each item in the array needs to be an object with a string. + /// + /// + /// + /// + /// The legacy property editor saved this data as new line delimited! strange but we have to maintain that. + /// + public override object FormatDataForEditor(object dbValue) + { + return dbValue == null + ? new JObject[] {} + : dbValue.ToString().Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries) + .Select(x => JObject.FromObject(new {value = x})); + + + } + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs b/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs index 0a9089ce44..387558a24f 100644 --- a/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/PublishValueValueEditor.cs @@ -63,7 +63,7 @@ namespace Umbraco.Web.PropertyEditors var preVals = _dataTypeService.GetPreValuesCollectionByDataTypeId(property.PropertyType.DataTypeDefinitionId); if (preVals != null) { - var dictionary = PreValueCollection.AsDictionary(preVals); + var dictionary = preVals.FormatAsDictionary(); return dictionary; } return null; diff --git a/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs index 3aca2850ba..9caa937cdf 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueListPreValueEditor.cs @@ -60,7 +60,7 @@ namespace Umbraco.Web.PropertyEditors /// public override IDictionary FormatDataForEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals) { - var dictionary = PreValueCollection.AsDictionary(persistedPreVals); + var dictionary = persistedPreVals.FormatAsDictionary(); var arrayOfVals = dictionary.Select(item => item.Value).ToList(); //the items list will be a dictionary of it's id -> value we need to use the id for persistence for backwards compatibility @@ -115,7 +115,7 @@ namespace Umbraco.Web.PropertyEditors /// internal class EnsureUniqueValuesValidator : IPropertyValidator { - public IEnumerable Validate(object value, string preValues, PropertyEditor editor) + public IEnumerable Validate(object value, PreValueCollection preValues, PropertyEditor editor) { var json = value as JArray; if (json == null) yield break; diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index beab9ac756..e0c56a92c3 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -302,6 +302,7 @@ + @@ -324,6 +325,7 @@ + diff --git a/src/Umbraco.Web/WebApi/Filters/ContentItemValidationHelper.cs b/src/Umbraco.Web/WebApi/Filters/ContentItemValidationHelper.cs index f77d9a8910..cb40afd794 100644 --- a/src/Umbraco.Web/WebApi/Filters/ContentItemValidationHelper.cs +++ b/src/Umbraco.Web/WebApi/Filters/ContentItemValidationHelper.cs @@ -127,7 +127,7 @@ namespace Umbraco.Web.WebApi.Filters var postedValue = postedItem.Properties.Single(x => x.Alias == p.Alias).Value; //get the pre-values for this property - var preValues = _applicationContext.Services.DataTypeService.GetPreValueAsString(p.DataType.Id); + var preValues = p.PreValues; //TODO: when we figure out how to 'override' certain pre-value properties we'll either need to: // * Combine the preValues with the overridden values stored with the document type property (but how to combine?)