From 10cf10b33a4333a18e816ed5f11be2b5d7e91a6d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Wed, 9 Jan 2019 12:22:55 +0100 Subject: [PATCH 01/25] Set form as dirty when removing a tag --- .../src/views/propertyeditors/tags/tags.controller.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js index 688ac7693f..a61930f877 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js @@ -1,9 +1,12 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.TagsController", - function ($scope) { + function ($scope, angularHelper) { $scope.valueChanged = function(value) { $scope.model.value = value; + // the model value seems to be a reference to the same array, so we need + // to set the form as dirty explicitly when the content of the array changes + angularHelper.getCurrentForm($scope).$setDirty(); } } From e88c789b88f85e36e672ce7881fec6d0995b7145 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 11 Jan 2019 15:44:54 +0100 Subject: [PATCH 02/25] Add some defensive coding to avoid JS errors --- .../components/content/umbcontentnodeinfo.directive.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index 71fbabe943..b65bf447e7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -97,9 +97,9 @@ } //load in the audit trail if we are currently looking at the INFO tab - if (umbVariantContentCtrl) { + if (umbVariantContentCtrl && umbVariantContentCtrl.editor) { var activeApp = _.find(umbVariantContentCtrl.editor.content.apps, a => a.active); - if (activeApp.alias === "umbInfo") { + if (activeApp && activeApp.alias === "umbInfo") { isInfoTab = true; loadAuditTrail(); loadRedirectUrls(); From 291d58900ff2ad3f4e3772857748301aa70a1f3b Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 13 Jan 2019 12:42:42 +0100 Subject: [PATCH 03/25] V8: Remember editor language between sessions (#4035) --- .../src/controllers/navigation.controller.js | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js index 06b82d6eab..335fe99e7c 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js @@ -9,7 +9,7 @@ * * @param {navigationService} navigationService A reference to the navigationService */ -function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource) { +function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource) { //this is used to trigger the tree to start loading once everything is ready var treeInitPromise = $q.defer(); @@ -344,9 +344,6 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar $scope.languages = languages; if ($scope.languages.length > 1) { - var defaultLang = _.find($scope.languages, function (l) { - return l.isDefault; - }); //if there's already one set, check if it exists var currCulture = null; var mainCulture = $location.search().mculture; @@ -356,7 +353,18 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar }); } if (!currCulture) { - $location.search("mculture", defaultLang ? defaultLang.culture : null); + // no culture in the request, let's look for one in the cookie that's set when changing language + var defaultCulture = $cookies.get("UMB_MCULTURE"); + if (!defaultCulture) { + // no luck either, look for the default language + var defaultLang = _.find($scope.languages, function (l) { + return l.isDefault; + }); + if (defaultLang) { + defaultCulture = defaultLang.culture; + } + } + $location.search("mculture", defaultCulture ? defaultCulture : null); } } @@ -391,6 +399,9 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar $scope.selectLanguage = function (language) { $location.search("mculture", language.culture); + // add the selected culture to a cookie so the user will log back into the same culture later on (cookie max age is one year = 31536000 seconds) + // NOTE: $cookies doesn't support max-age, so we need to go the good ol' JS way about setting the cookie + document.cookie = "UMB_MCULTURE=" +language.culture + ";path=/;max-age=31536000;"; // close the language selector $scope.page.languageSelectorIsOpen = false; From c619b79276be80db73442cee0b4dfd4b975bfed9 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sun, 13 Jan 2019 13:06:16 +0100 Subject: [PATCH 04/25] Fix placeholder text when empty alias input after generating alias (#4032) --- .../components/umbGenerateAlias.directive.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 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 47d1431e13..56a18a217e 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 @@ -86,20 +86,21 @@ angular.module("umbraco.directives") function generateAlias(value) { if (generateAliasTimeout) { - $timeout.cancel(generateAliasTimeout); + $timeout.cancel(generateAliasTimeout); } - if( value !== undefined && value !== "" && value !== null) { + if (value !== undefined && value !== "" && value !== null) { - scope.alias = ""; + scope.alias = ""; scope.placeholderText = scope.labels.busy; generateAliasTimeout = $timeout(function () { updateAlias = true; entityResource.getSafeAlias(value, true).then(function (safeAlias) { if (updateAlias) { - scope.alias = safeAlias.alias; - } + scope.alias = safeAlias.alias; + } + scope.placeholderText = scope.labels.idle; }); }, 500); @@ -108,7 +109,6 @@ angular.module("umbraco.directives") scope.alias = ""; scope.placeholderText = scope.labels.idle; } - } // if alias gets unlocked - stop watching alias @@ -119,17 +119,17 @@ angular.module("umbraco.directives") })); // validate custom entered alias - eventBindings.push(scope.$watch('alias', function(newValue, oldValue){ - - if(scope.alias === "" && bindWatcher === true || scope.alias === null && bindWatcher === true) { - // add watcher - eventBindings.push(scope.$watch('aliasFrom', function(newValue, oldValue) { - if(bindWatcher) { - generateAlias(newValue); - } - })); - } - + eventBindings.push(scope.$watch('alias', function (newValue, oldValue) { + if (scope.alias === "" || scope.alias === null || scope.alias === undefined) { + if (bindWatcher === true) { + // add watcher + eventBindings.push(scope.$watch('aliasFrom', function (newValue, oldValue) { + if (bindWatcher) { + generateAlias(newValue); + } + })); + } + } })); // clean up From 2ebd7a077cc5e71bd476f3f71d18d6ab16723e0c Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Sun, 13 Jan 2019 13:06:16 +0100 Subject: [PATCH 05/25] Fix placeholder text when empty alias input after generating alias (#4032) (cherry picked from commit c619b79276be80db73442cee0b4dfd4b975bfed9) --- .../components/umbGenerateAlias.directive.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 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 9b4bd17a12..b6c53d3876 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 @@ -86,20 +86,21 @@ angular.module("umbraco.directives") function generateAlias(value) { if (generateAliasTimeout) { - $timeout.cancel(generateAliasTimeout); + $timeout.cancel(generateAliasTimeout); } - if( value !== undefined && value !== "" && value !== null) { + if (value !== undefined && value !== "" && value !== null) { - scope.alias = ""; + scope.alias = ""; scope.placeholderText = scope.labels.busy; generateAliasTimeout = $timeout(function () { updateAlias = true; entityResource.getSafeAlias(encodeURIComponent(value), true).then(function (safeAlias) { if (updateAlias) { - scope.alias = safeAlias.alias; - } + scope.alias = safeAlias.alias; + } + scope.placeholderText = scope.labels.idle; }); }, 500); @@ -108,7 +109,6 @@ angular.module("umbraco.directives") scope.alias = ""; scope.placeholderText = scope.labels.idle; } - } // if alias gets unlocked - stop watching alias @@ -119,17 +119,17 @@ angular.module("umbraco.directives") })); // validate custom entered alias - eventBindings.push(scope.$watch('alias', function(newValue, oldValue){ - - if(scope.alias === "" && bindWatcher === true || scope.alias === null && bindWatcher === true) { - // add watcher - eventBindings.push(scope.$watch('aliasFrom', function(newValue, oldValue) { - if(bindWatcher) { - generateAlias(newValue); - } - })); - } - + eventBindings.push(scope.$watch('alias', function (newValue, oldValue) { + if (scope.alias === "" || scope.alias === null || scope.alias === undefined) { + if (bindWatcher === true) { + // add watcher + eventBindings.push(scope.$watch('aliasFrom', function (newValue, oldValue) { + if (bindWatcher) { + generateAlias(newValue); + } + })); + } + } })); // clean up From cd6cdcfda468bba4c6d07bf9d9b1886ec072023b Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 13 Jan 2019 13:54:30 +0100 Subject: [PATCH 06/25] Visualize image transparency in upload and image cropper thumbnails (#4050) --- .../src/less/property-editors.less | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index e9d598a8cf..73de7aeee7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -1,3 +1,5 @@ +@checkered-background: url(../img/checkered-background.png); + // // Container styles // -------------------------------------------------- @@ -386,7 +388,7 @@ div.umb-codeeditor .umb-btn-toolbar { max-height:100%; margin:auto; display:block; - background-image: url(../img/checkered-background.png); + background-image: @checkered-background; } .umb-sortable-thumbnails li .trashed { @@ -599,12 +601,18 @@ div.umb-codeeditor .umb-btn-toolbar { vertical-align: top; } - .gravity-container .viewport { - max-width: 600px; - } + .gravity-container { + border: 1px solid @gray-8; + line-height: 0; - .gravity-container .viewport:hover { - cursor: pointer; + .viewport { + max-width: 600px; + background: @checkered-background; + + &:hover { + cursor: pointer; + } + } } .imagecropper { @@ -617,6 +625,10 @@ div.umb-codeeditor .umb-btn-toolbar { float: left; max-width: 100%; } + + .viewport img { + background: @checkered-background; + } } .imagecropper .umb-cropper__container { @@ -884,6 +896,10 @@ div.umb-codeeditor .umb-btn-toolbar { list-style: none; vertical-align: middle; margin-bottom: 0; + + img { + background: @checkered-background; + } } .umb-fileupload label { From d1cfe847235198c49ae6640e56a0dc3d50fe35fd Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sat, 12 Jan 2019 17:05:52 +0100 Subject: [PATCH 07/25] Set the form dirty when changing focal point and editing crops --- .../imaging/umbimagegravity.directive.js | 18 +++++++++--------- .../imagecropper/imagecropper.controller.js | 5 +++++ .../imagecropper/imagecropper.html | 3 ++- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js index bb364b79ee..7d1b1c5a18 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js @@ -14,7 +14,8 @@ angular.module("umbraco.directives") scope: { src: '=', center: "=", - onImageLoaded: "&" + onImageLoaded: "&", + onGravityChanged: "&" }, link: function(scope, element, attrs) { @@ -52,7 +53,7 @@ angular.module("umbraco.directives") calculateGravity(offsetX, offsetY); - lazyEndEvent(); + gravityChanged(); }; var setDimensions = function () { @@ -77,12 +78,11 @@ angular.module("umbraco.directives") scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height; }; - var lazyEndEvent = _.debounce(function(){ - scope.$apply(function(){ - scope.$emit("imageFocalPointStop"); - }); - }, 2000); - + var gravityChanged = function () { + if (angular.isFunction(scope.onGravityChanged)) { + scope.onGravityChanged(); + } + }; //Drag and drop positioning, using jquery ui draggable //TODO ensure that the point doesnt go outside the box @@ -100,7 +100,7 @@ angular.module("umbraco.directives") calculateGravity(offsetX, offsetY); }); - lazyEndEvent(); + gravityChanged(); } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js index 47442495f7..d9131e88a4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js @@ -52,6 +52,7 @@ angular.module('umbraco') }); editedCrop.coordinates = $scope.currentCrop.coordinates; $scope.close(); + angularHelper.getCurrentForm($scope).$setDirty(); }; //reset the current crop @@ -98,6 +99,10 @@ angular.module('umbraco') $scope.hasDimensions = hasDimensions; }; + $scope.focalPointChanged = function () { + angularHelper.getCurrentForm($scope).$setDirty(); + } + //on image selected, update the cropper $scope.$on("filesSelected", function (ev, args) { $scope.model.value = config; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html index a8f513f006..6e1cb52822 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html @@ -40,7 +40,8 @@ + on-image-loaded="imageLoaded(isCroppable, hasDimensions)" + on-gravity-changed="focalPointChanged()"> Remove file From 0f7cb7c17737632bdd759300de003eb1f3095afe Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 11 Jan 2019 14:36:45 +0100 Subject: [PATCH 08/25] Use built-in localization for Nested Content + fix element picker title --- .../nestedcontent/nestedcontent.controller.js | 26 +++++-------------- .../nestedcontent/nestedcontent.html | 6 ++--- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 6e67d2d251..79e00fa453 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -81,25 +81,6 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop : undefined; }); - $scope.editIconTitle = ''; - $scope.moveIconTitle = ''; - $scope.deleteIconTitle = ''; - - // localize the edit icon title - localizationService.localize('general_edit').then(function (value) { - $scope.editIconTitle = value; - }); - - // localize the delete icon title - localizationService.localize('general_delete').then(function (value) { - $scope.deleteIconTitle = value; - }); - - // localize the move icon title - localizationService.localize('actions_move').then(function (value) { - $scope.moveIconTitle = value; - }); - $scope.nodes = []; $scope.currentNode = undefined; $scope.realCurrentNode = undefined; @@ -116,6 +97,11 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop $scope.showIcons = $scope.model.config.showIcons || true; $scope.wideMode = $scope.model.config.hideLabel == "1"; + $scope.labels = {}; + localizationService.localizeMany(["grid_insertControl"]).then(function(data) { + $scope.labels.docTypePickerTitle = data[0]; + }); + // helper to force the current form into the dirty state $scope.setDirty = function () { if ($scope.propertyForm) { @@ -138,7 +124,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop } $scope.overlayMenu = { - title: localizationService.localize('grid_insertControl'), + title: $scope.labels.docTypePickerTitle, show: false, style: {}, filter: $scope.scaffolds.length > 15 ? true : false, diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html index 1ff6666907..572021aebd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html @@ -12,13 +12,13 @@
From d350ab0789f7d97202bdff299ef32d9acce4b30e Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Fri, 11 Jan 2019 15:07:04 +0100 Subject: [PATCH 09/25] Ensure some spacing between properties within Nested Content items --- .../src/less/components/umb-nested-content.less | 7 +++++++ .../nestedcontent/nestedcontent.editor.html | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index df8977a2bf..6ddd9d8d50 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -3,6 +3,13 @@ position: relative; } +.umb-nested-content-property-container { + position: relative; + &:not(:last-child){ + margin-bottom: 12px; + } +} + .umb-nested-content--not-supported { opacity: 0.3; pointer-events: none; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html index 83076b54a0..0cf67022c6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html @@ -1,5 +1,5 @@ 
-
+
From f620d0deca457d3733755a8e6bb71082511fd1f2 Mon Sep 17 00:00:00 2001 From: Steve Date: Fri, 11 Jan 2019 11:16:46 +0000 Subject: [PATCH 10/25] Fixes #4016 unpublished content styling in list view --- src/Umbraco.Web.UI.Client/src/less/components/umb-table.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less index 2c1fc4a701..217d94bc00 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-table.less @@ -165,7 +165,7 @@ input.umb-table__input { } -.-content :not(.with-unpublished-version).-unpublished { +.-content .-unpublished:not(.with-unpublished-version) { .umb-table__name > * { opacity: .4; } From b354eda7ac9ed2f3f650ef4c169425f2d39dc1a7 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 8 Jan 2019 00:39:45 +0100 Subject: [PATCH 11/25] Add styles for umb-property-file-upload and move styles for umb-upload-big-button --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../components/umb-property-file-upload.less | 28 +++++++++++++++++++ .../src/less/property-editors.less | 13 +-------- .../upload/umb-property-file-upload.html | 3 +- 4 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index a28c128706..a65b94444e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -161,6 +161,7 @@ @import "components/umb-file-dropzone.less"; @import "components/umb-node-preview.less"; @import "components/umb-mini-editor.less"; +@import "components/umb-property-file-upload.less"; @import "components/users/umb-user-cards.less"; @import "components/users/umb-user-details.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less new file mode 100644 index 0000000000..08b1a1b5e1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less @@ -0,0 +1,28 @@ +.umb-property-file-upload { + + .umb-upload-button-big { + display: block; + padding: 20px; + opacity: 1; + border: 1px dashed @gray-8; + background: none; + text-align: center; + font-size: 14px; + + &, &:hover { + color: @gray-8; + } + + i.icon { + font-size: 55px; + line-height: 70px + } + + input { + left: 0; + bottom: 0; + height: 100%; + width: 100%; + } + } +} diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 7e3d3f1491..3ec8c383da 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -699,7 +699,7 @@ // // folder-browser // -------------------------------------------------- -.umb-folderbrowser .add-link{ +.umb-folderbrowser .add-link { display: inline-block; height: 120px; width: 120px; @@ -708,17 +708,6 @@ line-height: 120px } -.umb-upload-button-big:hover{color: @gray-8;} - -.umb-upload-button-big {display: block} -.umb-upload-button-big input { - left: 0; - bottom: 0; - height: 100%; - width: 100%; -} - - // // File upload // -------------------------------------------------- diff --git a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html index f205dffa57..dea532b7ea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-property-file-upload.html @@ -1,4 +1,5 @@ -
+
+
Date: Tue, 8 Jan 2019 00:40:23 +0100 Subject: [PATCH 12/25] Only show extension if media item has a file --- .../src/views/components/umb-media-grid.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html index 2591789a36..360c6b2bb7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html @@ -24,7 +24,7 @@ - .{{item.extension}} + .{{item.extension}}
From 9968cd257d072e049fdc163e6862cbe6da9c7487 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 8 Jan 2019 00:41:26 +0100 Subject: [PATCH 13/25] Rename wrapper class on umb-file-dropzone --- .../src/less/components/umb-file-dropzone.less | 2 +- .../src/views/components/upload/umb-file-dropzone.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less index 4803c05f6e..b7c58ad3cf 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-file-dropzone.less @@ -1,5 +1,5 @@ -.umb-file-dropzone-directive{ +.umb-file-dropzone { // drop zone // tall and small version - animate height diff --git a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html index 9d2997625e..c99655b7fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/upload/umb-file-dropzone.html @@ -1,4 +1,4 @@ -
+
From 1213551ca951db86acc0fda3f40b0a1d4efad4a5 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Tue, 8 Jan 2019 00:40:23 +0100 Subject: [PATCH 14/25] Only show extension if media item has a file (cherry picked from commit 168febae248cb80a6c87ca9d0c840b70f97121fc) --- .../src/views/components/umb-media-grid.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html index bad1ccc1ce..e97c91b414 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-media-grid.html @@ -23,7 +23,7 @@ - .{{item.extension}} + .{{item.extension}}
From 8d1733fd1275ba18f3420e995613bc52e0b51344 Mon Sep 17 00:00:00 2001 From: Jannik Anker Date: Tue, 2 Oct 2018 18:43:12 +0200 Subject: [PATCH 15/25] Issue 3018 - non-admin users cannot create groups --- .../Editors/UserGroupEditorAuthorizationHelper.cs | 14 +++++++++++++- src/Umbraco.Web/Editors/UserGroupsController.cs | 11 ++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs b/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs index 6bc39b376d..5e6458f93a 100644 --- a/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs +++ b/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs @@ -53,6 +53,18 @@ namespace Umbraco.Web.Editors if (currentUser.IsAdmin()) return Attempt.Succeed(); + // var derp = ((userGroupSave.Id == null || (int)userGroupSave.Id <= 0) && Services.SectionService.GetAllowedSections(Security.CurrentUser.Id).Select(x => x.Alias).Contains(Constants.Applications.Users)) ? Attempt.Succeed() : Attempt.Failed(); + + var existingGroups = _userService.GetUserGroupsByAlias(groupAliases); + + if(!existingGroups.Any()) + { + // We're dealing with new groups, + // so authorization should be given to any user with access to Users section + if (currentUser.AllowedSections.Contains(Constants.Applications.Users)) + return Attempt.Succeed(); + } + var userGroups = currentUser.Groups.Select(x => x.Alias).ToArray(); var missingAccess = groupAliases.Except(userGroups).ToArray(); return missingAccess.Length == 0 @@ -119,4 +131,4 @@ namespace Umbraco.Web.Editors return Attempt.Succeed(); } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Editors/UserGroupsController.cs b/src/Umbraco.Web/Editors/UserGroupsController.cs index 66011e01b2..7d08560b6f 100644 --- a/src/Umbraco.Web/Editors/UserGroupsController.cs +++ b/src/Umbraco.Web/Editors/UserGroupsController.cs @@ -30,6 +30,7 @@ namespace Umbraco.Web.Editors //authorize that the user has access to save this user group var authHelper = new UserGroupEditorAuthorizationHelper( Services.UserService, Services.ContentService, Services.MediaService, Services.EntityService); + var isAuthorized = authHelper.AuthorizeGroupAccess(Security.CurrentUser, userGroupSave.Alias); if (isAuthorized == false) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, isAuthorized.Result)); @@ -50,6 +51,14 @@ namespace Umbraco.Web.Editors if (isAuthorized == false) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, isAuthorized.Result)); + //current user needs to be added to a new group if not an admin (possibly only if no other users are added?) to avoid a 401 + if(!Security.CurrentUser.IsAdmin() && (userGroupSave.Id == null || Convert.ToInt32(userGroupSave.Id) >= 0)/* && !userGroupSave.Users.Any() */) + { + var userIds = userGroupSave.Users.ToList(); + userIds.Add(Security.CurrentUser.Id); + userGroupSave.Users = userIds; + } + //save the group Services.UserService.Save(userGroupSave.PersistedUserGroup, userGroupSave.Users.ToArray()); @@ -148,4 +157,4 @@ namespace Umbraco.Web.Editors Services.TextService.Localize("speechBubbles/deleteUserGroupSuccess", new[] {userGroups[0].Name})); } } -} \ No newline at end of file +} From 1224f682977d32b0526fa36d0605618b31fdca1f Mon Sep 17 00:00:00 2001 From: Jannik Anker Date: Tue, 2 Oct 2018 18:45:44 +0200 Subject: [PATCH 16/25] Issue 3018 - non-admin users cannot create groups (derpness cleanup) --- src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs b/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs index 5e6458f93a..1f7601379e 100644 --- a/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs +++ b/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs @@ -53,8 +53,6 @@ namespace Umbraco.Web.Editors if (currentUser.IsAdmin()) return Attempt.Succeed(); - // var derp = ((userGroupSave.Id == null || (int)userGroupSave.Id <= 0) && Services.SectionService.GetAllowedSections(Security.CurrentUser.Id).Select(x => x.Alias).Contains(Constants.Applications.Users)) ? Attempt.Succeed() : Attempt.Failed(); - var existingGroups = _userService.GetUserGroupsByAlias(groupAliases); if(!existingGroups.Any()) From 282ac2b9c5da047cb6b725a74500a18e21890c73 Mon Sep 17 00:00:00 2001 From: christophnz Date: Fri, 14 Dec 2018 22:46:35 +1300 Subject: [PATCH 17/25] Added 2 HtmlHelper extension methods for rendering RelatedLinks, including Unit tests --- .../Mvc/HtmlHelperExtensionMethodsTests.cs | 29 +++++++++++++++-- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 32 +++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs b/src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs index adf5772f76..e7e4f363c1 100644 --- a/src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/HtmlHelperExtensionMethodsTests.cs @@ -29,5 +29,30 @@ namespace Umbraco.Tests.Web.Mvc var output = _htmlHelper.Wrap("div", "hello world", new {style = "color:red;", onclick = "void();"}); Assert.AreEqual("
hello world
", output.ToHtmlString()); } - } -} \ No newline at end of file + + [Test] + public void GetRelatedLinkHtml_Simple() + { + var relatedLink = new Umbraco.Web.Models.RelatedLink { + Caption = "Link Caption", + NewWindow = true, + Link = "https://www.google.com/" + }; + var output = _htmlHelper.GetRelatedLinkHtml(relatedLink); + Assert.AreEqual("Link Caption", output.ToHtmlString()); + } + + [Test] + public void GetRelatedLinkHtml_HtmlAttributes() + { + var relatedLink = new Umbraco.Web.Models.RelatedLink + { + Caption = "Link Caption", + NewWindow = true, + Link = "https://www.google.com/" + }; + var output = _htmlHelper.GetRelatedLinkHtml(relatedLink, new { @class = "test-class"}); + Assert.AreEqual("Link Caption", output.ToHtmlString()); + } + } +} diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 30b4e64e33..90bc97ca19 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -965,5 +965,37 @@ namespace Umbraco.Web #endregion + #region RelatedLink + + /// + /// Renders an anchor element for a RelatedLink instance. + /// Format: <a href="relatedLink.Link" target="_blank/_self">relatedLink.Caption</a> + /// + /// The HTML helper instance that this method extends. + /// The RelatedLink instance + /// An anchor element + public static MvcHtmlString GetRelatedLinkHtml(this HtmlHelper htmlHelper, RelatedLink relatedLink) + { + return htmlHelper.GetRelatedLinkHtml(relatedLink, null); + } + + /// + /// Renders an anchor element for a RelatedLink instance, accepting htmlAttributes. + /// Format: <a href="relatedLink.Link" target="_blank/_self" htmlAttributes>relatedLink.Caption</a> + /// + /// The HTML helper instance that this method extends. + /// The RelatedLink instance + /// An object that contains the HTML attributes to set for the element. + /// + public static MvcHtmlString GetRelatedLinkHtml(this HtmlHelper htmlHelper, RelatedLink relatedLink, object htmlAttributes) + { + var tagBuilder = new TagBuilder("a"); + tagBuilder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); + tagBuilder.MergeAttribute("href", relatedLink.Link); + tagBuilder.MergeAttribute("target", relatedLink.NewWindow ? "_blank" : "_self"); + tagBuilder.InnerHtml = HttpUtility.HtmlEncode(relatedLink.Caption); + return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal)); + } + #endregion } } From 74986d24f15637093bb1b6662ea4351e40e417fc Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 13 Jan 2019 15:49:37 +0100 Subject: [PATCH 18/25] V8: Add "Move" option for deleted items in the content tree (#3931) --- .../content/content.restore.controller.js | 128 +++++++++++++----- .../src/views/content/restore.html | 109 +++++++++++---- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 4 +- .../Umbraco/config/lang/en_us.xml | 4 +- 4 files changed, 180 insertions(+), 65 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js index 859e6281c0..24457f6980 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js @@ -1,26 +1,91 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController", - function ($scope, relationResource, contentResource, entityResource, navigationService, appState, treeService, localizationService) { + function ($scope, relationResource, contentResource, entityResource, navigationService, appState, treeService, userService) { $scope.source = _.clone($scope.currentNode); - $scope.error = null; - $scope.success = false; + $scope.error = null; $scope.loading = true; + $scope.moving = false; + $scope.success = false; + + $scope.dialogTreeApi = {}; + $scope.searchInfo = { + showSearch: false, + results: [], + selectedSearchResults: [] + } + $scope.treeModel = { + hideHeader: false + } + userService.getCurrentUser().then(function (userData) { + $scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1; + }); + + function nodeSelectHandler(args) { + + if (args && args.event) { + args.event.preventDefault(); + args.event.stopPropagation(); + } + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + + } + + function nodeExpandedHandler(args) { + // open mini list view for list views + if (args.node.metaData.isContainer) { + openMiniListView(args.node); + } + } + + $scope.hideSearch = function () { + $scope.searchInfo.showSearch = false; + $scope.searchInfo.results = []; + } + + // method to select a search result + $scope.selectResult = function (evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler(evt, { event: evt, node: result }); + }; + + //callback when there are search results + $scope.onSearchResults = function (results) { + $scope.searchInfo.results = results; + $scope.searchInfo.showSearch = true; + }; + + $scope.onTreeInit = function () { + $scope.dialogTreeApi.callbacks.treeNodeSelect(nodeSelectHandler); + $scope.dialogTreeApi.callbacks.treeNodeExpanded(nodeExpandedHandler); + } + + // Mini list view + $scope.selectListViewNode = function (node) { + node.selected = node.selected === true ? false : true; + nodeSelectHandler({}, { node: node }); + }; + + $scope.closeMiniListView = function () { + $scope.miniListView = undefined; + }; + + function openMiniListView(node) { + $scope.miniListView = node; + } relationResource.getByChildId($scope.source.id, "relateParentDocumentOnDelete").then(function (data) { $scope.loading = false; if (!data.length) { - localizationService.localizeMany(["recycleBin_itemCannotBeRestored", "recycleBin_noRestoreRelation"]) - .then(function(values) { - $scope.success = false; - $scope.error = { - errorMsg: values[0], - data: { - Message: values[1] - } - } - }); + $scope.moving = true; return; } @@ -30,40 +95,32 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController" $scope.target = { id: -1, name: "Root" }; } else { - $scope.loading = true; + $scope.loading = true; + entityResource.getById($scope.relation.parentId, "Document").then(function (data) { $scope.loading = false; - $scope.target = data; - // make sure the target item isn't in the recycle bin - if($scope.target.path.indexOf("-20") !== -1) { - localizationService.localizeMany(["recycleBin_itemCannotBeRestored", "recycleBin_restoreUnderRecycled"]) - .then(function (values) { - $scope.success = false; - $scope.error = { - errorMsg: values[0], - data: { - Message: values[1].replace('%0%', $scope.target.name) - } - } - }); - $scope.success = false; - } + $scope.target = data; + // make sure the target item isn't in the recycle bin + if ($scope.target.path.indexOf("-20") !== -1) { + $scope.moving = true; + $scope.target = null; + } }, function (err) { - $scope.success = false; - $scope.error = err; $scope.loading = false; + $scope.error = err; }); } }, function (err) { - $scope.success = false; - $scope.error = err; + $scope.loading = false; + $scope.error = err; }); $scope.restore = function () { $scope.loading = true; - // this code was copied from `content.move.controller.js` + + // this code was copied from `content.move.controller.js` contentResource.move({ parentId: $scope.target.id, id: $scope.source.id }) .then(function (path) { @@ -88,9 +145,8 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController" }); }, function (err) { - $scope.success = false; - $scope.error = err; $scope.loading = false; + $scope.error = err; }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/restore.html b/src/Umbraco.Web.UI.Client/src/views/content/restore.html index 83a69effb6..ead6ed91ab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/restore.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/restore.html @@ -1,34 +1,93 @@
-
- +
+ + + - - +
+
+
{{error.errorMsg}}
+
{{error.data.Message}}
+
+
-

- Restore {{source.name}} under {{target.name}}? -

+
+
+ {{source.name}} + was restored under + was moved underneath + {{target.name}} +
+ +
-
-
-
{{error.errorMsg}}
-
{{error.data.Message}}
-
-
+
-
-
- {{source.name}} was moved underneath {{target.name}} -
- -
+

+ Restore {{source.name}} under {{target.name}}? +

- +
+ +
+
+
+
Cannot automatically restore this item
+
There is no location where this item can be automatically restored. You can move the item manually using the tree below.
+
+
+ +
+ + + +
+ + + + +
+ + +
+
+ + + + +
+ +
- + + +
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 69f106a775..993286f4c6 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1968,8 +1968,8 @@ To manage your website, simply open the Umbraco back office and start adding con Trashed content with Id: {0} related to original parent content with Id: {1} Trashed media with Id: {0} related to original parent media item with Id: {1} Cannot automatically restore this item - There is no 'restore' relation found for this node. Use the Move menu item to move it manually. - The item you want to restore it under ('%0%') is in the recycle bin. Use the Move menu item to move the item manually. + There is no location where this item can be automatically restored. You can move the item manually using the tree below. + was restored under Direction diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index eaa1c6c39e..b15ff24729 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -2024,8 +2024,8 @@ To manage your website, simply open the Umbraco back office and start adding con Trashed content with Id: {0} related to original parent content with Id: {1} Trashed media with Id: {0} related to original parent media item with Id: {1} Cannot automatically restore this item - There is no 'restore' relation found for this node. Use the Move menu item to move it manually. - The item you want to restore it under ('%0%') is in the recycle bin. Use the Move menu item to move the item manually. + There is no location where this item can be automatically restored. You can move the item manually using the tree below. + was restored under Direction From 3b8a570c3c3356c745aa8a51d5347e0173d4e03b Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 13 Jan 2019 15:53:42 +0100 Subject: [PATCH 19/25] V8: Safeguard against invalid culture in UMB_MCULTURE cookie (#4051) --- .../src/controllers/navigation.controller.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js index 335fe99e7c..e023c6d23c 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js @@ -355,7 +355,9 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar if (!currCulture) { // no culture in the request, let's look for one in the cookie that's set when changing language var defaultCulture = $cookies.get("UMB_MCULTURE"); - if (!defaultCulture) { + if (!defaultCulture || !_.find($scope.languages, function (l) { + return l.culture.toLowerCase() === defaultCulture.toLowerCase(); + })) { // no luck either, look for the default language var defaultLang = _.find($scope.languages, function (l) { return l.isDefault; @@ -399,9 +401,10 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar $scope.selectLanguage = function (language) { $location.search("mculture", language.culture); - // add the selected culture to a cookie so the user will log back into the same culture later on (cookie max age is one year = 31536000 seconds) - // NOTE: $cookies doesn't support max-age, so we need to go the good ol' JS way about setting the cookie - document.cookie = "UMB_MCULTURE=" +language.culture + ";path=/;max-age=31536000;"; + // add the selected culture to a cookie so the user will log back into the same culture later on (cookie lifetime = one year) + var expireDate = new Date(); + expireDate.setDate(expireDate.getDate() + 365); + $cookies.put("UMB_MCULTURE", language.culture, {path: "/", expires: expireDate}); // close the language selector $scope.page.languageSelectorIsOpen = false; From be44741bf986c1961fed8ff7119f6e10b1b984fa Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Sun, 13 Jan 2019 15:58:31 +0100 Subject: [PATCH 20/25] Localized parts of the codefile and template resources (#3495) --- .../src/common/resources/codefile.resource.js | 17 +++++++++++++---- .../src/common/resources/template.resource.js | 7 +++++-- .../src/views/partialviews/create.html | 2 +- .../src/views/partialviews/delete.controller.js | 6 ++++++ .../src/views/partialviews/delete.html | 7 +++++++ .../src/views/templates/delete.controller.js | 7 +++++++ .../src/views/templates/delete.html | 9 ++++++++- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 7 +++++++ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 7 +++++++ .../umbraco/config/lang/en_us.xml | 7 +++++++ 10 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js index 7cd55b268a..c7bbbe9227 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js @@ -3,7 +3,7 @@ * @name umbraco.resources.codefileResource * @description Loads in data for files that contain code such as js scripts, partial views and partial view macros **/ -function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { +function codefileResource($q, $http, umbDataFormatter, umbRequestHelper, localizationService) { return { @@ -106,13 +106,16 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { * */ deleteByPath: function (type, virtualpath) { + + var promise = localizationService.localize("codefile_deleteItemFailed", [virtualpath]); + return umbRequestHelper.resourcePromise( $http.post( umbRequestHelper.getApiUrl( "codeFileApiBaseUrl", "Delete", [{ type: type }, { virtualPath: virtualpath}])), - "Failed to delete item: " + virtualpath); + promise); }, /** @@ -236,13 +239,19 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { * */ - createContainer: function(type, parentId, name) { + createContainer: function (type, parentId, name) { + + // Is the parent ID numeric? + var key = "codefile_createFolderFailedBy" + (isNaN(parseInt(parentId)) ? "Name" : "Id"); + + var promise = localizationService.localize(key, [parentId]); + return umbRequestHelper.resourcePromise( $http.post(umbRequestHelper.getApiUrl( "codeFileApiBaseUrl", "PostCreateContainer", { type: type, parentId: parentId, name: encodeURIComponent(name) })), - 'Failed to create a folder under parent id ' + parentId); + promise); } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js index f969864ba1..377bb415fc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js @@ -3,7 +3,7 @@ * @name umbraco.resources.templateResource * @description Loads in data for templates **/ -function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { +function templateResource($q, $http, umbDataFormatter, umbRequestHelper, localizationService) { return { @@ -152,13 +152,16 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { * */ deleteById: function(id) { + + var promise = localizationService.localize("template_deleteByIdFailed", [id]); + return umbRequestHelper.resourcePromise( $http.post( umbRequestHelper.getApiUrl( "templateApiBaseUrl", "DeleteById", [{ id: id }])), - "Failed to delete item " + id); + promise); }, /** diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/create.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/create.html index 4d15ab6c01..5cd739adf5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/create.html @@ -57,7 +57,7 @@
- + diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.controller.js index b25ac3c7b8..1e615ad0d4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.controller.js @@ -12,6 +12,9 @@ function PartialViewsDeleteController($scope, codefileResource, treeService, nav //mark it for deletion (used in the UI) $scope.currentNode.loading = true; + + // Reset the error message + $scope.error = null; codefileResource.deleteByPath('partialViews', $scope.currentNode.id) .then(function() { @@ -21,6 +24,9 @@ function PartialViewsDeleteController($scope, codefileResource, treeService, nav //TODO: Need to sync tree, etc... treeService.removeNode($scope.currentNode); navigationService.hideMenu(); + }, function (err) { + $scope.currentNode.loading = false; + $scope.error = err; }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html index 0f75e8514e..c0fdf2c77f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html @@ -1,6 +1,13 @@
+
+
+
{{error.errorMsg}}
+
{{error.data.message}}
+
+
+

Are you sure you want to delete {{currentNode.name}} ?

diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js index 8995cb1a31..d019a44a10 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js @@ -12,6 +12,10 @@ function TemplatesDeleteController($scope, templateResource , treeService, navig //mark it for deletion (used in the UI) $scope.currentNode.loading = true; + + // Reset the error message + $scope.error = null; + templateResource.deleteById($scope.currentNode.id).then(function () { $scope.currentNode.loading = false; @@ -21,6 +25,9 @@ function TemplatesDeleteController($scope, templateResource , treeService, navig //TODO: Need to sync tree, etc... treeService.removeNode($scope.currentNode); navigationService.hideMenu(); + }, function (err) { + $scope.currentNode.loading = false; + $scope.error = err; }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/delete.html b/src/Umbraco.Web.UI.Client/src/views/templates/delete.html index 34648aa43e..c98677f764 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/delete.html @@ -1,11 +1,18 @@
+
+
+
{{error.errorMsg}}
+
{{error.data.message}}
+
+
+

Are you sure you want to delete {{currentNode.name}} ?

- +
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 658dd091f6..b4ab08422e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -182,6 +182,11 @@ Overførsel af egenskaber kunne ikke fuldføres, da en eller flere egenskaber er indstillet til at blive overført mere end én gang. Kun andre dokumenttyper, der er gyldige på denne placering, vises. + + Oprettelse af mappen under parent med ID %0% fejlede + Oprettelse af mappen under parent med navnet %0% fejlede + Sletning af filen/mappen fejlede: %0% + Udgivet Om siden @@ -282,6 +287,7 @@ Hvor ønsker du at oprette den nye %0% Opret under Vælg den dokumenttype, du vil oprette en indholdsskabelon til + Angiv et navn for mappen Vælg en type og skriv en titel "dokument typer".]]> "media typer".]]> @@ -1158,6 +1164,7 @@ Mange hilsner fra Umbraco robotten + Sletning af skabelonen med ID %0% fejlede Rediger skabelon Sektioner diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index cd5f32da0d..7d3bfa7c99 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -188,6 +188,11 @@ Could not complete property mapping as one or more properties have more than one mapping defined. Only alternate types valid for the current location are displayed. + + Failed to create a folder under parent with ID %0% + Failed to create a folder under parent with name %0% + Failed to delete item: %0% + Is Published About this page @@ -290,6 +295,7 @@ Where do you want to create the new %0% Create an item under Select the document type you want to make a content template for + Enter a folder name Choose a type and a title "document types".]]> "media types".]]> @@ -1483,6 +1489,7 @@ To manage your website, simply open the Umbraco back office and start adding con + Failed to delete template with ID %0% Edit template Sections diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 88b736a34f..0a1756038f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -189,6 +189,11 @@ Could not complete property mapping as one or more properties have more than one mapping defined. Only alternate types valid for the current location are displayed. + + Failed to create a folder under parent with ID %0% + Failed to create a folder under parent with name %0% + Failed to delete item: %0% + Is Published About this page @@ -292,6 +297,7 @@ Where do you want to create the new %0% Create an item under Select the document type you want to make a content template for + Enter a folder name Choose a type and a title "document types".]]> "media types".]]> @@ -1481,6 +1487,7 @@ To manage your website, simply open the Umbraco back office and start adding con Styles + Failed to delete template with ID %0% Edit template Sections From 941baf09cefe0cf32aae027c17f820f68e1390b9 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Sun, 13 Jan 2019 16:17:44 +0100 Subject: [PATCH 21/25] Fix linkpicker overlay issues (#3718) --- .../linkpicker/linkpicker.controller.js | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js index 7b890edbf4..fcce34621b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js @@ -25,30 +25,31 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.showTarget = $scope.model.hideTarget !== true; if (dialogOptions.currentTarget) { - $scope.model.target = dialogOptions.currentTarget; + // clone the current target so we don't accidentally update the caller's model while manipulating $scope.model.target + $scope.model.target = angular.copy(dialogOptions.currentTarget); //if we have a node ID, we fetch the current node to build the form data if ($scope.model.target.id || $scope.model.target.udi) { //will be either a udi or an int var id = $scope.model.target.udi ? $scope.model.target.udi : $scope.model.target.id; - if (!$scope.model.target.path) { + // is it a content link? + if (!$scope.model.target.isMedia) { + // get the content path + entityResource.getPath(id, "Document").then(function(path) { + //now sync the tree to this path + $scope.dialogTreeEventHandler.syncTree({ + path: path, + tree: "content" + }); + }); - entityResource.getPath(id, "Document").then(function (path) { - $scope.model.target.path = path; - //now sync the tree to this path - $scope.dialogTreeEventHandler.syncTree({ - path: $scope.model.target.path, - tree: "content" - }); - }); - } - - // if a link exists, get the properties to build the anchor name list - contentResource.getById(id).then(function (resp) { - $scope.model.target.url = resp.urls[0]; - $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); - }); + // get the content properties to build the anchor name list + contentResource.getById(id).then(function (resp) { + $scope.model.target.url = resp.urls[0]; + $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); + }); + } } else if ($scope.model.target.url.length) { // a url but no id/udi indicates an external link - trim the url to remove the anchor/qs // only do the substring if there's a # or a ? @@ -122,6 +123,12 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", $scope.mediaPickerOverlay.show = false; $scope.mediaPickerOverlay = null; + + // make sure the content tree has nothing highlighted + $scope.dialogTreeEventHandler.syncTree({ + path: "-1", + tree: "content" + }); } }; }); From 92c278c593353aaa4af3011f68242734cc4e66b4 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 14 Jan 2019 07:12:21 +0100 Subject: [PATCH 22/25] Adds missing translation, fixes #4054 --- src/Umbraco.Web.UI/Umbraco/config/lang/en.xml | 1 + src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 02b10fe22f..386d3af518 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1254,6 +1254,7 @@ To manage your website, simply open the Umbraco back office and start adding con This Content Type uses as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself No properties defined on this tab. Click on the "add a new property" link at the top to create a new property. + Create matching template Add icon diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 83d6b9224b..5de373f571 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1278,6 +1278,7 @@ To manage your website, simply open the Umbraco back office and start adding con This Content Type uses as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself No properties defined on this tab. Click on the "add a new property" link at the top to create a new property. + Create matching template Add icon From eb6b172be26e30ef5a3b4808e0ca4d3216978d51 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 11 Jan 2019 16:24:16 +0100 Subject: [PATCH 23/25] Cleanup --- .../PublishedCache/NuCache/PublishedSnapshotService.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 3aa33f0e2f..0647e1c806 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; -using System.Web.Hosting; using CSharpTest.Net.Collections; using Newtonsoft.Json; using Umbraco.Core; @@ -15,7 +14,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; @@ -26,7 +24,6 @@ using Umbraco.Core.Services.Implement; using Umbraco.Web.Cache; using Umbraco.Web.Install; using Umbraco.Web.PublishedCache.NuCache.DataSource; -using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Routing; namespace Umbraco.Web.PublishedCache.NuCache From c17ee34edb380229b55a8bd812d88517b921e5a8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 11 Jan 2019 16:34:58 +0100 Subject: [PATCH 24/25] Support enlisting in exiting scope contexts (why not?) --- src/Umbraco.Core/Scoping/ScopeContext.cs | 30 +++++++++++++----------- src/Umbraco.Tests/Scoping/ScopeTests.cs | 16 ++++--------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Core/Scoping/ScopeContext.cs b/src/Umbraco.Core/Scoping/ScopeContext.cs index dd26fda85e..4ba1999474 100644 --- a/src/Umbraco.Core/Scoping/ScopeContext.cs +++ b/src/Umbraco.Core/Scoping/ScopeContext.cs @@ -7,27 +7,32 @@ namespace Umbraco.Core.Scoping internal class ScopeContext : IScopeContext, IInstanceIdentifiable { private Dictionary _enlisted; - private bool _exiting; public void ScopeExit(bool completed) { if (_enlisted == null) return; - _exiting = true; + // fixme - can we create infinite loops? + // fixme - what about nested events? will they just be plainly ignored = really bad? List exceptions = null; - foreach (var enlisted in _enlisted.Values.OrderBy(x => x.Priority)) + List orderedEnlisted; + while ((orderedEnlisted = _enlisted.Values.OrderBy(x => x.Priority).ToList()).Count > 0) { - try + _enlisted.Clear(); + foreach (var enlisted in orderedEnlisted) { - enlisted.Execute(completed); - } - catch (Exception e) - { - if (exceptions == null) - exceptions = new List(); - exceptions.Add(e); + try + { + enlisted.Execute(completed); + } + catch (Exception e) + { + if (exceptions == null) + exceptions = new List(); + exceptions.Add(e); + } } } @@ -74,9 +79,6 @@ namespace Umbraco.Core.Scoping public T Enlist(string key, Func creator, Action action = null, int priority = 100) { - if (_exiting) - throw new InvalidOperationException("Cannot enlist now, context is exiting."); - var enlistedObjects = _enlisted ?? (_enlisted = new Dictionary()); if (enlistedObjects.TryGetValue(key, out var enlisted)) diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index 3f2740291b..6c5e9a74b5 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -540,7 +540,7 @@ namespace Umbraco.Tests.Scoping var scopeProvider = ScopeProvider; bool? completed = null; - Exception exception = null; + bool? completed2 = null; Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope()) @@ -551,15 +551,7 @@ namespace Umbraco.Tests.Scoping // at that point the scope is gone, but the context is still there var ambientContext = scopeProvider.AmbientContext; - - try - { - ambientContext.Enlist("another", c2 => { }); - } - catch (Exception e) - { - exception = e; - } + ambientContext.Enlist("another", c2 => { completed2 = c2; }); }); if (complete) scope.Complete(); @@ -567,8 +559,8 @@ namespace Umbraco.Tests.Scoping Assert.IsNull(scopeProvider.AmbientScope); Assert.IsNull(scopeProvider.AmbientContext); Assert.IsNotNull(completed); - Assert.IsNotNull(exception); - Assert.IsInstanceOf(exception); + Assert.AreEqual(complete,completed.Value); + Assert.AreEqual(complete, completed2.Value); } [Test] From 05fbf6c38a91cbc0e611c510b1d785b888f11c99 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 14 Jan 2019 08:05:37 +0100 Subject: [PATCH 25/25] Fix DistributedCacheBinder ContentService.TreeChanged event --- src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs index 81b133b9ef..d522e54de6 100644 --- a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs @@ -142,8 +142,8 @@ namespace Umbraco.Web.Cache () => ContentService.Saved -= ContentService_Saved); Bind(() => ContentService.Copied += ContentService_Copied, // needed for permissions () => ContentService.Copied -= ContentService_Copied); - Bind(() => ContentService.TreeChanged += ContentService_Changed,// handles all content changes - () => ContentService.TreeChanged -= ContentService_Changed); + Bind(() => ContentService.TreeChanged += ContentService_TreeChanged,// handles all content changes + () => ContentService.TreeChanged -= ContentService_TreeChanged); // TreeChanged should also deal with this //Bind(() => ContentService.SavedBlueprint += ContentService_SavedBlueprint, @@ -206,7 +206,7 @@ namespace Umbraco.Web.Cache { } - private void ContentService_Changed(IContentService sender, TreeChange.EventArgs args) + private void ContentService_TreeChanged(IContentService sender, TreeChange.EventArgs args) { _distributedCache.RefreshContentCache(args.Changes.ToArray()); }