diff --git a/src/Umbraco.Web.UI.Client/src/common/services/menuactions.service.js b/src/Umbraco.Web.UI.Client/src/common/services/menuactions.service.js index 302834194c..583678b86f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/menuactions.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/menuactions.service.js @@ -14,7 +14,7 @@ function umbracoMenuActions($q, treeService, $location) { /** * @ngdoc method - * @name umbraco.services.umbracoMenuActions#RefreshNodeMenuItem + * @name umbraco.services.umbracoMenuActions#RefreshNode * @methodOf umbraco.services.umbracoMenuActions * @function * @@ -24,7 +24,7 @@ function umbracoMenuActions($q, treeService, $location) { * @param {object} args.treeNode The tree node * @param {object} args.section The current section */ - "RefreshNodeMenuItem": function (args) { + "RefreshNode": function (args) { treeService.loadNodeChildren({ node: args.treeNode, section: args.section }); }, diff --git a/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js index 72e52d835c..a6ac7b319e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatype/datatype.edit.controller.js @@ -108,10 +108,7 @@ function DataTypeEditController($scope, $routeParams, $location, dataTypeResourc contentEditingHelper.handleSaveError({ err: err, allNewProps: $scope.preValues, - allOrigProps: $scope.preValues, - rebindCallback: function () { - createPreValueProps(err.data.preValues); - } + allOrigProps: $scope.preValues }); }); }; diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 91ff59eecd..ba66776276 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Net.Http; using System.Web.Http; using System.Web.Http.ModelBinding; using AutoMapper; @@ -68,79 +67,17 @@ namespace Umbraco.Web.Editors /// /// /// + [DataTypeValidate] public DataTypeDisplay PostSave(DataTypeSave dataType) { - //Validate that the property editor exists - var propertyEditor = PropertyEditorResolver.Current.GetById(dataType.SelectedEditor); - if (propertyEditor == null) - { - var message = string.Format("Property editor with id: {0} was not found", dataType.SelectedEditor); - throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, message)); - } - - //Validate the data type exists or create one if required - IDataTypeDefinition persisted; - switch (dataType.Action) - { - case ContentSaveAction.Save: - persisted = ApplicationContext.Services.DataTypeService.GetDataTypeDefinitionById(dataType.Id); - if (persisted == null) - { - var message = string.Format("Data type with id: {0} was not found", dataType.Id); - throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, message)); - } - //map the model to the persisted instance - Mapper.Map(dataType, persisted); - break; - case ContentSaveAction.SaveNew: - //create the persisted model from mapping the saved model - persisted = Mapper.Map(dataType); - break; - default: - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - //Validate each field - foreach (var preVal in dataType.PreValues) - { - var postedValue = preVal.Value; - - foreach (var v in propertyEditor.PreValueEditor.Fields.SelectMany(x => x.Validators)) - { - foreach (var result in v.Validate(postedValue, preVal.Key, 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 - if (!result.MemberNames.Any()) - { - //add a model state error for the entire property - ModelState.AddModelError(string.Format("{0}.{1}", "Properties", preVal.Key), result.ErrorMessage); - } - else - { - //there's assigned field names so we'll combine the field name with the property name - // so that we can try to match it up to a real sub field of this editor - foreach (var field in result.MemberNames) - { - ModelState.AddModelError(string.Format("{0}.{1}.{2}", "Properties", preVal.Key, field), result.ErrorMessage); - } - } - } - } - } - - if (ModelState.IsValid == false) - { - //if it is not valid, do not continue and return the model state - throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.Forbidden, ModelState)); - } + //If we've made it here, then everything has been wired up and validated by the attribute //finally we need to save the data type and it's pre-vals var dtService = (DataTypeService) ApplicationContext.Services.DataTypeService; var preVals = Mapper.Map(dataType.PreValues); - dtService.SaveDataTypeAndPreValues(persisted, preVals, (int)Security.CurrentUser.Id); + dtService.SaveDataTypeAndPreValues(dataType.PersistedDataType, preVals, (int)Security.CurrentUser.Id); - var display = Mapper.Map(persisted); + var display = Mapper.Map(dataType.PersistedDataType); display.AddSuccessNotification(ui.Text("speechBubbles", "dataTypeSaved"), ""); //now return the updated model diff --git a/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs b/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs new file mode 100644 index 0000000000..07b839eabe --- /dev/null +++ b/src/Umbraco.Web/Editors/DataTypeValidateAttribute.cs @@ -0,0 +1,119 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Filters; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Editors +{ + /// + /// An action filter that wires up the persisted entity of the DataTypeSave model and validates the whole request + /// + internal sealed class DataTypeValidateAttribute : ActionFilterAttribute + { + private readonly IDataTypeService _dataTypeService; + + public DataTypeValidateAttribute() + { + } + + public DataTypeValidateAttribute(IDataTypeService dataTypeService) + { + if (dataTypeService == null) throw new ArgumentNullException("dataTypeService"); + _dataTypeService = dataTypeService; + } + + private IDataTypeService DataTypeService + { + get { return _dataTypeService ?? ApplicationContext.Current.Services.DataTypeService; } + } + + public override void OnActionExecuting(HttpActionContext actionContext) + { + var dataType = (DataTypeSave)actionContext.ActionArguments["dataType"]; + + //Validate that the property editor exists + var propertyEditor = PropertyEditorResolver.Current.GetById(dataType.SelectedEditor); + if (propertyEditor == null) + { + var message = string.Format("Property editor with id: {0} was not found", dataType.SelectedEditor); + actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, message); + return; + } + + //assign the prop editor to the model + dataType.PropertyEditor = propertyEditor; + + //Validate the data type exists or create one if required + IDataTypeDefinition persisted; + switch (dataType.Action) + { + case ContentSaveAction.Save: + persisted = DataTypeService.GetDataTypeDefinitionById(dataType.Id); + if (persisted == null) + { + var message = string.Format("Data type with id: {0} was not found", dataType.Id); + actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, message); + return; + } + //map the model to the persisted instance + Mapper.Map(dataType, persisted); + break; + case ContentSaveAction.SaveNew: + //create the persisted model from mapping the saved model + persisted = Mapper.Map(dataType); + break; + default: + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + //now assign the persisted entity to the model so we can use it in the action + dataType.PersistedDataType = persisted; + + //Validate each field + foreach (var preVal in dataType.PreValues) + { + var postedValue = preVal.Value; + + foreach (var v in propertyEditor.PreValueEditor.Fields.SelectMany(x => x.Validators)) + { + foreach (var result in v.Validate(postedValue, preVal.Key, 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 + if (!result.MemberNames.Any()) + { + //add a model state error for the entire property + actionContext.ModelState.AddModelError(string.Format("{0}.{1}", "Properties", preVal.Key), result.ErrorMessage); + } + else + { + //there's assigned field names so we'll combine the field name with the property name + // so that we can try to match it up to a real sub field of this editor + foreach (var field in result.MemberNames) + { + actionContext.ModelState.AddModelError(string.Format("{0}.{1}.{2}", "Properties", preVal.Key, field), result.ErrorMessage); + } + } + } + } + } + + if (actionContext.ModelState.IsValid == false) + { + //if it is not valid, do not continue and return the model state + actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, actionContext.ModelState); + return; + } + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/DataTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/DataTypeSave.cs index 3652f776b6..4215932716 100644 --- a/src/Umbraco.Web/Models/ContentEditing/DataTypeSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/DataTypeSave.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; +using Newtonsoft.Json; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.Models.ContentEditing { @@ -25,5 +28,17 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "preValues")] public IEnumerable PreValues { get; set; } + /// + /// The real persisted data type + /// + [JsonIgnore] + internal IDataTypeDefinition PersistedDataType { get; set; } + + /// + /// The PropertyEditor assigned + /// + [JsonIgnore] + internal PropertyEditor PropertyEditor { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 27e787575e..b5a450638f 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -111,7 +111,7 @@ namespace Umbraco.Web.Trees // add default actions for *all* users allowedMenu.AddMenuItem(); - allowedMenu.AddMenuItem(true); + allowedMenu.AddMenuItem(true); return allowedMenu; } @@ -155,7 +155,7 @@ namespace Umbraco.Web.Trees menu.AddMenuItem(true); menu.AddMenuItem(); - menu.AddMenuItem(true); + menu.AddMenuItem(true); return menu; } diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 73477e0f71..5907f89fe9 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -45,8 +45,8 @@ namespace Umbraco.Web.Trees if (id == Constants.System.Root.ToInvariantString()) { // root actions - menu.AddMenuItem(); - menu.AddMenuItem(true); + menu.AddMenuItem(); + menu.AddMenuItem(true); return menu; } diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 7078e7d2ac..95a39fd3c6 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -47,7 +47,7 @@ namespace Umbraco.Web.Trees // root actions menu.AddMenuItem(); menu.AddMenuItem(true); - menu.AddMenuItem(true); + menu.AddMenuItem(true); return menu; } diff --git a/src/Umbraco.Web/Trees/Menu/CreateChildEntity.cs b/src/Umbraco.Web/Trees/Menu/CreateChildEntity.cs new file mode 100644 index 0000000000..8a26e12aed --- /dev/null +++ b/src/Umbraco.Web/Trees/Menu/CreateChildEntity.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Web.Trees.Menu +{ + /// + /// Represents the refresh node menu item + /// + [ActionMenuItem("umbracoMenuActions")] + public sealed class CreateChildEntity : ActionMenuItem + { + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/Menu/RefreshNodeMenuItem.cs b/src/Umbraco.Web/Trees/Menu/RefreshNode.cs similarity index 72% rename from src/Umbraco.Web/Trees/Menu/RefreshNodeMenuItem.cs rename to src/Umbraco.Web/Trees/Menu/RefreshNode.cs index 0eb78a0eb5..5f1d06bf4e 100644 --- a/src/Umbraco.Web/Trees/Menu/RefreshNodeMenuItem.cs +++ b/src/Umbraco.Web/Trees/Menu/RefreshNode.cs @@ -1,10 +1,10 @@ -namespace Umbraco.Web.Trees.Menu -{ - /// - /// Represents the refresh node menu item - /// - [ActionMenuItem("umbracoMenuActions")] - public sealed class RefreshNodeMenuItem : ActionMenuItem - { - } +namespace Umbraco.Web.Trees.Menu +{ + /// + /// Represents the refresh node menu item + /// + [ActionMenuItem("umbracoMenuActions")] + public sealed class RefreshNode : ActionMenuItem + { + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e1948163dd..651a651cdc 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -299,6 +299,7 @@ + @@ -320,6 +321,7 @@ + @@ -379,7 +381,7 @@ - + diff --git a/src/umbraco.cms/Actions/ActionRefresh.cs b/src/umbraco.cms/Actions/ActionRefresh.cs index 9aa0a4fe78..e94d4d2fed 100644 --- a/src/umbraco.cms/Actions/ActionRefresh.cs +++ b/src/umbraco.cms/Actions/ActionRefresh.cs @@ -9,7 +9,7 @@ namespace umbraco.BusinessLogic.Actions /// Concerns only the tree itself and thus you should not handle /// this action from without umbraco. /// - [LegacyActionMenuItem("umbracoMenuActions", "RefreshNodeMenuItem")] + [LegacyActionMenuItem("umbracoMenuActions", "RefreshNode")] public class ActionRefresh : IAction { //create singleton