diff --git a/src/Umbraco.Web.UI.Client/common/resources/tree.resource.js b/src/Umbraco.Web.UI.Client/common/resources/tree.resource.js index 672f052666..1172ae9df9 100644 --- a/src/Umbraco.Web.UI.Client/common/resources/tree.resource.js +++ b/src/Umbraco.Web.UI.Client/common/resources/tree.resource.js @@ -8,13 +8,21 @@ define(['angular'], function(angular) { **/ function umbTreeResource($q, $http) { - //internal method to get the tree app url + /** internal method to get the tree app url */ function getTreeAppUrl(section) { return Umbraco.Sys.ServerVariables.treeApplicationApiBaseUrl + "GetApplicationTrees?application=" + section; } + /** internal method to get the tree node's children url */ + function getTreeNodesUrl(node) { + if (!node.childNodesUrl) + throw "No childNodesUrl property found on the tree node, cannot load child nodes"; + return node.childNodesUrl; + } //the factory object returned return { + + /** Loads in the data to display the nodes for an application */ loadApplication: function (section) { var deferred = $q.defer(); @@ -29,6 +37,23 @@ define(['angular'], function(angular) { }); return deferred.promise; + }, + /** Loads in the data to display the child nodes for a given node */ + loadNodes: function(section, node) { + + var deferred = $q.defer(); + + //go and get the tree data + $http.get(getTreeNodesUrl(node)). + success(function (data, status, headers, config) { + deferred.resolve(data); + }). + error(function (data, status, headers, config) { + deferred.reject('Failed to retreive data for child nodes ' + node.nodeId); + }); + + return deferred.promise; + } }; } diff --git a/src/Umbraco.Web.UI/umbraco/js/umbraco.controllers.js b/src/Umbraco.Web.UI/umbraco/js/umbraco.controllers.js index c7a56ac65c..b77424fade 100644 --- a/src/Umbraco.Web.UI/umbraco/js/umbraco.controllers.js +++ b/src/Umbraco.Web.UI/umbraco/js/umbraco.controllers.js @@ -106,9 +106,16 @@ define(['angular'], function (angular) { if (node.expanded) { node.expanded = false; node.children = []; - } else { - node.children = tree.getChildren(node, $scope.currentSection); - node.expanded = true; + } + else { + tree.getChildren(node, $scope.currentSection) + .then(function (data) { + node.children = data; + node.expanded = true; + }, function (reason) { + alert(reason); + return; + }); } }; diff --git a/src/Umbraco.Web.UI/umbraco/js/umbraco.resources.js b/src/Umbraco.Web.UI/umbraco/js/umbraco.resources.js index 682ce8695e..83aa11617b 100644 --- a/src/Umbraco.Web.UI/umbraco/js/umbraco.resources.js +++ b/src/Umbraco.Web.UI/umbraco/js/umbraco.resources.js @@ -12,13 +12,20 @@ define(['angular'], function (angular) { **/ function umbTreeResource($q, $http) { - //internal method to get the tree app url + /** internal method to get the tree app url */ function getTreeAppUrl(section) { return Umbraco.Sys.ServerVariables.treeApplicationApiBaseUrl + "GetApplicationTrees?application=" + section; } + /** internal method to get the tree node's children url */ + function getTreeNodesUrl(node) { + if (!node.childNodesUrl) + throw "No childNodesUrl property found on the tree node, cannot load child nodes"; + return node.childNodesUrl; + } //the factory object returned return { + /** Loads in the data to display the nodes for an application */ loadApplication: function (section) { var deferred = $q.defer(); @@ -33,6 +40,23 @@ define(['angular'], function (angular) { }); return deferred.promise; + }, + /** Loads in the data to display the child nodes for a given node */ + loadNodes: function (section, node) { + + var deferred = $q.defer(); + + //go and get the tree data + $http.get(getTreeNodesUrl(node)). + success(function (data, status, headers, config) { + deferred.resolve(data); + }). + error(function (data, status, headers, config) { + deferred.reject('Failed to retreive data for child nodes ' + node.nodeId); + }); + + return deferred.promise; + } }; } diff --git a/src/Umbraco.Web.UI/umbraco/js/umbraco.services.js b/src/Umbraco.Web.UI/umbraco/js/umbraco.services.js index 610355dacc..120e224d76 100644 --- a/src/Umbraco.Web.UI/umbraco/js/umbraco.services.js +++ b/src/Umbraco.Web.UI/umbraco/js/umbraco.services.js @@ -290,68 +290,68 @@ angular.module('umbraco.services.tree', ["umbraco.resources.trees"]) //NOTE: The below will never be hit, it is legacy code from the mock data services - var t; - switch(section){ + //var t; + //switch(section){ - case "content": - t = { - name: section, - alias: section, + // case "content": + // t = { + // name: section, + // alias: section, - nodes: [ - { name: "My website", id: 1234, icon: "icon-home", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1, defaultAction: "create" }, - { name: "Components", id: 1235, icon: "icon-cogs", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1, defaultAction: "create" }, - { name: "Archieve", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1, defaultAction: "create" }, - { name: "Recycle Bin", id: 1237, icon: "icon-trash", view: section + "/trash/view/", children: [], expanded: false, level: 1, defaultAction: "create" } - ] - }; - break; + // nodes: [ + // { name: "My website", id: 1234, icon: "icon-home", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1, defaultAction: "create" }, + // { name: "Components", id: 1235, icon: "icon-cogs", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1, defaultAction: "create" }, + // { name: "Archieve", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1, defaultAction: "create" }, + // { name: "Recycle Bin", id: 1237, icon: "icon-trash", view: section + "/trash/view/", children: [], expanded: false, level: 1, defaultAction: "create" } + // ] + // }; + // break; - case "developer": - t = { - name: section, - alias: section, + // case "developer": + // t = { + // name: section, + // alias: section, - nodes: [ - { name: "Data types", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, - { name: "Macros", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, - { name: "Pacakges", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, - { name: "XSLT Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 }, - { name: "Razor Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } - ] - }; - break; - case "settings": - t = { - name: section, - alias: section, + // nodes: [ + // { name: "Data types", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, + // { name: "Macros", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, + // { name: "Pacakges", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, + // { name: "XSLT Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 }, + // { name: "Razor Files", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } + // ] + // }; + // break; + // case "settings": + // t = { + // name: section, + // alias: section, - nodes: [ - { name: "Stylesheets", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, - { name: "Templates", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, - { name: "Dictionary", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, - { name: "Media types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 }, - { name: "Document types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } - ] - }; - break; - default: - t = { - name: section, - alias: section, + // nodes: [ + // { name: "Stylesheets", id: 1234, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, + // { name: "Templates", id: 1235, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, + // { name: "Dictionary", id: 1236, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, + // { name: "Media types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 }, + // { name: "Document types", id: 1237, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } + // ] + // }; + // break; + // default: + // t = { + // name: section, + // alias: section, - nodes: [ - { name: "random-name-" + section, id: 1234, icon: "icon-home", defaultAction: "create", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, - { name: "random-name-" + section, id: 1235, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, - { name: "random-name-" + section, id: 1236, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, - { name: "random-name-" + section, id: 1237, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } - ] - }; - break; - } + // nodes: [ + // { name: "random-name-" + section, id: 1234, icon: "icon-home", defaultAction: "create", view: section + "/edit/" + 1234, children: [], expanded: false, level: 1 }, + // { name: "random-name-" + section, id: 1235, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1235, children: [], expanded: false, level: 1 }, + // { name: "random-name-" + section, id: 1236, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1236, children: [], expanded: false, level: 1 }, + // { name: "random-name-" + section, id: 1237, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1237, children: [], expanded: false, level: 1 } + // ] + // }; + // break; + //} - treeArray[section] = t; - return treeArray[section]; + //treeArray[section] = t; + //return treeArray[section]; }, getActions: function(treeItem, section){ @@ -401,20 +401,42 @@ angular.module('umbraco.services.tree', ["umbraco.resources.trees"]) }, getChildren: function (treeItem, section) { - var iLevel = treeItem.level + 1; - //hack to have create as default content action - var action; - if(section === "content"){ - action = "create"; - } + if (!treeItem) { + throw "No treeItem defined"; + } + if (!section) { + throw "No section defined"; + } - return [ - { name: "child-of-" + treeItem.name, id: iLevel + "" + 1234, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1234, children: [], expanded: false, level: iLevel, defaultAction: action }, - { name: "random-name-" + section, id: iLevel + "" + 1235, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1235, children: [], expanded: false, level: iLevel, defaultAction: action }, - { name: "random-name-" + section, id: iLevel + "" + 1236, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1236, children: [], expanded: false, level: iLevel, defaultAction: action }, - { name: "random-name-" + section, id: iLevel + "" + 1237, icon: "icon-file-alt", view: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, level: iLevel, defaultAction: action } - ]; + var deferred = $q.defer(); + + umbTreeResource.loadNodes(section, treeItem) + .then(function (data) { + deferred.resolve(data); + }, function (reason) { + //bubble up the rejection + deferred.reject(reason); + return; + }); + + return deferred.promise; + + //NOTE: The below will never get hit it is legacy mock data + //var iLevel = treeItem.level + 1; + + ////hack to have create as default content action + //var action; + //if(section === "content"){ + // action = "create"; + //} + + //return [ + // { name: "child-of-" + treeItem.name, id: iLevel + "" + 1234, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1234, children: [], expanded: false, level: iLevel, defaultAction: action }, + // { name: "random-name-" + section, id: iLevel + "" + 1235, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1235, children: [], expanded: false, level: iLevel, defaultAction: action }, + // { name: "random-name-" + section, id: iLevel + "" + 1236, icon: "icon-file-alt", view: section + "/edit/" + iLevel + "" + 1236, children: [], expanded: false, level: iLevel, defaultAction: action }, + // { name: "random-name-" + section, id: iLevel + "" + 1237, icon: "icon-file-alt", view: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, level: iLevel, defaultAction: action } + //]; } }; }); diff --git a/src/Umbraco.Web/Trees/ApplicationTreeApiController.cs b/src/Umbraco.Web/Trees/ApplicationTreeApiController.cs index 41c7742bcb..16b33c86dd 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeApiController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeApiController.cs @@ -88,12 +88,12 @@ namespace Umbraco.Web.Trees private TreeNodeCollection GetNodeCollection(ApplicationTree configTree, string id, FormDataCollection queryStrings) { if (configTree == null) throw new ArgumentNullException("configTree"); - var byControllerAttempt = TryLoadFromControllerTree(configTree, id, queryStrings); + var byControllerAttempt = configTree.TryLoadFromControllerTree(id, queryStrings, ControllerContext, Request); if (byControllerAttempt.Success) { return byControllerAttempt.Result; } - var legacyAttempt = TryLoadFromLegacyTree(configTree, id, queryStrings); + var legacyAttempt = configTree.TryLoadFromLegacyTree(id, queryStrings, Url); if (legacyAttempt.Success) { return legacyAttempt.Result; @@ -101,58 +101,7 @@ namespace Umbraco.Web.Trees throw new ApplicationException("Could not render a tree for type " + configTree.Alias); } - - private Attempt TryLoadFromControllerTree(ApplicationTree appTree, string id, FormDataCollection formCollection) - { - //get reference to all TreeApiControllers - var controllerTrees = UmbracoApiControllerResolver.Current.RegisteredUmbracoApiControllers - .Where(TypeHelper.IsTypeAssignableFrom) - .ToArray(); - - //find the one we're looking for - var foundControllerTree = controllerTrees.FirstOrDefault(x => x.GetFullNameWithAssembly() == appTree.Type); - if (foundControllerTree == null) - { - return new Attempt(new InstanceNotFoundException("Could not find tree of type " + appTree.Type + " in any loaded DLLs")); - } - - //instantiate it, since we are proxying, we need to setup the instance with our current context - var instance = (TreeApiController)DependencyResolver.Current.GetService(foundControllerTree); - instance.ControllerContext = ControllerContext; - instance.Request = Request; - //return it's data - return new Attempt(true, instance.GetNodes(id, formCollection)); - } - - private Attempt TryLoadFromLegacyTree(ApplicationTree appTree, string id, FormDataCollection formCollection) - { - //This is how the legacy trees worked.... - var treeDef = TreeDefinitionCollection.Instance.FindTree(appTree.Alias); - if (treeDef == null) - { - return new Attempt(new InstanceNotFoundException("Could not find tree of type " + appTree.Alias)); - } - - var bTree = treeDef.CreateInstance(); - var treeParams = new TreeParams(); - - //we currently only support an integer id or a string id, we'll refactor how this works - //later but we'll get this working first - int startId; - if (int.TryParse(id, out startId)) - { - treeParams.StartNodeID = startId; - } - else - { - treeParams.NodeKey = id; - } - var xTree = new XmlTree(); - bTree.SetTreeParameters(treeParams); - bTree.Render(ref xTree); - - return new Attempt(true, LegacyTreeDataAdapter.ConvertFromLegacy(xTree)); - } + //Temporary, but necessary until we refactor trees in general internal class TreeParams : ITreeService diff --git a/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs new file mode 100644 index 0000000000..36a9aa0b69 --- /dev/null +++ b/src/Umbraco.Web/Trees/ApplicationTreeExtensions.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Instrumentation; +using System.Net.Http; +using System.Net.Http.Formatting; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http.Controllers; +using System.Web.Mvc; +using Umbraco.Core; +using Umbraco.Web.WebApi; +using umbraco.BusinessLogic; +using umbraco.cms.presentation.Trees; +using UrlHelper = System.Web.Http.Routing.UrlHelper; + +namespace Umbraco.Web.Trees +{ + internal static class ApplicationTreeExtensions + { + + internal static Attempt TryLoadFromControllerTree(this ApplicationTree appTree, string id, FormDataCollection formCollection, HttpControllerContext controllerContext, HttpRequestMessage request) + { + //get reference to all TreeApiControllers + var controllerTrees = UmbracoApiControllerResolver.Current.RegisteredUmbracoApiControllers + .Where(TypeHelper.IsTypeAssignableFrom) + .ToArray(); + + //find the one we're looking for + var foundControllerTree = controllerTrees.FirstOrDefault(x => x.GetFullNameWithAssembly() == appTree.Type); + if (foundControllerTree == null) + { + return new Attempt(new InstanceNotFoundException("Could not find tree of type " + appTree.Type + " in any loaded DLLs")); + } + + //instantiate it, since we are proxying, we need to setup the instance with our current context + var instance = (TreeApiController)DependencyResolver.Current.GetService(foundControllerTree); + instance.ControllerContext = controllerContext; + instance.Request = request; + //return it's data + return new Attempt(true, instance.GetNodes(id, formCollection)); + } + + internal static Attempt TryLoadFromLegacyTree(this ApplicationTree appTree, string id, FormDataCollection formCollection, UrlHelper urlHelper) + { + //This is how the legacy trees worked.... + var treeDef = TreeDefinitionCollection.Instance.FindTree(appTree.Alias); + if (treeDef == null) + { + return new Attempt(new InstanceNotFoundException("Could not find tree of type " + appTree.Alias)); + } + + var bTree = treeDef.CreateInstance(); + var treeParams = new ApplicationTreeApiController.TreeParams(); + + //we currently only support an integer id or a string id, we'll refactor how this works + //later but we'll get this working first + int startId; + if (int.TryParse(id, out startId)) + { + treeParams.StartNodeID = startId; + } + else + { + treeParams.NodeKey = id; + } + var xTree = new XmlTree(); + bTree.SetTreeParameters(treeParams); + bTree.Render(ref xTree); + + return new Attempt(true, LegacyTreeDataAdapter.ConvertFromLegacy(xTree, urlHelper)); + } + + } +} diff --git a/src/Umbraco.Web/Trees/LegacyTreeApiController.cs b/src/Umbraco.Web/Trees/LegacyTreeApiController.cs new file mode 100644 index 0000000000..4d5b2a4c14 --- /dev/null +++ b/src/Umbraco.Web/Trees/LegacyTreeApiController.cs @@ -0,0 +1,47 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Net.Http.Formatting; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; +using umbraco.BusinessLogic; +using umbraco.businesslogic; + +namespace Umbraco.Web.Trees +{ + //NOTE: We will of course have to authorized this but changing the base class once integrated + + /// + /// This is used to output JSON from legacy trees + /// + [PluginController("UmbracoTrees")] + public class LegacyTreeApiController : UmbracoApiController //UmbracoAuthorizedApiController + { + /// + /// Convert a legacy tree to a new tree result + /// + /// + /// + /// + [HttpQueryStringFilter("queryStrings")] + public TreeNodeCollection GetNodes(string id, FormDataCollection queryStrings) + { + //need to ensure we have a tree type + var treeType = queryStrings.GetRequiredString("treeType"); + //now we'll look up that tree + var tree = ApplicationTree.getByAlias(treeType); + if (tree == null) + throw new InvalidOperationException("No tree found with alias " + treeType); + + var attempt = tree.TryLoadFromLegacyTree(id, queryStrings, Url); + if (attempt.Success == false) + { + throw new ApplicationException("Could not render tree " + treeType + " for node id " + id); + } + + return attempt.Result; + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataAdapter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataAdapter.cs index da34634d93..7b70d52a45 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataAdapter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataAdapter.cs @@ -1,4 +1,7 @@ -using umbraco.cms.presentation.Trees; +using System; +using System.Web.Http.Routing; +using Umbraco.Core; +using umbraco.cms.presentation.Trees; namespace Umbraco.Web.Trees { @@ -8,7 +11,7 @@ namespace Umbraco.Web.Trees internal class LegacyTreeDataAdapter { - internal static TreeNodeCollection ConvertFromLegacy(XmlTree xmlTree) + internal static TreeNodeCollection ConvertFromLegacy(XmlTree xmlTree, UrlHelper urlHelper) { //TODO: Once we get the editor URL stuff working we'll need to figure out how to convert // that over to use the old school ui.xml stuff for these old trees and however the old menu items worked. @@ -16,7 +19,15 @@ namespace Umbraco.Web.Trees var collection = new TreeNodeCollection(); foreach (var x in xmlTree.treeCollection) { - var node = new TreeNode(x.NodeID, x.Source) + // /umbraco/tree.aspx?rnd=d0d0ff11a1c347dabfaa0fc75effcc2a&id=1046&treeType=content&contextMenu=false&isDialog=false + + //we need to convert the node source to our legacy tree controller + var source = urlHelper.GetUmbracoApiService("GetNodes"); + //append the query strings + var query = x.Source.Split(new[] {'?'}, StringSplitOptions.RemoveEmptyEntries); + source += query.Length > 1 ? query[1].EnsureStartsWith('?') : ""; + + var node = new TreeNode(x.NodeID, source) { HasChildren = x.HasChildren, Icon = x.Icon, diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 919337c88f..e41f527696 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -295,10 +295,12 @@ + + @@ -1870,7 +1872,9 @@ ASPXCodeBehind - + + ASPXCodeBehind +