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 0ce43585d3..6c03c4ed11 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 @@ -40,13 +40,22 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc _formatNodeDataForUseInUI: function (parentNode, treeNodes, section, level) { //if no level is set, then we make it 1 var childLevel = (level ? level : 1); + //set the section if it's not already set if (!parentNode.section) { parentNode.section = section; } + //create a method outside of the loop to return the parent - otherwise jshint blows up + var funcParent = function() { + return parentNode; + }; for (var i = 0; i < treeNodes.length; i++) { treeNodes[i].level = childLevel; - treeNodes[i].parent = parentNode; + + //create a function to get the parent node, we could assign the parent node but + // then we cannot serialize this entity because we have a cyclical reference. + // Instead we just make a function to return the parentNode. + treeNodes[i].parent = funcParent; //set the section for each tree node - this allows us to reference this easily when accessing tree nodes treeNodes[i].section = section; @@ -242,11 +251,11 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc /** Removes a given tree node from the tree */ removeNode: function(treeNode) { - if (treeNode.parent == null) { + if (treeNode.parent() == null) { throw "Cannot remove a node that doesn't have a parent"; } //remove the current item from it's siblings - treeNode.parent.children.splice(treeNode.parent.children.indexOf(treeNode), 1); + treeNode.parent().children.splice(treeNode.parent().children.indexOf(treeNode), 1); }, /** Removes all child nodes from a given tree node */ @@ -309,7 +318,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc root = current; } else { - current = current.parent; + current = current.parent(); } } return root; @@ -418,7 +427,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc if (!node) { throw "node cannot be null"; } - if (!node.parent) { + if (!node.parent()) { throw "cannot reload a single node without a parent"; } if (!node.section) { @@ -430,7 +439,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc //set the node to loading node.loading = true; - this.getChildren({ node: node.parent, section: node.section }).then(function(data) { + this.getChildren({ node: node.parent(), section: node.section }).then(function(data) { //ok, now that we have the children, find the node we're reloading var found = _.find(data, function(item) { @@ -438,14 +447,14 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc }); if (found) { //now we need to find the node in the parent.children collection to replace - var index = _.indexOf(node.parent.children, node); + var index = _.indexOf(node.parent().children, node); //the trick here is to not actually replace the node - this would cause the delete animations //to fire, instead we're just going to replace all the properties of this node. - _.extend(node.parent.children[index], found); + _.extend(node.parent().children[index], found); //set the node to loading - node.parent.children[index].loading = false; + node.parent().children[index].loading = false; //return - deferred.resolve(node.parent.children[index]); + deferred.resolve(node.parent().children[index]); } else { deferred.reject(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js index 7a44e6d580..cedb409117 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js @@ -39,7 +39,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController", $scope.success = true; //reloads the parent - navigationService.reloadNode(dialogOptions.currentNode.parent); + navigationService.reloadNode(dialogOptions.currentNode.parent()); //reloads the target navigationService.syncTree({ tree: "content", path: path, forceReload: true }); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js index 8659eadf07..c4d5ca49a5 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/tree-service.spec.js @@ -253,7 +253,7 @@ describe('tree service tests', function () { cacheKey: "__content", filter: function (currentCache) { var toRemove = treeService.getDescendantNode(currentCache.root, 1235); - toRemove.parent.children = _.without(toRemove.parent.children, toRemove); + toRemove.parent().children = _.without(toRemove.parent().children, toRemove); return currentCache; } }); @@ -286,8 +286,6 @@ describe('tree service tests', function () { cache = treeService._getTreeCache(); - console.log(" blah: " + cache.__content.root.children.length); - expect(cache.__content.root.children.length).toBe(4); expect(cache.__content.root.children[0].children).toBeNull(); expect(cache.__content.root.children[0].expanded).toBe(false); diff --git a/src/Umbraco.Web/Models/Trees/SectionRootNode.cs b/src/Umbraco.Web/Models/Trees/SectionRootNode.cs index 59b50a7792..3116159f5f 100644 --- a/src/Umbraco.Web/Models/Trees/SectionRootNode.cs +++ b/src/Umbraco.Web/Models/Trees/SectionRootNode.cs @@ -1,4 +1,5 @@ using System.Runtime.Serialization; +using Umbraco.Core; namespace Umbraco.Web.Models.Trees { @@ -33,7 +34,7 @@ namespace Umbraco.Web.Models.Trees } private SectionRootNode(string nodeId, string getChildNodesUrl, string menuUrl) - : base(nodeId, getChildNodesUrl, menuUrl) + : base(nodeId, null, getChildNodesUrl, menuUrl) { //default to false IsContainer = false; diff --git a/src/Umbraco.Web/Models/Trees/TreeNode.cs b/src/Umbraco.Web/Models/Trees/TreeNode.cs index c13fae94d9..c413edea37 100644 --- a/src/Umbraco.Web/Models/Trees/TreeNode.cs +++ b/src/Umbraco.Web/Models/Trees/TreeNode.cs @@ -19,11 +19,15 @@ namespace Umbraco.Web.Models.Trees /// Internal constructor, to create a tree node use the CreateTreeNode methods of the TreeApiController. /// /// + /// The parent id for the current node /// /// - internal TreeNode(string nodeId, string getChildNodesUrl, string menuUrl) + internal TreeNode(string nodeId, string parentId, string getChildNodesUrl, string menuUrl) { + Mandate.ParameterNotNullOrEmpty(nodeId, "nodeId"); + Id = nodeId; + ParentId = parentId; ChildNodesUrl = getChildNodesUrl; MenuUrl = menuUrl; CssClasses = new List(); @@ -31,6 +35,9 @@ namespace Umbraco.Web.Models.Trees Icon = "icon-folder-close"; } + [DataMember(Name = "parentId", IsRequired = true)] + public new object ParentId { get; set; } + /// /// A flag to set whether or not this node has children /// @@ -63,13 +70,6 @@ namespace Umbraco.Web.Models.Trees /// [DataMember(Name = "menuUrl")] public string MenuUrl { get; set; } - - /// - /// The icon to use for the node, this can be either a path to an image or a Css class. - /// If a '/' is found in the string then it will be considered a path to an image. - /// - [DataMember(Name = "icon")] - public string Icon { get; set; } /// /// Returns true if the icon represents a CSS class instead of a file path diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 5bfa20e030..9cfc2bcb14 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -75,6 +75,7 @@ namespace Umbraco.Web.Trees var node = CreateTreeNode( e.Id.ToInvariantString(), + id, queryStrings, e.Name, e.ContentTypeIcon, diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 352f0c3521..5b9a6181f3 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -72,6 +72,7 @@ namespace Umbraco.Web.Trees { nodes.Add(CreateTreeNode( RecycleBinId.ToInvariantString(), + id, queryStrings, ui.GetText("general", "recycleBin"), "icon-trash", diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 76d934c9cf..c41deb350b 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -33,6 +33,7 @@ namespace Umbraco.Web.Trees .Select(dt => CreateTreeNode( dt.Id.ToInvariantString(), + id, queryStrings, dt.Name, "icon-autofill", diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs index 5aacb96f40..9cce00779f 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs @@ -348,7 +348,7 @@ namespace Umbraco.Web.Trees //TODO: Might need to add stuff to additional attributes - var node = new TreeNode(xmlTreeNode.NodeID, childNodesSource, menuSource) + var node = new TreeNode(xmlTreeNode.NodeID, isRoot ? null : parentId, childNodesSource, menuSource) { HasChildren = xmlTreeNode.HasChildren, Icon = xmlTreeNode.Icon, diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index fd85910c8f..e8283b0f90 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -55,7 +55,7 @@ namespace Umbraco.Web.Trees foreach (var entity in entities) { var e = (UmbracoEntity)entity; - var node = CreateTreeNode(e.Id.ToInvariantString(), queryStrings, e.Name, e.ContentTypeIcon, e.HasChildren); + var node = CreateTreeNode(e.Id.ToInvariantString(), id, queryStrings, e.Name, e.ContentTypeIcon, e.HasChildren); node.AdditionalData.Add("contentType", e.ContentTypeAlias); nodes.Add(node); diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index f7d536c083..6d4c2eb743 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -30,14 +30,14 @@ namespace Umbraco.Web.Trees for (var i = 97; i < 123; i++) { var charString = ((char) i).ToString(CultureInfo.InvariantCulture); - var folder = CreateTreeNode(charString, queryStrings, charString, "icon-folder-close", true); + var folder = CreateTreeNode(charString, id, queryStrings, charString, "icon-folder-close", true); folder.NodeType = "member-folder"; nodes.Add(folder); } //list out 'Others' if the membership provider is umbraco if (Member.InUmbracoMemberMode()) { - var folder = CreateTreeNode("others", queryStrings, "Others", "icon-folder-close", true); + var folder = CreateTreeNode("others", id, queryStrings, "Others", "icon-folder-close", true); folder.NodeType = "member-folder"; nodes.Add(folder); } @@ -52,7 +52,7 @@ namespace Umbraco.Web.Trees //get the members from our member data layer nodes.AddRange( Member.getMemberFromFirstLetter(id.ToCharArray()[0]) - .Select(m => CreateTreeNode(m.UniqueId.ToString("N"), queryStrings, m.Text, "icon-user"))); + .Select(m => CreateTreeNode(m.UniqueId.ToString("N"), id, queryStrings, m.Text, "icon-user"))); } else { @@ -60,7 +60,7 @@ namespace Umbraco.Web.Trees int total; nodes.AddRange( Membership.Provider.FindUsersByName(id + "%", 0, 9999, out total).Cast() - .Select(m => CreateTreeNode(m.ProviderUserKey.ToString(), queryStrings, m.UserName, "icon-user"))); + .Select(m => CreateTreeNode(m.ProviderUserKey.ToString(), id, queryStrings, m.UserName, "icon-user"))); } } else if (id == "others") @@ -68,7 +68,7 @@ namespace Umbraco.Web.Trees //others will only show up when in umbraco membership mode nodes.AddRange( Member.getAllOtherMembers() - .Select(m => CreateTreeNode(m.Id.ToInvariantString(), queryStrings, m.Text, "icon-user"))); + .Select(m => CreateTreeNode(m.Id.ToInvariantString(), id, queryStrings, m.Text, "icon-user"))); } } return nodes; diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index 35c8d75de5..e31cb85b0d 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -155,6 +155,7 @@ namespace Umbraco.Web.Trees var node = new TreeNode( rootNodeAsString, + null, //this is a root node, there is no parent Url.GetTreeUrl(GetType(), rootNodeAsString, queryStrings), Url.GetMenuUrl(GetType(), rootNodeAsString, queryStrings)) { @@ -172,14 +173,15 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes /// /// + /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title) { var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings); var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings); - var node = new TreeNode(id, jsonUrl, menuUrl) { Name = title }; + var node = new TreeNode(id, parentId, jsonUrl, menuUrl) { Name = title }; return node; } @@ -187,15 +189,16 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes /// /// + /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon) { var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings); var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings); - var node = new TreeNode(id, jsonUrl, menuUrl) { Name = title, Icon = icon }; + var node = new TreeNode(id, parentId, jsonUrl, menuUrl) { Name = title, Icon = icon }; return node; } @@ -203,16 +206,17 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes /// /// + /// /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, string routePath) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, string routePath) { var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings); var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings); - var node = new TreeNode(id, jsonUrl, menuUrl) { Name = title, RoutePath = routePath, Icon = icon }; + var node = new TreeNode(id, parentId, jsonUrl, menuUrl) { Name = title, RoutePath = routePath, Icon = icon }; return node; } @@ -220,14 +224,15 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes and automatically generate the json url /// /// + /// /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, bool hasChildren) { - var treeNode = CreateTreeNode(id, queryStrings, title, icon); + var treeNode = CreateTreeNode(id, parentId, queryStrings, title, icon); treeNode.HasChildren = hasChildren; return treeNode; } @@ -236,15 +241,16 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes and automatically generate the json url /// /// + /// /// /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren, string routePath) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, bool hasChildren, string routePath) { - var treeNode = CreateTreeNode(id, queryStrings, title, icon); + var treeNode = CreateTreeNode(id, parentId, queryStrings, title, icon); treeNode.HasChildren = hasChildren; treeNode.RoutePath = routePath; return treeNode;