diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js index 179d86c5e3..b7020e7aee 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js @@ -1,8 +1,46 @@ angular.module("umbraco.directives") -.directive('umbContextMenu', function () { +.directive('umbContextMenu', function ($injector) { return { restrict: 'E', replace: true, - templateUrl: 'views/directives/umb-contextmenu.html' + templateUrl: 'views/directives/umb-contextmenu.html', + link: function (scope, element, attrs, ctrl) { + + //adds a handler to the context menu item click, we need to handle this differently + //depending on what the menu item is supposed to do. + scope.executeMenuItem = function (currentNode, action, currentSection) { + + if (action.metaData && action.metaData["jsAction"] && angular.isString(action.metaData["jsAction"])) { + + //we'll try to get the jsAction from the injector + var menuAction = action.metaData["jsAction"].split('.'); + if (menuAction.length !== 2) { + throw "The jsAction assigned to a menu action must have two parts delimited by a '.' "; + } + + var service = $injector.get(menuAction[0]); + if (!service) { + throw "The angular service " + menuAction[0] + " could not be found"; + } + + var method = service[menuAction[1]]; + + if (!method) { + throw "The method " + menuAction[1] + " on the angular service " + menuAction[0] + " could not be found"; + } + + method.apply(this, [{ + treeNode: currentNode, + action: action, + section: currentSection + }]); + } + else { + //by default we launch the dialog + scope.openDialog(currentNode, action, currentSection); + } + }; + + } }; }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js index 950ebf8da5..fcd6777a94 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js @@ -76,7 +76,7 @@ angular.module("umbraco.directives") * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations. */ scope.animation = function () { - if (enableDeleteAnimations) { + if (enableDeleteAnimations && scope.node.expanded) { return { leave: 'tree-node-delete-leave' }; } else { @@ -94,46 +94,27 @@ angular.module("umbraco.directives") if (node.expanded) { enableDeleteAnimations = false; emitEvent("treeNodeCollapsing", { element: arrow, node: node }); - - node.expanded = false; - node.children = []; + node.expanded = false; } else { //emit treeNodeExpanding event, if a callback object is set on the tree emitEvent("treeNodeExpanding", { element: arrow, node: node }); - - //set element state to loading - node.loading = true; - - //get the children from the tree service - treeService.getChildren({ node: node, section: scope.section }) - .then(function(data) { - - //emit event - emitEvent("treeNodeLoaded", { element: arrow, node: node, children: data }); - - //set state to done and expand - node.loading = false; - node.children = data; - node.expanded = true; - - //emit expanded event - emitEvent("treeNodeExpanded", { element: arrow, node: node, children: data }); - - }, function(reason) { - - //in case of error, emit event - emitEvent("treeNodeLoadError", { element: arrow, node: node, error: reason }); - - //stop show the loading indicator - node.loading = false; - - //tell notications about the error - notificationsService.error(reason); - }); - enableDeleteAnimations = true; + if (!node.children || (angular.isArray(node.children) && node.children.length === 0)) { + //get the children from the tree service + treeService.loadNodeChildren({ node: node, section: scope.section }) + .then(function(data) { + //emit expanded event + emitEvent("treeNodeExpanded", { element: arrow, node: node, children: data }); + enableDeleteAnimations = true; + }); + } + else { + emitEvent("treeNodeExpanded", { element: arrow, node: node, children: node.children }); + node.expanded = true; + enableDeleteAnimations = true; + } } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/menuactions.service.js b/src/Umbraco.Web.UI.Client/src/common/services/menuactions.service.js new file mode 100644 index 0000000000..7e73c13763 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/menuactions.service.js @@ -0,0 +1,11 @@ + +function umbracoMenuActions($q, treeService) { + + return { + refresh: function(args) { + treeService.loadNodeChildren({ node: args.treeNode, section: args.section }); + } + }; +} + +angular.module('umbraco.services').factory('umbracoMenuActions', umbracoMenuActions); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js index 1b1393979c..43de6f1f46 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js @@ -7,7 +7,7 @@ * @description * The tree service factory, used internally by the umbTree and umbTreeItem directives */ -function treeService($q, treeResource, iconHelper) { +function treeService($q, treeResource, iconHelper, notificationsService, $rootScope) { //implement this in local storage var treeArray = []; var currentSection = "content"; @@ -28,6 +28,58 @@ function treeService($q, treeResource, iconHelper) { return { + /** + * @ngdoc method + * @name umbraco.services.treeService#getMenuItemByAlias + * @methodOf umbraco.services.treeService + * @function + * + * @description + * Clears all node children, gets it's up-to-date children from the server and re-assigns them and then + * returns them in a promise. + * @param {object} args An arguments object + * @param {object} args.node The tree node + * @param {object} args.section The current section + */ + loadNodeChildren: function(args) { + if (!args) { + throw "No args object defined for getChildren"; + } + if (!args.node) { + throw "No node defined on args object for getChildren"; + } + + this.removeChildNodes(args.node); + args.node.loading = true; + + return this.getChildren(args) + .then(function(data) { + + //set state to done and expand + args.node.loading = false; + args.node.children = data; + args.node.expanded = true; + args.node.hasChildren = true; + + return data; + + }, function(reason) { + + //in case of error, emit event + $rootScope.$broadcast("treeNodeLoadError", { element: arrow, node: node, error: reason }); + + //stop show the loading indicator + node.loading = false; + + //tell notications about the error + notificationsService.error(reason); + + return reason; + }); + + }, + + /** Removes a given tree node from the tree */ removeNode: function(treeNode) { if (treeNode.parent == null) { throw "Cannot remove a node that doesn't have a parent"; @@ -36,7 +88,9 @@ function treeService($q, treeResource, iconHelper) { treeNode.parent.children.splice(treeNode.parent.children.indexOf(treeNode), 1); }, + /** Removes all child nodes from a given tree node */ removeChildNodes : function(treeNode) { + treeNode.expanded = false; treeNode.children = []; treeNode.hasChildren = false; }, @@ -59,7 +113,7 @@ function treeService($q, treeResource, iconHelper) { //check each child of this node for (var i = 0; i < treeNode.children.length; i++) { - if (treeNode.children[i].hasChildren) { + if (treeNode.children[i].children && angular.isArray(treeNode.children[i].children) && treeNode.children[i].children.length > 0) { //recurse found = this.getDescendantNode(treeNode.children[i], id); if (found) { @@ -89,14 +143,14 @@ function treeService($q, treeResource, iconHelper) { return root; }, - getTree: function (options) { + getTree: function (args) { - if(options === undefined){ - options = {}; + if (args === undefined) { + args = {}; } - var section = options.section || 'content'; - var cacheKey = options.cachekey || ''; + var section = args.section || 'content'; + var cacheKey = args.cachekey || ''; cacheKey += "_" + section; //return the cache if it exists @@ -104,7 +158,7 @@ function treeService($q, treeResource, iconHelper) { return treeArray[cacheKey]; } - return treeResource.loadApplication(options) + return treeResource.loadApplication(args) .then(function(data) { //this will be called once the tree app data has loaded var result = { @@ -175,28 +229,25 @@ function treeService($q, treeResource, iconHelper) { }); }, - getChildren: function (options) { + /** Gets the children from the server for a given node */ + getChildren: function (args) { - if(options === undefined){ - throw "No options object defined for getChildren"; + if (!args) { + throw "No args object defined for getChildren"; } - if (options.node === undefined) { - throw "No node defined on options object for getChildren"; + if (!args.node) { + throw "No node defined on args object for getChildren"; } - var section = options.section || 'content'; - var treeItem = options.node; + var section = args.section || 'content'; + var treeItem = args.node; //hack to have create as default content action var action; if(section === "content"){ action = "create"; } - - if (!options.node) { - throw "No node defined"; - } - + return treeResource.loadNodes({ section: section, node: treeItem }) .then(function(data) { //now that we have the data, we need to add the level property to each item and the view diff --git a/src/Umbraco.Web.UI.Client/src/less/tree.less b/src/Umbraco.Web.UI.Client/src/less/tree.less index f18e33637a..0375a918cf 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/tree.less @@ -40,6 +40,11 @@ width: 100%; display: table } + +.umb-tree ul.collapsed { + display:none; +} + .umb-tree a { vertical-align: middle; display: inline-block; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js index 1ee3114a49..0b7ca0d4fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js @@ -15,11 +15,14 @@ function ContentDeleteController($scope, contentResource, treeService, navigatio contentResource.deleteById($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 rootNode = treeService.getTreeRoot($scope.currentNode); + //ensure the recycle bin has child nodes now var recycleBin = treeService.getDescendantNode(rootNode, -20); recycleBin.hasChildren = true; diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/umb-contextmenu.html b/src/Umbraco.Web.UI.Client/src/views/directives/umb-contextmenu.html index b82dcb7815..641fe465bf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/directives/umb-contextmenu.html +++ b/src/Umbraco.Web.UI.Client/src/views/directives/umb-contextmenu.html @@ -9,7 +9,7 @@