From 7b33f85a5d8109d89b31abd457f6d2d2521aa67c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 6 Jan 2016 15:15:50 +0100 Subject: [PATCH 1/2] fixes: U4-7593 Removing a composite from an existing doctype - Need better UI/error reporting (your data goes with it!) --- .../overlays/umboverlay.directive.js | 28 +++-- .../components/umbgroupsbuilder.directive.js | 119 +++++++++++------- .../src/less/components/overlays.less | 51 ++++---- .../components/overlays/umb-overlay.html | 52 +++++++- 4 files changed, 165 insertions(+), 85 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js index a76162be24..4741458c90 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/overlays/umboverlay.directive.js @@ -11,6 +11,10 @@ function link(scope, el, attr, ctrl) { + scope.directive = { + enableConfirmButton: false + }; + var overlayNumber = 0; var numberOfOverlays = 0; var isRegistered = false; @@ -222,21 +226,23 @@ } scope.submitForm = function(model) { - if(scope.model.submit) { + if (formHelper.submitForm({scope: scope})) { + formHelper.resetForm({ scope: scope }); - if (formHelper.submitForm({scope: scope})) { + if(scope.model.confirmSubmit && scope.model.confirmSubmit.enable && !scope.directive.enableConfirmButton) { + scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton); + } else { + unregisterOverlay(); + scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton); + } - formHelper.resetForm({ scope: scope }); - - unregisterOverlay(); - - scope.model.submit(model); - - } - - } + } + } + }; + scope.cancelConfirmSubmit = function() { + scope.model.confirmSubmit.show = false; }; scope.closeOverLay = function() { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index d18251da0b..5d3766f517 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -182,64 +182,95 @@ }; scope.openCompositionsDialog = function() { - scope.compositionsDialogModel = {}; - scope.compositionsDialogModel.title = "Compositions"; - scope.compositionsDialogModel.contentType = scope.model; - scope.compositionsDialogModel.availableCompositeContentTypes = scope.model.availableCompositeContentTypes; - scope.compositionsDialogModel.compositeContentTypes = scope.model.compositeContentTypes; - scope.compositionsDialogModel.view = "views/common/overlays/contenttypeeditor/compositions/compositions.html"; - scope.compositionsDialogModel.show = true; - scope.compositionsDialogModel.submit = function(model) { + scope.compositionsDialogModel = { + title: "Compositions", + contentType: scope.model, + availableCompositeContentTypes: scope.model.availableCompositeContentTypes, + compositeContentTypes: scope.model.compositeContentTypes, + view: "views/common/overlays/contenttypeeditor/compositions/compositions.html", + confirmSubmit: { + title: "Warning", + description: "Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?", + checkboxLabel: "I know what I'm doing", + enable: true + }, + show: true, + submit: function(model, oldModel, confirmed) { - // make sure that all tabs has an init property - if (scope.model.groups.length !== 0) { - angular.forEach(scope.model.groups, function(group) { - addInitProperty(group); - }); - } + var compositionRemoved = false; - // remove overlay - scope.compositionsDialogModel.show = false; - scope.compositionsDialogModel = null; - }; + // check if any compositions has been removed + for(var i = 0; oldModel.compositeContentTypes.length > i; i++) { - scope.compositionsDialogModel.close = function(oldModel) { - // reset composition changes - scope.model.groups = oldModel.contentType.groups; - scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes; + var oldComposition = oldModel.compositeContentTypes[i]; - // remove overlay - scope.compositionsDialogModel.show = false; - scope.compositionsDialogModel = null; - }; + if(_.contains(model.compositeContentTypes, oldComposition) === false) { + compositionRemoved = true; + } - scope.compositionsDialogModel.selectCompositeContentType = function(compositeContentType) { + } - if (scope.model.compositeContentTypes.indexOf(compositeContentType.alias) === -1) { - //merge composition with content type + // show overlay confirm box if compositions has been removed. + if(compositionRemoved && confirmed === false) { - if(scope.contentType === "documentType") { + scope.compositionsDialogModel.confirmSubmit.show = true; - contentTypeResource.getById(compositeContentType.id).then(function(composition){ - contentTypeHelper.mergeCompositeContentType(scope.model, composition); - }); + // submit overlay if no compositions has been removed + // or the action has been confirmed + } else { + + // make sure that all tabs has an init property + if (scope.model.groups.length !== 0) { + angular.forEach(scope.model.groups, function(group) { + addInitProperty(group); + }); + } + + // remove overlay + scope.compositionsDialogModel.show = false; + scope.compositionsDialogModel = null; + } + + }, + close: function(oldModel) { + + // reset composition changes + scope.model.groups = oldModel.contentType.groups; + scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes; + + // remove overlay + scope.compositionsDialogModel.show = false; + scope.compositionsDialogModel = null; + + }, + selectCompositeContentType: function(compositeContentType) { + + if (scope.model.compositeContentTypes.indexOf(compositeContentType.alias) === -1) { + //merge composition with content type + + if(scope.contentType === "documentType") { + + contentTypeResource.getById(compositeContentType.id).then(function(composition){ + contentTypeHelper.mergeCompositeContentType(scope.model, composition); + }); - } else if(scope.contentType === "mediaType") { + } else if(scope.contentType === "mediaType") { - mediaTypeResource.getById(compositeContentType.id).then(function(composition){ - contentTypeHelper.mergeCompositeContentType(scope.model, composition); - }); + mediaTypeResource.getById(compositeContentType.id).then(function(composition){ + contentTypeHelper.mergeCompositeContentType(scope.model, composition); + }); + + } + + + } else { + // split composition from content type + contentTypeHelper.splitCompositeContentType(scope.model, compositeContentType); + } } - - - } else { - // split composition from content type - contentTypeHelper.splitCompositeContentType(scope.model, compositeContentType); - } - }; }; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less index eff17fb20a..40b6ddea99 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/overlays.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/overlays.less @@ -7,11 +7,20 @@ box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); } +.umb-overlay__form { + display: flex; + flex-wrap: nowrap; + flex-direction: column; + height: 100%; +} + .umb-overlay .umb-overlay-header { background: @grayLighter; border-bottom: 1px solid @grayLight; padding: 10px; margin-top: 0; + flex-grow: 0; + flex-shrink: 0; } @@ -29,29 +38,31 @@ } .umb-overlay .umb-overlay-container { - top: 50px; - left: 7px; - right: 7px; - bottom: 7px; - position: absolute; + flex-grow: 1; + flex-shrink: 1; + flex-basis: auto; overflow-y: auto; overflow-x: hidden; + position: relative; + height: auto; } .umb-overlay .umb-overlay-drawer { - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 31px; - padding: 10px; + flex-grow: 0; + flex-shrink: 0; + flex-basis: 31px; + padding: 10px 20px; margin: 0; background: @grayLighter; border-top: 1px solid @grayLight; } -.umb-overlay .umb-overlay-drawer .umb-overlay-drawer-content { +.umb-overlay .umb-overlay-drawer.-auto-height { + flex-basis: auto; +} + +.umb-overlay .umb-overlay-drawer .umb-overlay-drawer__align-right { display: flex; justify-content: flex-end; } @@ -76,7 +87,6 @@ } .umb-overlay.umb-overlay-center .umb-overlay-container { - top: 68px; padding: 20px; } @@ -92,8 +102,7 @@ } .umb-overlay.umb-overlay-target .umb-overlay-container { - top: 68px; - bottom: 58px; + padding: 10px; } /* ---------- OVERLAY RIGHT ---------- */ @@ -107,16 +116,12 @@ } .umb-overlay.umb-overlay-right .umb-overlay-header { - height: 100px; + flex-basis: 100px; padding: 20px; box-sizing: border-box; } .umb-overlay.umb-overlay-right .umb-overlay-container { - top: 100px; - left: 0; - right: 0; - bottom: 52px; padding: 20px; } @@ -132,16 +137,12 @@ } .umb-overlay.umb-overlay-left .umb-overlay-header { - height: 100px; + flex-basis: 100px; padding: 20px; box-sizing: border-box; } .umb-overlay.umb-overlay-left .umb-overlay-container { - top: 100px; - left: 0; - right: 0; - bottom: 51px; padding: 20px; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html index ad115f1afd..4a15fc81c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html @@ -1,5 +1,5 @@
-
+

{{model.title}}

@@ -21,11 +21,53 @@
-
-
- - +
+ +
+ +
{{ model.confirmSubmit.title }}
+

{{ model.confirmSubmit.description }}

+ + + +
+ + + + + +
+ +
+ + + + + +
+
From 2f6cfaf99cc2e433e38f9615e3c6598550ab018f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 7 Jan 2016 09:49:13 +0100 Subject: [PATCH 2/2] prevent save when an overlay is open --- .../views/documenttypes/edit.controller.js | 118 +++++++++--------- .../src/views/mediatypes/edit.controller.js | 69 +++++----- .../src/views/membertypes/edit.controller.js | 65 +++++----- 3 files changed, 133 insertions(+), 119 deletions(-) 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 0700e08c2e..641ac1c954 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 @@ -8,8 +8,8 @@ */ (function () { "use strict"; - - function DocumentTypesEditController($scope, $routeParams, modelsResource, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService) { + + function DocumentTypesEditController($scope, $routeParams, modelsResource, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService, overlayHelper) { var vm = this; @@ -139,75 +139,81 @@ /* ---------- SAVE ---------- */ function save() { - var deferred = $q.defer(); - vm.page.saveButtonState = "busy"; + // only save if there is no overlays open + if(overlayHelper.getNumberOfOverlays() === 0) { - // reformat allowed content types to array if id's - vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes); + var deferred = $q.defer(); - contentEditingHelper.contentEditorPerformSave({ - statusMessage: "Saving...", - saveMethod: contentTypeResource.save, - scope: $scope, - content: vm.contentType, - // we need to rebind... the IDs that have been created! - rebindCallback: function (origContentType, savedContentType) { - vm.contentType.id = savedContentType.id; - vm.contentType.groups.forEach(function(group) { - if (!group.name) return; + vm.page.saveButtonState = "busy"; - var k = 0; - while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) - k++; - if (k == savedContentType.groups.length) { - group.id = 0; - return; - } + // reformat allowed content types to array if id's + vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes); - var savedGroup = savedContentType.groups[k]; - if (!group.id) group.id = savedGroup.id; + contentEditingHelper.contentEditorPerformSave({ + statusMessage: "Saving...", + saveMethod: contentTypeResource.save, + scope: $scope, + content: vm.contentType, + // we need to rebind... the IDs that have been created! + rebindCallback: function (origContentType, savedContentType) { + vm.contentType.id = savedContentType.id; + vm.contentType.groups.forEach(function(group) { + if (!group.name) return; - group.properties.forEach(function (property) { - if (property.id || !property.alias) return; - - k = 0; - while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) + var k = 0; + while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) k++; - if (k == savedGroup.properties.length) { - property.id = 0; + if (k == savedContentType.groups.length) { + group.id = 0; return; } - var savedProperty = savedGroup.properties[k]; - property.id = savedProperty.id; + var savedGroup = savedContentType.groups[k]; + if (!group.id) group.id = savedGroup.id; + + group.properties.forEach(function (property) { + if (property.id || !property.alias) return; + + k = 0; + while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) + k++; + if (k == savedGroup.properties.length) { + property.id = 0; + return; + } + + var savedProperty = savedGroup.properties[k]; + property.id = savedProperty.id; + }); }); - }); - } - }).then(function (data) { - //success - syncTreeNode(vm.contentType, data.path); + } + }).then(function (data) { + //success + syncTreeNode(vm.contentType, data.path); - vm.page.saveButtonState = "success"; + vm.page.saveButtonState = "success"; - deferred.resolve(data); - }, function (err) { - //error - if (err) { - editorState.set($scope.content); - } - else { - localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { - notificationsService.error(headerValue, msgValue); + deferred.resolve(data); + }, function (err) { + //error + if (err) { + editorState.set($scope.content); + } + else { + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { + notificationsService.error(headerValue, msgValue); + }); }); - }); - } - vm.page.saveButtonState = "error"; + } + vm.page.saveButtonState = "error"; - deferred.reject(err); - }); - return deferred.promise; + deferred.reject(err); + }); + return deferred.promise; + + } } 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 9bffc10fcb..bcfb826811 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 @@ -9,7 +9,7 @@ (function () { "use strict"; - function MediaTypesEditController($scope, $routeParams, mediaTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService) { + function MediaTypesEditController($scope, $routeParams, mediaTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService, overlayHelper) { var vm = this; vm.save = save; @@ -120,46 +120,51 @@ /* ---------- SAVE ---------- */ function save() { - var deferred = $q.defer(); - vm.page.saveButtonState = "busy"; + // only save if there is no overlays open + if(overlayHelper.getNumberOfOverlays() === 0) { - // reformat allowed content types to array if id's - vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes); + var deferred = $q.defer(); - contentEditingHelper.contentEditorPerformSave({ - statusMessage: "Saving...", - saveMethod: mediaTypeResource.save, - scope: $scope, - content: vm.contentType, - //no-op for rebind callback... we don't really need to rebind for content types - rebindCallback: angular.noop - }).then(function (data) { - //success - syncTreeNode(vm.contentType, data.path); + vm.page.saveButtonState = "busy"; - vm.page.saveButtonState = "success"; + // reformat allowed content types to array if id's + vm.contentType.allowedContentTypes = contentTypeHelper.createIdArray(vm.contentType.allowedContentTypes); - deferred.resolve(data); - }, function (err) { - //error - if (err) { - editorState.set($scope.content); - } - else { - localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { - notificationsService.error(headerValue, msgValue); + contentEditingHelper.contentEditorPerformSave({ + statusMessage: "Saving...", + saveMethod: mediaTypeResource.save, + scope: $scope, + content: vm.contentType, + //no-op for rebind callback... we don't really need to rebind for content types + rebindCallback: angular.noop + }).then(function (data) { + //success + syncTreeNode(vm.contentType, data.path); + + vm.page.saveButtonState = "success"; + + deferred.resolve(data); + }, function (err) { + //error + if (err) { + editorState.set($scope.content); + } + else { + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { + notificationsService.error(headerValue, msgValue); + }); }); - }); - } + } - vm.page.saveButtonState = "error"; + vm.page.saveButtonState = "error"; - deferred.reject(err); - }); + deferred.reject(err); + }); - return deferred.promise; + return deferred.promise; + } } function init(contentType) { 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 728c311e11..2999eb6919 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 @@ -9,7 +9,7 @@ (function () { "use strict"; - function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService, $q, localizationService) { + function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService, $q, localizationService, overlayHelper) { var vm = this; @@ -78,44 +78,47 @@ } function save() { + // only save if there is no overlays open + if(overlayHelper.getNumberOfOverlays() === 0) { - var deferred = $q.defer(); + var deferred = $q.defer(); - vm.page.saveButtonState = "busy"; - - contentEditingHelper.contentEditorPerformSave({ - statusMessage: "Saving...", - saveMethod: memberTypeResource.save, - scope: $scope, - content: vm.contentType, - //no-op for rebind callback... we don't really need to rebind for content types - rebindCallback: angular.noop - }).then(function (data) { - //success - syncTreeNode(vm.contentType, data.path); + vm.page.saveButtonState = "busy"; - vm.page.saveButtonState = "success"; + contentEditingHelper.contentEditorPerformSave({ + statusMessage: "Saving...", + saveMethod: memberTypeResource.save, + scope: $scope, + content: vm.contentType, + //no-op for rebind callback... we don't really need to rebind for content types + rebindCallback: angular.noop + }).then(function (data) { + //success + syncTreeNode(vm.contentType, data.path); - deferred.resolve(data); - }, function (err) { - //error - if (err) { - editorState.set($scope.content); - } - else { - localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { - notificationsService.error(headerValue, msgValue); + vm.page.saveButtonState = "success"; + + deferred.resolve(data); + }, function (err) { + //error + if (err) { + editorState.set($scope.content); + } + else { + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { + notificationsService.error(headerValue, msgValue); + }); }); - }); - } + } - vm.page.saveButtonState = "error"; + vm.page.saveButtonState = "error"; - deferred.reject(err); - }); + deferred.reject(err); + }); - return deferred.promise; + return deferred.promise; + } }