From c819dabfc0983048c14ea824718c1bde955f4662 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 12 Jan 2016 12:20:37 +0100 Subject: [PATCH] U4-7371 New Content type editor - Better validation when trying to create a new document/media/member type with the same name as an existing one & U4-7696 New content type editor - server side validation fails when creating new document type --- .../components/umbGenerateAlias.directive.js | 5 +++-- .../components/umblockedfield.directive.js | 14 ++++++++++++-- .../services/contenteditinghelper.service.js | 4 +++- .../views/components/editor/umb-editor-header.html | 7 ++++++- .../src/views/components/umb-generate-alias.html | 4 +++- .../src/views/components/umb-locked-field.html | 6 +++--- .../src/views/documenttypes/edit.controller.js | 4 ++++ .../src/views/mediatypes/edit.controller.js | 4 ++++ .../src/views/membertypes/edit.controller.js | 4 ++++ src/Umbraco.Web/Editors/ContentTypeController.cs | 9 +++++---- .../Editors/ContentTypeControllerBase.cs | 8 ++++++++ src/Umbraco.Web/Editors/MediaTypeController.cs | 7 ++++--- src/Umbraco.Web/Editors/MemberTypeController.cs | 9 +++++---- 13 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbGenerateAlias.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbGenerateAlias.directive.js index 26164baccb..b9d633b3d8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbGenerateAlias.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbGenerateAlias.directive.js @@ -7,7 +7,8 @@ angular.module("umbraco.directives") scope: { alias: '=', aliasFrom: '=', - enableLock: '=?' + enableLock: '=?', + serverValidationField: '@' }, link: function (scope, element, attrs, ctrl) { @@ -32,7 +33,7 @@ angular.module("umbraco.directives") generateAliasTimeout = $timeout(function () { updateAlias = true; entityResource.getSafeAlias(value, true).then(function (safeAlias) { - if(updateAlias) { + if (updateAlias) { scope.alias = safeAlias.alias; } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblockedfield.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblockedfield.directive.js index 04fde87de5..a23515ab83 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblockedfield.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblockedfield.directive.js @@ -11,7 +11,17 @@ function LockedFieldDirective($timeout, localizationService) { - function link(scope, el, attr, ngModel) { + function link(scope, el, attr, ngModelCtrl) { + + //watch the ngModel so we can manually update the textbox view value when it changes + // this ensures that the normal flow (i.e. a user editing the text box) occurs so that + // the parsers, validators and viewchangelisteners execute + scope.$watch("ngModel", function (newValue, oldValue) { + if (newValue !== oldValue) { + scope.lockedFieldForm.lockedField.$setViewValue(newValue); + scope.lockedFieldForm.lockedField.$render(); + } + }); var input = el.find('.umb-locked-field__input'); @@ -79,7 +89,7 @@ replace: true, templateUrl: 'views/components/umb-locked-field.html', scope: { - model: '=ngModel', + ngModel: "=", locked: "=?", placeholderText: "=?", regexValidation: "=?", diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index d21f297a91..4a690bf0c5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -46,6 +46,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica throw "args.saveMethod is not defined"; } + var redirectOnFailure = args.redirectOnFailure !== undefined ? args.redirectOnFailure : true; + var self = this; //we will use the default one for content if not specified @@ -75,7 +77,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica }, function (err) { self.handleSaveError({ - redirectOnFailure: true, + redirectOnFailure: redirectOnFailure, err: err, rebindCallback: function() { rebindCallback.apply(self, [args.content, err.data]); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index 5955241fb9..a6e2c4fefd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -25,7 +25,12 @@
{{ name }}
- + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html index d68b1cc104..69327474d6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-generate-alias.html @@ -4,7 +4,9 @@ + placeholder-text="placeholderText" + server-validation-field="{{serverValidationField}}"> + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html index dca8a01ef8..36f3d43dce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-locked-field.html @@ -1,4 +1,4 @@ - +
@@ -13,7 +13,7 @@ + title="{{inputModel}}" />
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js index 641ac1c954..abd6b0fdd2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js @@ -155,6 +155,10 @@ saveMethod: contentTypeResource.save, scope: $scope, content: vm.contentType, + //We do not redirect on failure for doc types - this is because it is not possible to actually save the doc + // type when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, // we need to rebind... the IDs that have been created! rebindCallback: function (origContentType, savedContentType) { vm.contentType.id = savedContentType.id; diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js index bcfb826811..49133e8713 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/edit.controller.js @@ -136,6 +136,10 @@ saveMethod: mediaTypeResource.save, scope: $scope, content: vm.contentType, + //We do not redirect on failure for doc types - this is because it is not possible to actually save the doc + // type when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, //no-op for rebind callback... we don't really need to rebind for content types rebindCallback: angular.noop }).then(function (data) { diff --git a/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js index 2999eb6919..3a1d91d2f0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/membertypes/edit.controller.js @@ -90,6 +90,10 @@ saveMethod: memberTypeResource.save, scope: $scope, content: vm.contentType, + //We do not redirect on failure for doc types - this is because it is not possible to actually save the doc + // type when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, //no-op for rebind callback... we don't really need to rebind for content types rebindCallback: angular.noop }).then(function (data) { diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 4622fee1b5..de072331aa 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -147,10 +147,11 @@ namespace Umbraco.Web.Editors public ContentTypeDisplay PostSave(ContentTypeSave contentTypeSave) { var savedCt = PerformPostSave( - contentTypeSave: contentTypeSave, - getContentType: i => Services.ContentTypeService.GetContentType(i), - saveContentType: type => Services.ContentTypeService.Save(type), - beforeCreateNew: ctSave => + contentTypeSave: contentTypeSave, + getContentType: i => Services.ContentTypeService.GetContentType(i), + getContentTypeByAlias: alias => Services.ContentTypeService.GetContentType(alias), + saveContentType: type => Services.ContentTypeService.Save(type), + beforeCreateNew: ctSave => { //create a default template if it doesnt exist -but only if default template is == to the content type //TODO: Is this really what we want? What if we don't want any template assigned at all ? diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index 975154e6e8..f90c2c3e6b 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -155,6 +155,7 @@ namespace Umbraco.Web.Editors protected TContentType PerformPostSave( ContentTypeSave contentTypeSave, Func getContentType, + Func getContentTypeByAlias, Action saveContentType, bool validateComposition = true, Action beforeCreateNew = null) @@ -163,6 +164,13 @@ namespace Umbraco.Web.Editors { var ctId = Convert.ToInt32(contentTypeSave.Id); + //Validate that there's no other ct with the same name + var exists = getContentTypeByAlias(contentTypeSave.Alias); + if (exists != null) + { + ModelState.AddModelError("Alias", "A content type with this alias already exists"); + } + if (ModelState.IsValid == false) { var ct = getContentType(ctId); diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index d54981ff45..98e80aba25 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -134,9 +134,10 @@ namespace Umbraco.Web.Editors public ContentTypeCompositionDisplay PostSave(ContentTypeSave contentTypeSave) { var savedCt = PerformPostSave( - contentTypeSave: contentTypeSave, - getContentType: i => Services.ContentTypeService.GetMediaType(i), - saveContentType: type => Services.ContentTypeService.Save(type)); + contentTypeSave: contentTypeSave, + getContentType: i => Services.ContentTypeService.GetMediaType(i), + getContentTypeByAlias: alias => Services.ContentTypeService.GetMediaType(alias), + saveContentType: type => Services.ContentTypeService.Save(type)); var display = Mapper.Map(savedCt); diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs index 7716f6ea00..44eafe1500 100644 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ b/src/Umbraco.Web/Editors/MemberTypeController.cs @@ -110,10 +110,11 @@ namespace Umbraco.Web.Editors public ContentTypeCompositionDisplay PostSave(ContentTypeSave contentTypeSave) { var savedCt = PerformPostSave( - contentTypeSave: contentTypeSave, - getContentType: i => Services.MemberTypeService.Get(i), - saveContentType: type => Services.MemberTypeService.Save(type), - validateComposition: false); + contentTypeSave: contentTypeSave, + getContentType: i => Services.MemberTypeService.Get(i), + getContentTypeByAlias: alias => Services.MemberTypeService.Get(alias), + saveContentType: type => Services.MemberTypeService.Save(type), + validateComposition: false); var display = Mapper.Map(savedCt);