Updated trees to get children nodes from the server... nearly working and also works with proxying

to legacy trees. In fact there are not new trees created yet, just focusing on adapting for legacy trees
which so far seems to be good.
This commit is contained in:
Shannon Deminick
2013-05-31 17:20:56 -10:00
parent 4a57dcfd01
commit f3fbae7173
9 changed files with 294 additions and 130 deletions
@@ -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;
}
};
}
@@ -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;
});
}
};
@@ -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;
}
};
}
@@ -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 }
//];
}
};
});
@@ -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<TreeNodeCollection> TryLoadFromControllerTree(ApplicationTree appTree, string id, FormDataCollection formCollection)
{
//get reference to all TreeApiControllers
var controllerTrees = UmbracoApiControllerResolver.Current.RegisteredUmbracoApiControllers
.Where(TypeHelper.IsTypeAssignableFrom<TreeApiController>)
.ToArray();
//find the one we're looking for
var foundControllerTree = controllerTrees.FirstOrDefault(x => x.GetFullNameWithAssembly() == appTree.Type);
if (foundControllerTree == null)
{
return new Attempt<TreeNodeCollection>(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<TreeNodeCollection>(true, instance.GetNodes(id, formCollection));
}
private Attempt<TreeNodeCollection> 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<TreeNodeCollection>(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<TreeNodeCollection>(true, LegacyTreeDataAdapter.ConvertFromLegacy(xTree));
}
//Temporary, but necessary until we refactor trees in general
internal class TreeParams : ITreeService
@@ -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<TreeNodeCollection> 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<TreeApiController>)
.ToArray();
//find the one we're looking for
var foundControllerTree = controllerTrees.FirstOrDefault(x => x.GetFullNameWithAssembly() == appTree.Type);
if (foundControllerTree == null)
{
return new Attempt<TreeNodeCollection>(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<TreeNodeCollection>(true, instance.GetNodes(id, formCollection));
}
internal static Attempt<TreeNodeCollection> 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<TreeNodeCollection>(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<TreeNodeCollection>(true, LegacyTreeDataAdapter.ConvertFromLegacy(xTree, urlHelper));
}
}
}
@@ -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
/// <summary>
/// This is used to output JSON from legacy trees
/// </summary>
[PluginController("UmbracoTrees")]
public class LegacyTreeApiController : UmbracoApiController //UmbracoAuthorizedApiController
{
/// <summary>
/// Convert a legacy tree to a new tree result
/// </summary>
/// <param name="id"></param>
/// <param name="queryStrings"></param>
/// <returns></returns>
[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;
}
}
}
+14 -3
View File
@@ -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<LegacyTreeApiController>("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,
+5 -1
View File
@@ -295,10 +295,12 @@
<Compile Include="Models\ContentEditing\Tab.cs" />
<Compile Include="Models\Mapping\ContentModelMapper.cs" />
<Compile Include="FormDataCollectionExtensions.cs" />
<Compile Include="Trees\LegacyTreeApiController.cs" />
<Compile Include="Trees\ISearchableTree.cs" />
<Compile Include="Trees\LegacyTreeDataAdapter.cs" />
<Compile Include="Trees\SearchResultItem.cs" />
<Compile Include="Trees\TreeApiController.cs" />
<Compile Include="Trees\ApplicationTreeExtensions.cs" />
<Compile Include="Trees\TreeNode.cs" />
<Compile Include="Trees\TreeNodeCollection.cs" />
<Compile Include="Trees\TreeQueryStringParameters.cs" />
@@ -1870,7 +1872,9 @@
<Content Include="umbraco.presentation\umbraco\controls\Images\UploadMediaImage.ascx">
<SubType>ASPXCodeBehind</SubType>
</Content>
<Content Include="umbraco.presentation\umbraco\controls\Tree\TreeControl.ascx" />
<Content Include="umbraco.presentation\umbraco\controls\Tree\TreeControl.ascx">
<SubType>ASPXCodeBehind</SubType>
</Content>
<Content Include="umbraco.presentation\umbraco\actions\delete.aspx" />
<Content Include="umbraco.presentation\umbraco\actions\editContent.aspx" />
<Content Include="umbraco.presentation\umbraco\actions\preview.aspx" />