From d01f3a1012492006cf470ca9724d99f407a91ac6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Aug 2013 15:42:28 +1000 Subject: [PATCH] Got the sort dialog posting the correct data and displaying a success message. Updated the alerts.less to not have a txt shadow as it looks pretty bad with the colors we have. --- .../common/mocks/resources/content.mocks.js | 12 ++- .../src/common/resources/content.resource.js | 20 +++++ .../src/less/alerts.less | 78 +++++++++++++++++++ src/Umbraco.Web.UI.Client/src/less/belle.less | 2 +- .../src/views/common/dialogs/login.html | 2 +- .../src/views/common/main.controller.js | 8 +- .../views/content/content.sort.controller.js | 38 +++++---- .../src/views/content/sort.html | 22 ++++-- src/Umbraco.Web/Editors/ContentController.cs | 63 +++++++++++++++ src/Umbraco.Web/Editors/MediaController.cs | 5 +- 10 files changed, 223 insertions(+), 27 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/alerts.less diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js index dca27e582f..d0cf5b8f63 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js @@ -95,10 +95,20 @@ angular.module('umbraco.mocks'). return [200, node, null]; } - + function returnSort(status, data, headers) { + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + + return [200, null, null]; + } return { register: function () { + $httpBackend + .whenPOST(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/PostSort')) + .respond(returnSort); + $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetChildren')) .respond(returnChildren); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index c610901025..bb24d8b617 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -16,6 +16,26 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { return { + sort: function (args) { + if (!args) { + throw "args cannot be null"; + } + if (!args.parentId) { + throw "args.parentId cannot be null"; + } + if (!args.sortedIds) { + throw "args.sortedIds cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("contentApiBaseUrl", "PostSort"), + { + parentId: args.parentId, + idSortOrder: args.sortedIds + }), + 'Failed to sort content'); + }, + emptyRecycleBin: function() { return umbRequestHelper.resourcePromise( $http.delete( diff --git a/src/Umbraco.Web.UI.Client/src/less/alerts.less b/src/Umbraco.Web.UI.Client/src/less/alerts.less new file mode 100644 index 0000000000..1c64378aa3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/alerts.less @@ -0,0 +1,78 @@ +// +// Alerts +// -------------------------------------------------- + + +// Base styles +// ------------------------- + +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: @baseLineHeight; + background-color: @warningBackground; + border: 1px solid @warningBorder; + .border-radius(@baseBorderRadius); +} +.alert, +.alert h4 { + // Specified for the h4 to prevent conflicts of changing @headingsColor + color: @warningText; +} +.alert h4 { + margin: 0; +} + +// Adjust close link position +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: @baseLineHeight; +} + + +// Alternate styles +// ------------------------- + +.alert-success { + background-color: @successBackground; + border-color: @successBorder; + color: @successText; +} +.alert-success h4 { + color: @successText; +} +.alert-danger, +.alert-error { + background-color: @errorBackground; + border-color: @errorBorder; + color: @errorText; +} +.alert-danger h4, +.alert-error h4 { + color: @errorText; +} +.alert-info { + background-color: @infoBackground; + border-color: @infoBorder; + color: @infoText; +} +.alert-info h4 { + color: @infoText; +} + + +// Block alerts +// ------------------------- + +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index c5b339ebff..ab54bea7b4 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -34,7 +34,7 @@ // Components: Buttons & Alerts // @import "../../lib/bootstrap/less/buttons.less"; @import "../../lib/bootstrap/less/button-groups.less"; -@import "../../lib/bootstrap/less/alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less +@import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less // Components: Nav @import "navs.less"; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html index 32ad4c97f4..5ad4c95d7e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.html @@ -9,7 +9,7 @@ - +
{{errorMsg}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js index 41a927086f..08c86f1f84 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/main.controller.js @@ -31,7 +31,13 @@ function MainController($scope, $routeParams, $rootScope, $timeout, notification var el = event.target.nodeName; var pEl = event.target.parentElement.nodeName; - if(el != "I" && el != "A" && el != "BUTTON" && pEl != "A" && pEl != "BUTTON"){ + //SD: I've updated this so that we don't close the dialog when clicking inside of the dialog + if ($(event.target).closest("#dialog").length === 1) { + return; + } + + //SD: I've added a check for INPUT elements too + if(el != "I" && el != "A" && el != "BUTTON" && pEl != "A" && pEl != "BUTTON" && el != "INPUT" && pEl != "INPUT"){ $rootScope.$emit("closeDialogs", event); } }; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js index e89678e3ee..bb151af2eb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js @@ -6,7 +6,9 @@ * @description * The controller for deleting content */ -function ContentSortController($scope, $element, contentResource, treeService, navigationService) { +function ContentSortController($scope, contentResource, angularHelper) { + + $scope.complete = false; //defines the options for the jquery sortable $scope.sortableOptions = { @@ -14,7 +16,18 @@ function ContentSortController($scope, $element, contentResource, treeService, n cursor: "move", placeholder: "ui-sortable-placeholder", update: function (ev, ui) { + //highlight the item when the position is changed $(ui.item).effect("highlight", { color: "#049cdb" }, 500); + }, + stop: function (ev, ui) { + //the ui-sortable directive already ensures that our list is re-sorted, so now we just + // need to update the sortOrder to the index of each item + angularHelper.safeApply($scope, function () { + angular.forEach($scope.itemsToSort, function (val, index) { + val.sortOrder = index + 1; + }); + + }); } }; @@ -24,21 +37,14 @@ function ContentSortController($scope, $element, contentResource, treeService, n $scope.performSort = function() { - //contentResource.sort($scope.currentNode.id).then(function () { - // $scope.currentNode.loading = false; - - // //get the root node before we remove it - // var rootNode = treeService.getTreeRoot($scope.currentNode); - - // //TODO: Need to sync tree, etc... - // treeService.removeNode($scope.currentNode); - - // //ensure the recycle bin has child nodes now - // var recycleBin = treeService.getDescendantNode(rootNode, -20); - // recycleBin.hasChildren = true; - - // navigationService.hideMenu(); - //}); + var sortedIds = []; + for (var i = 0; i < $scope.itemsToSort.length; i++) { + sortedIds.push($scope.itemsToSort[i].id); + } + contentResource.sort({ parentId: $scope.currentNode.id, sortedIds: sortedIds }) + .then(function() { + $scope.complete = true; + }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/sort.html b/src/Umbraco.Web.UI.Client/src/views/content/sort.html index 270b2f1c22..fe5b8d8fce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/sort.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/sort.html @@ -1,5 +1,8 @@ -
- +
+ +

Sort children of {{nav.ui.currentNode.name}}

@@ -15,14 +18,23 @@ {{model.name}} {{model.updateDate}} {{model.sortOrder}} - +
- +
- +
+
+
+ Sorting successful +
+ +
+ +
+
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 9b126ec3d7..feeffa90d4 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -8,6 +8,7 @@ using System.Web.Http; using System.Web.Http.ModelBinding; using AutoMapper; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -24,9 +25,12 @@ using Umbraco.Web.WebApi.Filters; using umbraco; using Umbraco.Core.Models; using Umbraco.Core.Dynamics; +using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { + //TODO: For each of these requests the user will need to have access to the content app! + /// /// The API controller used for editing content /// @@ -54,6 +58,8 @@ namespace Umbraco.Web.Editors { var foundContent = ((ContentService) Services.ContentService).GetByIds(ids); + //TODO: We need to check if the current user is allowed to see each node! + return foundContent.Select(Mapper.Map); } @@ -69,6 +75,9 @@ namespace Umbraco.Web.Editors { HandleContentNotFound(id); } + + //TODO: We need to check if the current user is allowed to see this node! + return Mapper.Map(foundContent); } @@ -108,6 +117,8 @@ namespace Umbraco.Web.Editors //TODO: This will be horribly inefficient for paging! This is because our datasource/repository // doesn't support paging at the SQL level... and it'll be pretty interesting to try to make that work. + //TODO: We need to check the nodes returned to see if the current user is allowed to see each of them! + var foundContent = Services.ContentService.GetById(id); if (foundContent == null) { @@ -162,6 +173,8 @@ namespace Umbraco.Web.Editors //TODO: We'll need to save the new template, publishat, etc... values here + //TODO: We need to check the user's permissions to see if they are allowed to do this! + MapPropertyValues(contentItem); //We need to manually check the validation results here because: @@ -245,6 +258,8 @@ namespace Umbraco.Web.Editors return HandleContentNotFound(id, false); } + //TODO: We need to check if the user is allowed to do this! + //if the current item is in the recycle bin if (foundContent.IsInRecycleBin() == false) { @@ -265,10 +280,58 @@ namespace Umbraco.Web.Editors [HttpDelete] public HttpResponseMessage EmptyRecycleBin() { + //TODO: We need to check if the user is allowed access to the recycle bin! + Services.ContentService.EmptyRecycleBin(); return Request.CreateResponse(HttpStatusCode.OK); } + /// + /// Change the sort order for media + /// + /// + /// + public HttpResponseMessage PostSort(ContentSortOrder sorted) + { + if (sorted == null) + { + return Request.CreateResponse(HttpStatusCode.NotFound); + } + + //if there's nothing to sort just return ok + if (sorted.IdSortOrder.Length == 0) + { + return Request.CreateResponse(HttpStatusCode.OK); + } + + if (Security.UserHasAppAccess(Constants.Applications.Content, UmbracoUser) == false) + { + return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "User has no access to this application"); + } + + //TODO: We need to check if the user is allowed to sort here! + + var contentService = Services.ContentService; + var sortedContent = new List(); + try + { + sortedContent.AddRange(sorted.IdSortOrder.Select(contentService.GetById)); + + // Save content with new sort order and update content xml in db accordingly + if (contentService.Sort(sortedContent) == false) + { + LogHelper.Warn("Content sorting failed, this was probably caused by an event being cancelled"); + return Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Content sorting failed, this was probably caused by an event being cancelled"); + } + return Request.CreateResponse(HttpStatusCode.OK); + } + catch (Exception ex) + { + LogHelper.Error("Could not update content sort order", ex); + throw; + } + } + private void ShowMessageForStatus(PublishStatus status, ContentItemDisplay display) { switch (status.StatusType) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 48d95c125c..3226083bcb 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -17,6 +17,7 @@ using System.Linq; using Umbraco.Web.WebApi.Binders; using Umbraco.Web.WebApi.Filters; using umbraco; +using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { @@ -180,7 +181,7 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK); } - if (!Security.UserHasAppAccess(global::Umbraco.Core.Constants.Applications.Media, UmbracoUser)) + if (Security.UserHasAppAccess(Constants.Applications.Media, UmbracoUser) == false) { return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "User has no access to this application"); } @@ -204,7 +205,7 @@ namespace Umbraco.Web.Editors LogHelper.Error("Could not update media sort order", ex); throw; } - } + } } }