Got a solution for legacy dialog trees with the new format trees, just committing all of the work in case i need to come back and reference some stuff I'm about to remove.

This commit is contained in:
Shannon
2013-09-04 17:36:05 +10:00
parent db79d87c08
commit be9c3b8996
9 changed files with 371 additions and 137 deletions

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<trees>
<!--Content-->
<add initialize="true" sortOrder="0" alias="content" application="content" title="Content" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.ContentTreeController, umbraco" action="" />
<add initialize="true" sortOrder="0" alias="content" application="content" title="Content" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.ContentTreeController, umbraco" />
<add initialize="false" sortOrder="0" alias="contentRecycleBin" application="content" title="Recycle Bin" iconClosed="folder.gif" iconOpen="folder_o.gif" type="umbraco.cms.presentation.Trees.ContentRecycleBin, umbraco" action="" />
<!--Media-->
<add initialize="true" sortOrder="0" alias="media" application="media" title="Media" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MediaTreeController, umbraco" />
@@ -38,5 +38,5 @@
<add silent="false" initialize="true" sortOrder="2" alias="yourTasks" application="translation" title="Tasks created by you" iconClosed=".sprTreeFolder" iconOpen=".sprTreeFolder_o" type="umbraco.loadYourTasks, umbraco" action="" />
<!-- Custom -->
<!--<add application="myApplication" alias="myTree" title="Me Tree" type="MyNamespace.myTree, MyAssembly"
iconClosed=".sprTreeFolder" iconOpen=".sprTreeFolder_o" sortOrder="10" />-->
iconClosed=".sprTreeFolder" iconOpen=".sprTreeFolder_o" sortOrder="10" />-->
</trees>

View File

@@ -19,12 +19,15 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Trees
{
[LegacyBaseTree(typeof(loadContent))]
[Tree(Constants.Applications.Content, Constants.Trees.Content, "Content")]
[PluginController("UmbracoTrees")]
public class ContentTreeController : ContentTreeControllerBase
{
protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
{
TreeNode node;
//if the user's start node is not default, then return their start node as the root node.
if (UmbracoUser.StartNodeId != Constants.System.Root)
{
@@ -35,20 +38,27 @@ namespace Umbraco.Web.Trees
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var node = new TreeNode(
node = new TreeNode(
userRoot.Id.ToInvariantString(),
"", //root nodes aren't expandable, no need to lookup the child nodes url
Url.GetMenuUrl(GetType(), userRoot.Id.ToInvariantString(), queryStrings))
{
HasChildren = true,
RoutePath = currApp,
Title = userRoot.Name
};
{
HasChildren = true,
RoutePath = currApp,
Title = userRoot.Name
};
return node;
}
else
{
node = base.CreateRootNode(queryStrings);
}
return base.CreateRootNode(queryStrings);
//InjectLegacyTreeCompatibility(node, queryStrings);
return node;
}
protected override int RecycleBinId
@@ -74,13 +84,16 @@ namespace Umbraco.Web.Trees
var allowedUserOptions = GetUserMenuItemsForNode(e);
if (CanUserAccessNode(e, allowedUserOptions))
{
nodes.Add(
CreateTreeNode(
var node = CreateTreeNode(
e.Id.ToInvariantString(),
queryStrings,
e.Name,
e.ContentTypeIcon,
e.HasChildren));
e.HasChildren);
//InjectLegacyTreeCompatibility(node, queryStrings);
nodes.Add(node);
}
}
return nodes;
@@ -164,6 +177,23 @@ namespace Umbraco.Web.Trees
return menu;
}
///// <summary>
///// This is required so that the legacy tree dialog pickers and the legacy TreeControl.ascx stuff works with these new trees.
///// </summary>
///// <param name="node"></param>
///// <param name="queryStrings"></param>
///// <remarks>
///// NOTE: That if developers create brand new trees using webapi controllers then they'll need to manually make it
///// compatible with the legacy tree pickers, 99.9% of the time devs won't be doing that and once we make the new tree
///// pickers and devs update their apps to be angularized it won't matter
///// </remarks>
//private void InjectLegacyTreeCompatibility(TreeNode node, FormDataCollection queryStrings)
//{
// if (IsDialog(queryStrings))
// {
// node.AdditionalData["legacyDialogAction"] = "javascript:openContent(" + node.NodeId + ");";
// }
//}
}
}

View File

@@ -58,15 +58,20 @@ namespace Umbraco.Web.Trees
{
if (id == Constants.System.Root.ToInvariantString())
{
//we need to append the recycle bin to the end
//we need to append the recycle bin to the end (if not in dialog mode)
var nodes = PerformGetTreeNodes(id, queryStrings);
nodes.Add(CreateTreeNode(
Constants.System.RecycleBinContent.ToInvariantString(),
queryStrings,
ui.GetText("general", "recycleBin"),
"icon-trash",
RecycleBinSmells,
queryStrings.GetValue<string>("application") + TreeAlias.EnsureStartsWith('/') + "/recyclebin"));
if (!IsDialog(queryStrings))
{
nodes.Add(CreateTreeNode(
Constants.System.RecycleBinContent.ToInvariantString(),
queryStrings,
ui.GetText("general", "recycleBin"),
"icon-trash",
RecycleBinSmells,
queryStrings.GetValue<string>("application") + TreeAlias.EnsureStartsWith('/') + "/recyclebin"));
}
return nodes;
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Text;
using System.Web;
using System.Web.Http.Routing;
using Umbraco.Core;
@@ -15,12 +17,97 @@ using umbraco.interfaces;
namespace Umbraco.Web.Trees
{
[AttributeUsage(AttributeTargets.Class)]
internal sealed class LegacyBaseTreeAttribute : Attribute
{
public Type BaseTreeType { get; private set; }
public LegacyBaseTreeAttribute(Type baseTreeType)
{
if (!TypeHelper.IsTypeAssignableFrom<BaseTree>(baseTreeType))
{
throw new InvalidOperationException("The type for baseTreeType must be assignable from " + typeof(BaseTree));
}
BaseTreeType = baseTreeType;
}
}
internal class LegacyBaseTreeWrapper : BaseTree
{
private readonly string _treeAlias;
private readonly TreeNodeCollection _children;
private readonly TreeNode _root;
public LegacyBaseTreeWrapper(string treeAlias, string application, TreeNode root, TreeNodeCollection children = null)
: base(application)
{
_treeAlias = treeAlias;
_root = root;
_children = children;
}
public override void RenderJS(ref StringBuilder javascript)
{
}
public override void Render(ref XmlTree tree)
{
foreach (var c in _children)
{
var node = XmlTreeNode.Create(this);
LegacyTreeDataConverter.ConvertToLegacyNode(node, c, _treeAlias);
node.Source = IsDialog == false ? GetTreeServiceUrl(int.Parse(node.NodeID)) : GetTreeDialogUrl(int.Parse(node.NodeID));
tree.Add(node);
}
}
protected override void CreateRootNode(ref XmlTreeNode rootNode)
{
rootNode.NodeID = _root.NodeId;
rootNode.Icon = _root.IconIsClass ? _root.Icon.EnsureStartsWith('.') : _root.IconFilePath;
rootNode.HasChildren = _root.HasChildren;
rootNode.NodeID = _root.NodeId;
rootNode.Text = _root.Title;
rootNode.NodeType = _root.NodeType;
rootNode.OpenIcon = _root.IconIsClass ? _root.Icon.EnsureStartsWith('.') : _root.IconFilePath;
}
public override string TreeAlias
{
get { return _treeAlias; }
}
}
/// <summary>
/// Converts the legacy tree data to the new format
/// </summary>
internal class LegacyTreeDataConverter
{
internal static FormDataCollection ConvertFromLegacyTreeParams(TreeRequestParams treeParams)
{
return new FormDataCollection(new Dictionary<string, string>
{
{TreeQueryStringParameters.Application, treeParams.Application},
{TreeQueryStringParameters.DialogMode, treeParams.IsDialog.ToString()},
});
}
internal static void ConvertToLegacyNode(XmlTreeNode legacy, TreeNode node, string treeType)
{
legacy.Action = node.AdditionalData.ContainsKey("legacyDialogAction") ? node.AdditionalData["legacyDialogAction"].ToString() : "";
legacy.HasChildren = node.HasChildren;
legacy.Icon = node.IconIsClass ? node.Icon.EnsureStartsWith('.') : node.IconFilePath;
legacy.NodeID = node.NodeId;
legacy.NodeType = node.NodeType;
legacy.OpenIcon = node.IconIsClass ? node.Icon.EnsureStartsWith('.') : node.IconFilePath;
legacy.Text = node.Title;
legacy.TreeType = treeType;
}
/// <summary>
/// Gets the menu item collection from a legacy tree node based on it's parent node's child collection
/// </summary>

View File

@@ -102,9 +102,8 @@ namespace Umbraco.Web.Trees
node.AdditionalData.Add("searchable", "true");
}
//now update all data based on some of the query strings, like if we are running in dialog mode
var isDialog = queryStrings.GetValue<bool>(TreeQueryStringParameters.DialogMode);
if (isDialog)
//now update all data based on some of the query strings, like if we are running in dialog mode
if (IsDialog(queryStrings))
{
node.RoutePath = "#";
}
@@ -137,9 +136,8 @@ namespace Umbraco.Web.Trees
AddQueryStringsToAdditionalData(node, queryStrings);
}
//now update all data based on some of the query strings, like if we are running in dialog mode
var isDialog = queryStrings.GetValue<bool>(TreeQueryStringParameters.DialogMode);
if (isDialog)
//now update all data based on some of the query strings, like if we are running in dialog mode
if (IsDialog((queryStrings)))
{
foreach (var node in nodes)
{
@@ -288,17 +286,19 @@ namespace Umbraco.Web.Trees
#endregion
///// <summary>
///// The tree name based on the controller type so that everything is based on naming conventions
///// </summary>
//public string TreeType
//{
// get
// {
// var name = GetType().Name;
// return name.Substring(0, name.LastIndexOf("TreeController", StringComparison.Ordinal));
// }
//}
#region Query String parameter helpers
/// <summary>
/// If the request is for a dialog mode tree
/// </summary>
/// <param name="queryStrings"></param>
/// <returns></returns>
protected bool IsDialog(FormDataCollection queryStrings)
{
return queryStrings.GetValue<bool>(TreeQueryStringParameters.DialogMode);
}
#endregion
public static event EventHandler<TreeNodesRenderingEventArgs> TreeNodesRendering;

View File

@@ -84,6 +84,7 @@ namespace umbraco.cms.presentation.Trees
return m_treeAlias;
}
internal set { m_treeAlias = value; }
}
private string m_treeAlias;

View File

@@ -14,6 +14,7 @@ namespace umbraco
/// Handles loading the content tree into umbraco's application tree
/// </summary>
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
//[Tree(Constants.Applications.Content, "content", "Content", silent: true)]
public class loadContent : BaseContentTree
{

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Web;
@@ -10,8 +11,12 @@ using System.Web.Http.Routing;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Services;
using Umbraco.Core;
using Umbraco.Web;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebServices;
using umbraco.BusinessLogic;
using umbraco.businesslogic;
using umbraco.presentation.umbraco.controls;
using umbraco.cms.presentation.Trees;
using System.Web.Script.Services;
@@ -64,6 +69,9 @@ namespace umbraco.presentation.webservices
}
else
{
BaseTree tree = null;
var xTree = new XmlTree();
//first get the app tree definition so we can then figure out if we need to load by legacy or new
//now we'll look up that tree
var appTree = Services.ApplicationTreeService.GetByAlias(treeType);
@@ -73,34 +81,79 @@ namespace umbraco.presentation.webservices
var controllerAttempt = appTree.TryGetControllerTree();
if (controllerAttempt.Success)
{
var context = WebApiHelper.CreateContext(new HttpMethod("GET"), Context.Request.Url, new HttpContextWrapper(Context));
var rootAttempt = appTree.TryGetRootNodeFromControllerTree(
new FormDataCollection(new Dictionary<string, string> {{"app", app}}),
context);
if (rootAttempt.Success)
var legacyAtt = controllerAttempt.Result.GetCustomAttribute<LegacyBaseTreeAttribute>(false);
if (legacyAtt == null)
{
throw new InvalidOperationException("Cannot render a " + typeof (TreeApiController) + " tree type with the legacy web services unless attributed with " + typeof (LegacyBaseTreeAttribute));
}
var treeDef = new TreeDefinition(
legacyAtt.BaseTreeType,
new ApplicationTree(false, true, appTree.SortOrder, appTree.ApplicationAlias, appTree.Alias, appTree.Title, appTree.IconClosed, appTree.IconOpened, "", legacyAtt.BaseTreeType.GetFullNameWithAssembly(), ""),
new Application(treeType, treeType, "", 0));
tree = treeDef.CreateInstance();
tree.TreeAlias = appTree.Alias;
//var queryStrings = new FormDataCollection(new Dictionary<string, string>
// {
// {TreeQueryStringParameters.Application, app},
// {TreeQueryStringParameters.DialogMode, isDialog.ToString()}
// });
//var context = WebApiHelper.CreateContext(new HttpMethod("GET"), Context.Request.Url, new HttpContextWrapper(Context));
//var rootAttempt = appTree.TryGetRootNodeFromControllerTree(
// queryStrings,
// context);
//if (rootAttempt.Success)
//{
// tree = new LegacyBaseTreeWrapper(treeType, app, rootAttempt.Result);
//}
}
else
{
//get the tree that we need to render
var treeDef = TreeDefinitionCollection.Instance.FindTree(treeType);
//if (treeDef == null)
//{
// // Load all LEGACY Trees by attribute and add them to the XML config
// var legacyTreeTypes = PluginManager.Current.ResolveAttributedTrees();
// var found = legacyTreeTypes
// .Select(x => new { att = x.GetCustomAttribute<businesslogic.TreeAttribute>(false), type = x })
// .FirstOrDefault(x => x.att.Alias == treeType);
// if (found == null)
// {
// throw new InvalidOperationException("The " + GetType() + " service can only return data for legacy tree types");
// }
// treeDef = new TreeDefinition(
// found.type,
// new ApplicationTree(found.att.Silent, found.att.Initialize, (byte)found.att.SortOrder, found.att.ApplicationAlias, found.att.Alias, found.att.Title, found.att.IconClosed, found.att.IconOpen, "", found.type.GetFullNameWithAssembly(), found.att.Action),
// new Application(treeType, treeType, "", 0));
// tree = treeDef.CreateInstance();
//}
//else
//{
// tree = treeDef.CreateInstance();
//}
tree = treeDef.CreateInstance();
}
var legacyAttempt = appTree.TryGetLegacyTreeDef();
//get the tree that we need to render
var tree = TreeDefinitionCollection.Instance.FindTree(treeType).CreateInstance();
tree.ShowContextMenu = showContextMenu;
tree.IsDialog = isDialog;
tree.DialogMode = dialogMode;
tree.NodeKey = string.IsNullOrEmpty(nodeKey) ? "" : nodeKey;
tree.FunctionToCall = string.IsNullOrEmpty(functionToCall) ? "" : functionToCall;
//this would be nice to set, but no parameters :(
//tree.StartNodeID =
//tree.StartNodeID =
//now render it's start node
var xTree = new XmlTree();
xTree.Add(tree.RootNode);
returnVal.Add("json", xTree.ToString());
}

View File

@@ -1,115 +1,172 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Runtime.Remoting.Contexts;
using System.Web;
using System.Web.Services;
using Umbraco.Core;
using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebServices;
using umbraco.BusinessLogic;
using umbraco.cms.presentation;
using umbraco.cms.presentation.Trees;
using System.Threading;
namespace umbraco.presentation.webservices
{
/// <summary>
/// Summary description for TreeDataService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class TreeDataService : UmbracoAuthorizedHttpHandler
{
/// <summary>
/// Summary description for TreeDataService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class TreeDataService : UmbracoAuthorizedHttpHandler
{
public override void ProcessRequest(HttpContext context)
{
AuthorizeRequest(true);
context.Response.ContentType = "application/json";
context.Response.Write(GetXmlTree().ToString());
public override void ProcessRequest(HttpContext context)
{
AuthorizeRequest(true);
context.Response.ContentType = "application/json";
context.Response.Write(GetXmlTree(context).ToString());
}
}
public override bool IsReusable
{
get
{
return false;
}
}
public override bool IsReusable
{
get
{
return false;
}
}
[Obsolete("Use the base class AuthorizeRequest methods in UmbracoAuthorizedHttpHandler")]
public static void Authorize()
{
if (!BasePages.BasePage.ValidateUserContextID(BasePages.BasePage.umbracoUserContextID))
throw new Exception("Client authorization failed. User is not logged in");
public static void Authorize()
{
if (!BasePages.BasePage.ValidateUserContextID(BasePages.BasePage.umbracoUserContextID))
throw new Exception("Client authorization failed. User is not logged in");
}
}
/// <summary>
/// Returns the an XmlTree based on the current http request
/// </summary>
/// <returns></returns>
public XmlTree GetXmlTree()
{
var treeParams = TreeRequestParams.FromQueryStrings();
/// <summary>
/// Returns the an XmlTree based on the current http request
/// </summary>
/// <returns></returns>
public XmlTree GetXmlTree(HttpContext context)
{
var treeParams = TreeRequestParams.FromQueryStrings();
//validate the current user for the request app!
AuthorizeRequest(treeParams.Application, true);
if (string.IsNullOrEmpty(treeParams.TreeType))
if (!string.IsNullOrEmpty(treeParams.Application))
LoadAppTrees(treeParams);
else
return new XmlTree(); //returns an empty tree
else
LoadTree(treeParams);
if (string.IsNullOrEmpty(treeParams.TreeType))
if (!string.IsNullOrEmpty(treeParams.Application))
LoadAppTrees(treeParams, context);
else
return new XmlTree(); //returns an empty tree
else
LoadTree(treeParams, context);
return _xTree;
}
return _xTree;
}
private XmlTree _xTree = new XmlTree();
private XmlTree _xTree = new XmlTree();
/// <summary>
/// If the application supports multiple trees, then this function iterates over all of the trees assigned to it
/// and creates their top level nodes and context menus.
/// </summary>
/// <summary>
/// If the application supports multiple trees, then this function iterates over all of the trees assigned to it
/// and creates their top level nodes and context menus.
/// </summary>
/// <param name="treeParams"></param>
private void LoadAppTrees(TreeRequestParams treeParams)
{
//find all tree definitions that have the current application alias
List<TreeDefinition> treeDefs = TreeDefinitionCollection.Instance.FindActiveTrees(treeParams.Application);
/// <param name="context"></param>
private void LoadAppTrees(TreeRequestParams treeParams, HttpContext context)
{
//find all tree definitions that have the current application alias
List<TreeDefinition> treeDefs = TreeDefinitionCollection.Instance.FindActiveTrees(treeParams.Application);
foreach (TreeDefinition treeDef in treeDefs)
{
BaseTree bTree = treeDef.CreateInstance();
bTree.SetTreeParameters(treeParams);
_xTree.Add(bTree.RootNode);
}
}
foreach (TreeDefinition treeDef in treeDefs)
{
BaseTree bTree = treeDef.CreateInstance();
bTree.SetTreeParameters(treeParams);
_xTree.Add(bTree.RootNode);
}
}
/// <summary>
/// This will load the particular ITree object and call it's render method to get the nodes that need to be rendered.
/// </summary>
/// <summary>
/// This will load the particular ITree object and call it's render method to get the nodes that need to be rendered.
/// </summary>
/// <param name="treeParams"></param>
private void LoadTree(TreeRequestParams treeParams)
{
/// <param name="httpContext"></param>
private void LoadTree(TreeRequestParams treeParams, HttpContext httpContext)
{
TreeDefinition treeDef = TreeDefinitionCollection.Instance.FindTree(treeParams.TreeType);
var appTree = Services.ApplicationTreeService.GetByAlias(treeParams.TreeType);
if (appTree == null)
throw new InvalidOperationException("No tree found with alias " + treeParams.TreeType);
if (treeDef != null)
{
BaseTree bTree = treeDef.CreateInstance();
bTree.SetTreeParameters(treeParams);
bTree.Render(ref _xTree);
}
else
LoadNullTree(treeParams);
}
var controllerAttempt = appTree.TryGetControllerTree();
if (controllerAttempt.Success)
{
var legacyAtt = controllerAttempt.Result.GetCustomAttribute<LegacyBaseTreeAttribute>(false);
if (legacyAtt == null)
{
throw new InvalidOperationException("Cannot render a " + typeof(TreeApiController) + " tree type with the legacy web services unless attributed with " + typeof(LegacyBaseTreeAttribute));
}
/// <summary>
/// Load an empty tree structure to show the end user that there was a problem loading the tree.
/// </summary>
private void LoadNullTree(TreeRequestParams treeParams)
{
BaseTree nullTree = new NullTree(treeParams.Application);
nullTree.SetTreeParameters(treeParams);
nullTree.Render(ref _xTree);
}
}
var treeDef = new TreeDefinition(
legacyAtt.BaseTreeType,
new ApplicationTree(false, true, appTree.SortOrder, appTree.ApplicationAlias, appTree.Alias, appTree.Title, appTree.IconClosed, appTree.IconOpened, "", legacyAtt.BaseTreeType.GetFullNameWithAssembly(), ""),
new Application(treeParams.TreeType, treeParams.TreeType, "", 0));
var tree = treeDef.CreateInstance();
tree.TreeAlias = appTree.Alias;
tree.SetTreeParameters(treeParams);
tree.Render(ref _xTree);
//var context = WebApiHelper.CreateContext(new HttpMethod("GET"), httpContext.Request.Url, new HttpContextWrapper(httpContext));
//var rootAttempt = appTree.TryGetRootNodeFromControllerTree(
// LegacyTreeDataConverter.ConvertFromLegacyTreeParams(treeParams),
// context);
//var nodesAttempt = appTree.TryLoadFromControllerTree(
// treeParams.StartNodeID.ToInvariantString(),
// LegacyTreeDataConverter.ConvertFromLegacyTreeParams(treeParams),
// context);
//if (rootAttempt.Success && nodesAttempt.Success)
//{
// var tree = new LegacyBaseTreeWrapper(treeParams.TreeType, treeParams.Application, rootAttempt.Result, nodesAttempt.Result);
// tree.SetTreeParameters(treeParams);
// tree.Render(ref _xTree);
//}
}
else
{
var treeDef = TreeDefinitionCollection.Instance.FindTree(treeParams.TreeType);
if (treeDef != null)
{
var bTree = treeDef.CreateInstance();
bTree.SetTreeParameters(treeParams);
bTree.Render(ref _xTree);
}
else
LoadNullTree(treeParams);
}
}
/// <summary>
/// Load an empty tree structure to show the end user that there was a problem loading the tree.
/// </summary>
private void LoadNullTree(TreeRequestParams treeParams)
{
BaseTree nullTree = new NullTree(treeParams.Application);
nullTree.SetTreeParameters(treeParams);
nullTree.Render(ref _xTree);
}
}
}