diff --git a/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs index 9b13457b76..df74bf7c87 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Delete/KeysAndIndexes/DeleteKeysAndIndexesBuilder.cs @@ -1,5 +1,7 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using NPoco; +using Umbraco.Core; using Umbraco.Core.Migrations.Expressions.Common; using Umbraco.Core.Persistence.SqlSyntax; @@ -27,31 +29,57 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.KeysAndIndexes { _context.BuildingExpression = false; + //get a list of all constraints - this will include all PK, FK and unique constraints + var tableConstraints = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToList(); + + //get a list of defined indexes - this will include all indexes, unique indexes and unique constraint indexes + var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToList(); + + var uniqueConstraintNames = tableConstraints.Where(x => !x.Item2.InvariantStartsWith("PK_") && !x.Item2.InvariantStartsWith("FK_")).Select(x => x.Item2); + var indexNames = indexes.Select(x => x.IndexName).ToList(); + // drop keys if (DeleteLocal || DeleteForeign) { // table, constraint - var tableKeys = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToList(); + if (DeleteForeign) { - foreach (var key in tableKeys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("FK_"))) + //In some cases not all FK's are prefixed with "FK" :/ mostly with old upgraded databases so we need to check if it's either: + // * starts with FK OR + // * doesn't start with PK_ and doesn't exist in the list of indexes + + foreach (var key in tableConstraints.Where(x => x.Item1 == TableName + && (x.Item2.InvariantStartsWith("FK_") || (!x.Item2.InvariantStartsWith("PK_") && !indexNames.InvariantContains(x.Item2))))) + { Delete.ForeignKey(key.Item2).OnTable(key.Item1).Do(); + } + } if (DeleteLocal) { - foreach (var key in tableKeys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("PK_"))) + foreach (var key in tableConstraints.Where(x => x.Item1 == TableName && x.Item2.InvariantStartsWith("PK_"))) Delete.PrimaryKey(key.Item2).FromTable(key.Item1).Do(); - // note: we do *not* delete the DEFAULT constraints + // note: we do *not* delete the DEFAULT constraints and if we wanted to we'd have to deal with that in interesting ways + // since SQL server has a specific way to handle that, see SqlServerSyntaxProvider.GetDefaultConstraintsPerColumn } } // drop indexes if (DeleteLocal) - { - var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToList(); + { foreach (var index in indexes.Where(x => x.TableName == TableName)) - Delete.Index(index.IndexName).OnTable(index.TableName).Do(); + { + //if this is a unique constraint we need to drop the constraint, else drop the index + //to figure this out, the index must be tagged as unique and it must exist in the tableConstraints + + if (index.IsUnique && uniqueConstraintNames.InvariantContains(index.IndexName)) + Delete.UniqueConstraint(index.IndexName).FromTable(index.TableName).Do(); + else + Delete.Index(index.IndexName).OnTable(index.TableName).Do(); + } + } } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js index f8dbefa5d7..97eb2bf708 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/users/changepassword.directive.js @@ -1,167 +1,172 @@ (function () { - 'use strict'; + 'use strict'; - function ChangePasswordController($scope) { + function ChangePasswordController($scope) { - function resetModel(isNew) { - //the model config will contain an object, if it does not we'll create defaults - //NOTE: We will not support doing the password regex on the client side because the regex on the server side - //based on the membership provider cannot always be ported to js from .net directly. - /* - { - hasPassword: true/false, - requiresQuestionAnswer: true/false, - enableReset: true/false, - enablePasswordRetrieval: true/false, - minPasswordLength: 10 - } - */ + var vm = this; - $scope.showReset = false; + vm.$onInit = onInit; + vm.$onDestroy = onDestroy; + vm.doChange = doChange; + vm.cancelChange = cancelChange; + vm.showOldPass = showOldPass; + vm.showCancelBtn = showCancelBtn; - //set defaults if they are not available - if ($scope.config.disableToggle === undefined) { - $scope.config.disableToggle = false; - } - if ($scope.config.hasPassword === undefined) { - $scope.config.hasPassword = false; - } - if ($scope.config.enablePasswordRetrieval === undefined) { - $scope.config.enablePasswordRetrieval = true; - } - if ($scope.config.requiresQuestionAnswer === undefined) { - $scope.config.requiresQuestionAnswer = false; - } - //don't enable reset if it is new - that doesn't make sense - if (isNew === "true") { - $scope.config.enableReset = false; - } - else if ($scope.config.enableReset === undefined) { - $scope.config.enableReset = true; - } - - if ($scope.config.minPasswordLength === undefined) { - $scope.config.minPasswordLength = 0; - } - - //set the model defaults - if (!angular.isObject($scope.passwordValues)) { - //if it's not an object then just create a new one - $scope.passwordValues = { - newPassword: null, - oldPassword: null, - reset: null, - answer: null - }; - } - else { - //just reset the values + var unsubscribe = []; + + function resetModel(isNew) { + //the model config will contain an object, if it does not we'll create defaults + //NOTE: We will not support doing the password regex on the client side because the regex on the server side + //based on the membership provider cannot always be ported to js from .net directly. + /* + { + hasPassword: true/false, + requiresQuestionAnswer: true/false, + enableReset: true/false, + enablePasswordRetrieval: true/false, + minPasswordLength: 10 + } + */ + + vm.showReset = false; + + //set defaults if they are not available + if (vm.config.disableToggle === undefined) { + vm.config.disableToggle = false; + } + if (vm.config.hasPassword === undefined) { + vm.config.hasPassword = false; + } + if (vm.config.enablePasswordRetrieval === undefined) { + vm.config.enablePasswordRetrieval = true; + } + if (vm.config.requiresQuestionAnswer === undefined) { + vm.config.requiresQuestionAnswer = false; + } + //don't enable reset if it is new - that doesn't make sense + if (isNew === "true") { + vm.config.enableReset = false; + } + else if (vm.config.enableReset === undefined) { + vm.config.enableReset = true; + } + + if (vm.config.minPasswordLength === undefined) { + vm.config.minPasswordLength = 0; + } + + //set the model defaults + if (!angular.isObject(vm.passwordValues)) { + //if it's not an object then just create a new one + vm.passwordValues = { + newPassword: null, + oldPassword: null, + reset: null, + answer: null + }; + } + else { + //just reset the values + + if (!isNew) { + //if it is new, then leave the generated pass displayed + vm.passwordValues.newPassword = null; + vm.passwordValues.oldPassword = null; + } + vm.passwordValues.reset = null; + vm.passwordValues.answer = null; + } + + //the value to compare to match passwords + if (!isNew) { + vm.passwordValues.confirm = ""; + } + else if (vm.passwordValues.newPassword && vm.passwordValues.newPassword.length > 0) { + //if it is new and a new password has been set, then set the confirm password too + vm.passwordValues.confirm = vm.passwordValues.newPassword; + } - if (!isNew) { - //if it is new, then leave the generated pass displayed - $scope.passwordValues.newPassword = null; - $scope.passwordValues.oldPassword = null; } - $scope.passwordValues.reset = null; - $scope.passwordValues.answer = null; - } - //the value to compare to match passwords - if (!isNew) { - $scope.passwordValues.confirm = ""; - } - else if ($scope.passwordValues.newPassword && $scope.passwordValues.newPassword.length > 0) { - //if it is new and a new password has been set, then set the confirm password too - $scope.passwordValues.confirm = $scope.passwordValues.newPassword; - } + //when the scope is destroyed we need to unsubscribe + function onDestroy() { + for (var u in unsubscribe) { + unsubscribe[u](); + } + } + + function onInit() { + //listen for the saved event, when that occurs we'll + //change to changing = false; + unsubscribe.push($scope.$on("formSubmitted", function () { + if (vm.config.disableToggle === false) { + vm.changing = false; + } + })); + + unsubscribe.push($scope.$on("formSubmitting", function () { + //if there was a previously generated password displaying, clear it + if (vm.changing && vm.passwordValues) { + vm.passwordValues.generatedPassword = null; + } + else if (!vm.changing) { + //we are not changing, so the model needs to be null + vm.passwordValues = null; + } + })); + + resetModel(vm.isNew); + + //if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there + //with validators turned on. + vm.changing = vm.config.disableToggle === true || !vm.config.hasPassword; + + //we're not currently changing so set the model to null + if (!vm.changing) { + vm.passwordValues = null; + } + } + + function doChange() { + resetModel(); + vm.changing = true; + //if there was a previously generated password displaying, clear it + vm.passwordValues.generatedPassword = null; + vm.passwordValues.confirm = null; + }; + + function cancelChange() { + vm.changing = false; + //set model to null + vm.passwordValues = null; + }; + + function showOldPass() { + return vm.config.hasPassword && + !vm.config.allowManuallyChangingPassword && + !vm.config.enablePasswordRetrieval && !vm.showReset; + }; + + // TODO: I don't think we need this or the cancel button, this can be up to the editor rendering this component + function showCancelBtn() { + return vm.config.disableToggle !== true && vm.config.hasPassword; + }; } - resetModel($scope.isNew); - - //if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there - //with validators turned on. - $scope.changing = $scope.config.disableToggle === true || !$scope.config.hasPassword; - - //we're not currently changing so set the model to null - if (!$scope.changing) { - $scope.passwordValues = null; - } - - $scope.doChange = function () { - resetModel(); - $scope.changing = true; - //if there was a previously generated password displaying, clear it - $scope.passwordValues.generatedPassword = null; - $scope.passwordValues.confirm = null; + var component = { + templateUrl: 'views/components/users/change-password.html', + controller: ChangePasswordController, + controllerAs: 'vm', + bindings: { + isNew: "<", + passwordValues: "=", //TODO: Do we need bi-directional vals? + config: "=" //TODO: Do we need bi-directional vals? + //TODO: Do we need callbacks? + } }; - $scope.cancelChange = function () { - $scope.changing = false; - //set model to null - $scope.passwordValues = null; - }; - - var unsubscribe = []; - - //listen for the saved event, when that occurs we'll - //change to changing = false; - unsubscribe.push($scope.$on("formSubmitted", function () { - if ($scope.config.disableToggle === false) { - $scope.changing = false; - } - })); - unsubscribe.push($scope.$on("formSubmitting", function () { - //if there was a previously generated password displaying, clear it - if ($scope.changing && $scope.passwordValues) { - $scope.passwordValues.generatedPassword = null; - } - else if (!$scope.changing) { - //we are not changing, so the model needs to be null - $scope.passwordValues = null; - } - })); - - //when the scope is destroyed we need to unsubscribe - $scope.$on('$destroy', function () { - for (var u in unsubscribe) { - unsubscribe[u](); - } - }); - - $scope.showOldPass = function () { - return $scope.config.hasPassword && - !$scope.config.allowManuallyChangingPassword && - !$scope.config.enablePasswordRetrieval && !$scope.showReset; - }; - - // TODO: I don't think we need this or the cancel button, this can be up to the editor rendering this directive - $scope.showCancelBtn = function () { - return $scope.config.disableToggle !== true && $scope.config.hasPassword; - }; - - } - - function ChangePasswordDirective() { - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/users/change-password.html', - controller: 'Umbraco.Editors.Users.ChangePasswordDirectiveController', - scope: { - isNew: "=?", - passwordValues: "=", - config: "=" - } - }; - - return directive; - - } - - angular.module('umbraco.directives').controller('Umbraco.Editors.Users.ChangePasswordDirectiveController', ChangePasswordController); - angular.module('umbraco.directives').directive('changePassword', ChangePasswordDirective); + angular.module('umbraco.directives').component('changePassword', component); })(); 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 3525310e48..200e61ee96 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 @@ -491,7 +491,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt var savedVariants = []; if (origContent.variants) { isContent = true; - //it's contnet so assign the variants as they exist + //it's content so assign the variants as they exist origVariants = origContent.variants; savedVariants = savedContent.variants; } @@ -517,7 +517,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt //special case for content, don't sync this variant if it wasn't tagged //for saving in the first place - if (!origVariant.save) { + if (isContent && !origVariant.save) { continue; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html b/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html index 7b6c7ca0e1..ef5f7a4159 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/users/change-password.html @@ -1,68 +1,69 @@
-
+
Password has been reset to:
- {{passwordValues.generatedPassword}} + {{vm.passwordValues.generatedPassword}}
-
+
- + Change password
- - - + + - - {{passwordForm.resetPassword.errorMsg}} + ng-change="vm.showReset = !vm.showReset" /> + + {{vm.passwordForm.resetPassword.errorMsg}} - - + + - - + - + Required - {{passwordForm.oldPassword.errorMsg}} + {{vm.passwordForm.oldPassword.errorMsg}} - - + - + Required - Minimum {{config.minPasswordLength}} characters - {{passwordForm.password.errorMsg}} + Minimum {{vm.config.minPasswordLength}} characters + {{vm.passwordForm.password.errorMsg}} - - + - + The confirmed password doesn't match the new password! - + Cancel diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 4141b60a3d..3517024d0c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -348,6 +348,9 @@ 8200 / http://localhost:8200/ + 8130 + / + http://localhost:8130 False False