diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js index 304ed7d7cb..6df4bf4071 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js @@ -89,26 +89,43 @@ angular.module('umbraco.mocks'). hasChildren: true, level: 0, menuUrl: menuUrl, - metaData: { treeType: "Umbraco.Web.Trees.ContentTreeController" } + metaData: { treeAlias: "content" } }; break; + case "media": + t = { + name: "media", + id: -1, + children: [ + { name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl } + ], + expanded: true, + hasChildren: true, + level: 0, + menuUrl: menuUrl, + metaData: { treeAlias: "media" } + }; + break; case "developer": t = { name: "developer", id: -1, children: [ - { name: "Data types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.DataTypeTreeController" } }, - { name: "Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.MacrosTreeController" } }, - { name: "Pacakges", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.PackagesTreeController" } }, - { name: "XSLT Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.XsltTreeController" } }, - { name: "Razor Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.RazorTreeController" } } + { name: "Data types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "datatype" } }, + { name: "Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "macros" } }, + { name: "Packages", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "packager" } }, + { name: "XSLT Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "xslt" } }, + { name: "Partial View Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "partialViewMacros" } } ], expanded: true, hasChildren: true, level: 0, - isContainer: true + isContainer: true }; break; @@ -117,11 +134,11 @@ angular.module('umbraco.mocks'). name: "settings", id: -1, children: [ - { name: "Stylesheets", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.StylesheetTreeController" } }, - { name: "Templates", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.TemplatesTreeController" } }, - { name: "Dictionary", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.DictionaryTreeController" } }, - { name: "Media types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.MediaTypesTreeController" } }, - { name: "Document types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.ContentTypesTreeController" } } + { name: "Stylesheets", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "stylesheets" } }, + { name: "Templates", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "templates" } }, + { name: "Dictionary", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "dictionary" } }, + { name: "Media types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "mediaTypes" } }, + { name: "Document types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "nodeTypes" } } ], expanded: true, hasChildren: true, @@ -145,7 +162,7 @@ angular.module('umbraco.mocks'). hasChildren: true, level: 0, menuUrl: menuUrl, - metaData: { treeType: "Umbraco.Web.Trees.RandomTreeController" } + metaData: { treeAlias: "randomTree" } }; break; 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 db16c31cc0..5f192da606 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 @@ -12,41 +12,50 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc var treeArray = []; var standardCssClass = 'icon umb-tree-icon sprTree'; - /** ensures there's a routePath, parent and level property on each tree node and - adds some icon specific properties so that the nodes display properly */ - function formatNodeDataForUseInUI(parentNode, treeNodes, section, level) { - //if no level is set, then we make it 1 - var childLevel = (level ? level : 1); - for (var i = 0; i < treeNodes.length; i++) { - treeNodes[i].level = childLevel; - //if there is not route path specified, then set it automatically - if (!treeNodes[i].routePath) { - treeNodes[i].routePath = section + "/edit/" + treeNodes[i].id; - } - treeNodes[i].parent = parentNode; - - //now, format the icon data - if (treeNodes[i].iconIsClass === undefined || treeNodes[i].iconIsClass) { - var converted = iconHelper.convertFromLegacyTreeNodeIcon(treeNodes[i]); - treeNodes[i].cssClass = standardCssClass + " " + converted; - if (converted.startsWith('.')) { - //its legacy so add some width/height - treeNodes[i].style = "height:16px;width:16px;"; - } - else { - treeNodes[i].style = ""; - } - } - else { - treeNodes[i].style = "background-image: url('" + treeNodes[i].iconFilePath + "');height:16px; background-position:2px 0px; background-repeat: no-repeat"; - //we need an 'icon-' class in there for certain styles to work so if it is image based we'll add this - treeNodes[i].cssClass = standardCssClass + " icon-custom-file"; - } - } - } - return { + /** Internal method that ensures there's a routePath, parent and level property on each tree node and adds some icon specific properties so that the nodes display properly */ + _formatNodeDataForUseInUI: function (parentNode, treeNodes, section, level) { + //if no level is set, then we make it 1 + var childLevel = (level ? level : 1); + for (var i = 0; i < treeNodes.length; i++) { + + treeNodes[i].level = childLevel; + treeNodes[i].parent = parentNode; + + //if there is not route path specified, then set it automatically, + //if this is a tree root node then we want to route to the section's dashboard + if (!treeNodes[i].routePath) { + if (treeNodes[i].metaData && treeNodes[i].metaData["treeAlias"]) { + //this is a root node + treeNodes[i].routePath = section; + } + else { + var treeAlias = this.getTreeAlias(treeNodes[i]); + treeNodes[i].routePath = section + "/" + treeAlias + "/edit/" + treeNodes[i].id; + } + } + + //now, format the icon data + if (treeNodes[i].iconIsClass === undefined || treeNodes[i].iconIsClass) { + var converted = iconHelper.convertFromLegacyTreeNodeIcon(treeNodes[i]); + treeNodes[i].cssClass = standardCssClass + " " + converted; + if (converted.startsWith('.')) { + //its legacy so add some width/height + treeNodes[i].style = "height:16px;width:16px;"; + } + else { + treeNodes[i].style = ""; + } + } + else { + treeNodes[i].style = "background-image: url('" + treeNodes[i].iconFilePath + "');height:16px; background-position:2px 0px; background-repeat: no-repeat"; + //we need an 'icon-' class in there for certain styles to work so if it is image based we'll add this + treeNodes[i].cssClass = standardCssClass + " icon-custom-file"; + } + } + }, + /** * @ngdoc method * @name umbraco.services.treeService#getMenuItemByAlias @@ -147,12 +156,12 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc /** Gets the root node of the current tree type for a given tree node */ getTreeRoot: function(treeNode) { - //all root nodes have metadata key 'treeType' + //all root nodes have metadata key 'treeAlias' var root = null; var current = treeNode; while (root === null && current !== undefined) { - if (current.metaData && current.metaData["treeType"]) { + if (current.metaData && current.metaData["treeAlias"]) { root = current; } else { @@ -162,6 +171,15 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc return root; }, + /** Gets the node's tree alias, this is done by looking up the meta-data of the current node's root node */ + getTreeAlias : function(treeNode) { + var root = this.getTreeRoot(treeNode); + if (root) { + return root.metaData["treeAlias"]; + } + return ""; + }, + getTree: function (args) { if (args === undefined) { @@ -176,7 +194,9 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc if (treeArray[cacheKey] !== undefined){ return treeArray[cacheKey]; } - + + var self = this; + return treeResource.loadApplication(args) .then(function(data) { //this will be called once the tree app data has loaded @@ -186,7 +206,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc root: data }; //we need to format/modify some of the node data to be used in our app. - formatNodeDataForUseInUI(result.root, result.root.children, section); + self._formatNodeDataForUseInUI(result.root, result.root.children, section); //cache this result //TODO: We'll need to un-cache this in many circumstances treeArray[cacheKey] = result; @@ -266,11 +286,13 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc if(section === "content"){ action = "create"; } - + + var self = this; + return treeResource.loadNodes({ section: section, node: treeItem }) - .then(function(data) { + .then(function (data) { //now that we have the data, we need to add the level property to each item and the view - formatNodeDataForUseInUI(treeItem, data, section, treeItem.level + 1); + self._formatNodeDataForUseInUI(treeItem, data, section, treeItem.level + 1); return data; }); } diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 3ff78c119b..deb972df24 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -25,15 +25,24 @@ app.config(function ($routeProvider) { if (!rp.method) return "views/common/dashboard.html"; + //NOTE: This current isn't utilized by anything but does open up some cool opportunities for + // us since we'll be able to have specialized views for individual sections which is something + // we've never had before. So could utilize this for a new dashboard model when we get native + // angular dashboards working. Perhaps a normal section dashboard would list out the registered + // dashboards (as tabs if we wanted) and each tab could actually be a route link to one of these views? + return 'views/' + rp.section + '/' + rp.method + '.html'; } }) - .when('/:section/:method/:id', { + .when('/:section/:tree/:method/:id', { templateUrl: function (rp) { - if (!rp.method) + if (!rp.tree || !rp.method) { return "views/common/dashboard.html"; - - return 'views/' + rp.section + '/' + rp.method + '.html'; + } + + //we don't need to put views into section folders since theoretically trees + // could be moved among sections, we only need folders for specific trees. + return 'views/' + rp.tree + '/' + rp.method + '.html'; } }) .otherwise({ redirectTo: '/content' }); 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 184522825b..4d87a24f56 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 @@ -1,19 +1,6 @@ describe('tree service tests', function () { var treeService; - function ensureParentLevelAndView(parentNode, treeNodes, section, level) { - //if no level is set, then we make it 1 - var childLevel = (level ? level : 1); - for (var i = 0; i < treeNodes.length; i++) { - treeNodes[i].level = childLevel; - //if there is not route path specified, then set it automatically - if (!treeNodes[i].routePath) { - treeNodes[i].routePath = section + "/edit/" + treeNodes[i].id; - } - treeNodes[i].parent = parentNode; - } - } - function getContentTree() { var url = "/umbraco/UmbracoTrees/ApplicationTreeApi/GetChildren?treeType=content&id=1234&level=1"; @@ -40,11 +27,11 @@ describe('tree service tests', function () { hasChildren: true, level: 0, menuUrl: menuUrl, - metaData: { treeType: "Umbraco.Web.Trees.ContentTreeController" } + metaData: { treeAlias: "content" } }; - ensureParentLevelAndView(t, t.children, "content", 0); - ensureParentLevelAndView(t.children[0], t.children[0].children, "content", 1); + treeService._formatNodeDataForUseInUI(t, t.children, "content", 0); + treeService._formatNodeDataForUseInUI(t.children[0], t.children[0].children, "content", 1); return t; } diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 5e21d694f3..4d26e3dd36 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -66,7 +66,7 @@ namespace Umbraco.Web.Trees ui.GetText("general", "recycleBin"), "icon-trash", RecycleBinSmells, - queryStrings.GetValue("application") + "/recyclebin")); + queryStrings.GetValue("application") + TreeAlias.EnsureStartsWith('/') + "/recyclebin")); return nodes; } diff --git a/src/Umbraco.Web/Trees/TreeApiController.cs b/src/Umbraco.Web/Trees/TreeApiController.cs index 4370e33028..e7adaadd48 100644 --- a/src/Umbraco.Web/Trees/TreeApiController.cs +++ b/src/Umbraco.Web/Trees/TreeApiController.cs @@ -72,6 +72,14 @@ namespace Umbraco.Web.Trees get { return _attribute.Title; } } + /// + /// Gets the current tree alias from the attribute assigned to it. + /// + public string TreeAlias + { + get { return _attribute.Alias; } + } + /// /// Returns the root node for the tree /// @@ -83,8 +91,8 @@ namespace Umbraco.Web.Trees if (queryStrings == null) queryStrings = new FormDataCollection(""); var node = CreateRootNode(queryStrings); - //add the tree type to the root - node.AdditionalData.Add("treeType", GetType().FullName); + //add the tree alias to the root + node.AdditionalData.Add("treeAlias", TreeAlias); AddQueryStringsToAdditionalData(node, queryStrings); @@ -188,9 +196,7 @@ namespace Umbraco.Web.Trees /// protected void AddQueryStringsToAdditionalData(TreeNode node, FormDataCollection queryStrings) { - // Add additional data, ensure treeId isn't added as we've already done that - foreach (var q in queryStrings - .Where(x => x.Key != "treeId" && node.AdditionalData.ContainsKey(x.Key) == false)) + foreach (var q in queryStrings.Where(x => node.AdditionalData.ContainsKey(x.Key) == false)) { node.AdditionalData.Add(q.Key, q.Value); }