Files
Umbraco-CMS/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs
2013-02-26 18:12:22 -01:00

540 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Xml;
using Umbraco.Core;
using Umbraco.Core.Services;
using umbraco.BusinessLogic;
using umbraco.BusinessLogic.Actions;
using umbraco.interfaces;
namespace umbraco.cms.presentation.Trees
{
/// <summary>
/// All ITree's should inherit from BaseTree.
/// </summary>
public abstract class BaseTree : ITree, ITreeService
{
public BaseTree(string application)
{
this.app = application;
}
protected const string FolderIcon = ".sprTreeFolder";
protected const string FolderIconOpen = ".sprTreeFolder_o";
/// <summary>
/// Returns the node definition of the root node for this tree
/// </summary>
public XmlTreeNode RootNode
{
get
{
Initialize();
return m_initNode;
}
}
/// <summary>
/// By default the init actions that are allowed for all trees are Create, Reload Nodes.
/// These are the menu items that show up in the context menu for the root node of the current tree.
/// Should be used in conjunction with the RootNode property
/// </summary>
public List<IAction> RootNodeActions
{
get
{
Initialize();
return m_initActions;
}
}
/// <summary>
/// The actions that are allowed to be performed on this tree. These are the items that may show up on the
/// context menu for a given node.
/// </summary>
public List<IAction> AllowedActions
{
get
{
Initialize();
return m_allowedActions;
}
}
/// <summary>
/// The tree alias name. By default, if a BaseTree is instantiated by it's TreeDefinition, then the TreeAlias will be
/// the name defined in the database. Inheritors can override this property to set the TreeAlias to whatever they choose.
/// </summary>
public virtual string TreeAlias
{
get
{
if (string.IsNullOrEmpty(m_treeAlias))
{
TreeDefinition treeDef = TreeDefinitionCollection.Instance.FindTree(this);
m_treeAlias = (treeDef != null ? treeDef.Tree.Alias : "");
}
return m_treeAlias;
}
}
private string m_treeAlias;
#region ITreeService Members
/// <summary>
/// By default the start node id will be -1 which will return all of the nodes
/// </summary>
public virtual int StartNodeID
{
get { return -1; }
}
public bool ShowContextMenu
{
get { return m_showContextMenu; }
set { m_showContextMenu = value; }
}
public bool IsDialog
{
get { return m_isDialog; }
set { m_isDialog = value; }
}
/// <summary>
/// The NodeKey is a string representation of the nodeID. Generally this is used for tree's whos node's unique key value is a string in instead
/// of an integer such as folder names.
/// </summary>
public string NodeKey
{
get { return m_nodeKey; }
set { m_nodeKey = value; }
}
public string FunctionToCall
{
get { return m_functionToCall; }
set { m_functionToCall = value; }
}
public TreeDialogModes DialogMode
{
get { return m_dialogMode; }
set { m_dialogMode = value; }
}
#endregion
#region ITree Members
/// <summary>
/// The ID of the node to render. This is generally set before calling the render method of the tree. If it is not set then the
/// StartNodeID property is used as the node ID to render.
/// </summary>
public virtual int id
{
set { m_id = value; }
get { return m_id; }
}
public virtual string app
{
set { m_app = value; }
get { return m_app; }
}
/// <summary>
/// Renders out any JavaScript methods that may be required for tree functionality. Generally used to load the editor page when
/// a user clicks on a tree node.
/// </summary>
/// <param name="Javascript"></param>
public abstract void RenderJS(ref StringBuilder Javascript);
/// <summary>
/// This will call the new Render method which works using a typed XmlTree object instead of the untyped XmlDocument object.
/// This can still be overriden but is only for backwards compatibility.
/// </summary>
/// <param name="Tree"></param>
[Obsolete("Use the other Render method instead")]
public virtual void Render(ref XmlDocument Tree)
{
//call our render method by passing in the XmlTree instead of the XmlDocument
Render(ref m_xTree);
//now that we have an XmlTree object filled, we'll serialize it back to the XmlDocument of the ITree
Tree.LoadXml(m_xTree.ToString(SerializedTreeType.XmlTree));
}
/// <summary>
/// Classes need to override thid method to create the nodes for the XmlTree
/// </summary>
/// <param name="tree"></param>
public abstract void Render(ref XmlTree tree);
#endregion
protected int m_id;
protected string m_app;
protected XmlTreeNode m_initNode;
private List<IAction> m_initActions = new List<IAction>();
private List<IAction> m_allowedActions = new List<IAction>();
//these are the request parameters that can be specified.
//since we want to remove the querystring/httpcontext dependency from
//our trees, we need to define these as properties.
private bool m_showContextMenu = true;
private bool m_isDialog = false;
private TreeDialogModes m_dialogMode = TreeDialogModes.none;
private string m_nodeKey = "";
private string m_functionToCall = "";
private bool m_isInitialized = false;
private XmlTree m_xTree = new XmlTree();
/// <summary>
/// Provides easy access to the ServiceContext
/// </summary>
protected internal ServiceContext Services
{
get { return ApplicationContext.Current.Services; }
}
/// <summary>
/// Initializes the class if it hasn't been done already
/// </summary>
protected void Initialize()
{
if (!m_isInitialized)
{
//VERY IMPORTANT! otherwise it will go infinite loop!
m_isInitialized = true;
CreateAllowedActions(); //first create the allowed actions
//raise the event, allow developers to modify the collection
var nodeActions = new NodeActionsEventArgs(false, m_allowedActions);
OnNodeActionsCreated(nodeActions);
m_allowedActions = nodeActions.AllowedActions;
CreateRootNodeActions();//then create the root node actions
var rootActions = new NodeActionsEventArgs(true, m_initActions);
OnNodeActionsCreated(rootActions);
m_initActions = rootActions.AllowedActions;
CreateRootNode(); //finally, create the root node itself
}
}
/// <summary>
/// This method creates the Root node definition for the tree.
/// Inheritors must override this method to create their own definition.
/// </summary>
/// <param name="rootNode"></param>
protected abstract void CreateRootNode(ref XmlTreeNode rootNode);
protected void CreateRootNode()
{
m_initNode = XmlTreeNode.CreateRoot(this);
m_initNode.Icon = FolderIcon;
m_initNode.OpenIcon = FolderIconOpen;
CreateRootNode(ref m_initNode);
}
/// <summary>
/// This method creates the IAction list for the tree's root node.
/// Inheritors can override this method to create their own Context menu.
/// </summary>
/// <param name="actions"></param>
protected virtual void CreateRootNodeActions(ref List<IAction> actions)
{
actions.AddRange(GetDefaultRootNodeActions());
}
protected void CreateRootNodeActions()
{
CreateRootNodeActions(ref m_initActions);
}
/// <summary>
/// This method creates the AllowedActions IAction list for the tree's nodes.
/// Inheritors can override this method to create their own Context menu.
/// </summary>
/// <param name="actions"></param>
protected virtual void CreateAllowedActions(ref List<IAction> actions)
{
actions.Add(ActionDelete.Instance);
//raise the event, allow developers to modify the collection
var e = new NodeActionsEventArgs(false, actions);
OnNodeActionsCreated(e);
actions = e.AllowedActions;
}
protected void CreateAllowedActions()
{
CreateAllowedActions(ref m_allowedActions);
}
/// <summary>
/// A helper method to re-generate the root node for the current tree.
/// </summary>
/// <returns></returns>
public XmlTreeNode GenerateRootNode()
{
XmlTreeNode node = XmlTreeNode.CreateRoot(this);
this.CreateRootNode(ref node);
return node;
}
/// <summary>
/// This method can initialize the ITreeService parameters for this class with another ITreeService object.
/// This method could be used for Dependency Injection.
/// </summary>
/// <param name="treeParams"></param>
public void SetTreeParameters(ITreeService treeParams)
{
this.DialogMode = treeParams.DialogMode;
this.NodeKey = treeParams.NodeKey;
this.FunctionToCall = treeParams.FunctionToCall;
this.IsDialog = treeParams.IsDialog;
this.ShowContextMenu = treeParams.ShowContextMenu;
this.id = treeParams.StartNodeID;
if (!treeParams.ShowContextMenu)
this.RootNode.Menu = null;
}
/// <summary>
/// Returns the tree service url to render the tree
/// </summary>
/// <returns></returns>
public string GetTreeInitUrl()
{
TreeService treeSvc = new TreeService(this.StartNodeID, TreeAlias, null, null, TreeDialogModes.none, "");
return treeSvc.GetInitUrl();
}
/// <summary>
/// Returns the tree service url to return the tree xml structure from the root node
/// </summary>
/// <returns></returns>
public string GetTreeServiceUrl()
{
return GetTreeServiceUrl(this.StartNodeID);
}
/// <summary>
/// Returns the tree service url to return the tree xml structure from the node passed in
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public string GetTreeServiceUrl(int id)
{
// updated by NH to pass showcontextmenu, isdialog and dialogmode variables
TreeService treeSvc = new TreeService(id, TreeAlias, this.ShowContextMenu, this.IsDialog, this.DialogMode, "");
return treeSvc.GetServiceUrl();
}
/// <summary>
/// Returns the tree service url to return the tree xml structure based on a string node key.
/// </summary>
/// <param name="nodeKey"></param>
/// <returns></returns>
public string GetTreeServiceUrl(string nodeKey)
{
TreeService treeSvc = new TreeService(-1, TreeAlias, this.ShowContextMenu, this.IsDialog, this.DialogMode, "", nodeKey);
return treeSvc.GetServiceUrl();
}
/// <summary>
/// Returns the tree service url to render the tree in dialog mode
/// </summary>
/// <returns></returns>
public virtual string GetTreeDialogUrl()
{
TreeService treeSvc = new TreeService(this.StartNodeID, TreeAlias, false, true, this.DialogMode, "");
return treeSvc.GetServiceUrl();
}
/// <summary>
/// Returns the tree service url to render tree xml structure from the node passed in, in dialog mode.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public virtual string GetTreeDialogUrl(int id)
{
TreeService treeSvc = new TreeService(id, TreeAlias, false, true, this.DialogMode, "");
return treeSvc.GetServiceUrl();
}
/// <summary>
/// Returns the serialized data for the nodeId passed in.
/// </summary>
/// <remarks>
/// This may not work with ITrees that don't support the BaseTree structure with TreeService.
/// If a tree implements other query string data to make it work, this may not function since
/// it only relies on the 3 parameters.
/// </remarks>
/// <param name="alias"></param>
/// <param name="nodeId"></param>
/// <returns></returns>
public string GetSerializedNodeData(string nodeId)
{
XmlTree xTree = new XmlTree();
int id;
if (int.TryParse(nodeId, out id))
this.id = id;
else
this.NodeKey = nodeId;
this.Render(ref xTree);
return xTree.ToString();
}
/// <summary>
/// Returns a boolean value indicating if the ITree passed in is an extension of BaseTree.
/// This is used to preserve backwards compatibility previous to version 5.
/// </summary>
/// <param name="tree"></param>
/// <returns></returns>
public static bool IsBaseTree(ITree tree)
{
return typeof(BaseTree).IsAssignableFrom(tree.GetType());
}
/// <summary>
/// Converts an ITree into a BaseTree. This is used for Legacy trees that don't inherit from BaseTree already.
/// </summary>
/// <param name="tree"></param>
/// <param name="alias"></param>
/// <param name="appAlias"></param>
/// <param name="iconClosed"></param>
/// <param name="iconOpened"></param>
/// <param name="action"></param>
/// <returns></returns>
public static BaseTree FromITree(ITree tree, string alias, string appAlias, string iconClosed, string iconOpened, string action)
{
TreeService treeSvc = new TreeService(null, alias, null, null, TreeDialogModes.none, appAlias);
//create the generic XmlTreeNode and fill it with the properties from the db
NullTree nullTree = new NullTree(appAlias);
XmlTreeNode node = XmlTreeNode.CreateRoot(nullTree);
node.Text = BaseTree.GetTreeHeader(alias);;
node.Action = action;
node.Source = treeSvc.GetServiceUrl();
node.Icon = iconClosed;
node.OpenIcon = iconOpened;
node.NodeType = "init" + alias;
node.NodeType = alias;
node.NodeID = "init";
node.Menu = BaseTree.GetDefaultRootNodeActions();
//convert the tree to a LegacyTree
LegacyTree bTree = new LegacyTree(tree, appAlias, node);
return bTree;
}
/// <summary>
/// Returns the default actions for a root node
/// </summary>
/// <returns></returns>
public static List<IAction> GetDefaultRootNodeActions()
{
List<IAction> actions = new List<IAction>();
actions.Add(ActionNew.Instance);
actions.Add(ContextMenuSeperator.Instance);
actions.Add(ActionRefresh.Instance);
return actions;
}
/// <summary>
/// Returns the tree header title. If the alias isn't found in the language files, then it will
/// return the title stored in the umbracoAppTree table.
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
public static string GetTreeHeader(string alias)
{
string treeCaption = ui.Text(alias);
//this is a hack. the tree header title should be in the language files, however, if it is not, we're just
//going to make it equal to what is specified in the db.
if (treeCaption.Length > 0 && treeCaption.Substring(0, 1) == "[")
{
ApplicationTree tree = ApplicationTree.getByAlias(alias);
if (tree != null)
return tree.Title.SplitPascalCasing().ToFirstUpperInvariant();
}
return treeCaption;
}
#region Events
//These events are poorly designed because they cannot be implemented in the tree inheritance structure,
//it would be up to the individual trees to ensure they launch the events which is not ideal.
//they are also named in appropriately in regards to standards and because everything is by ref, there is no need to
//have 2 events, makes no difference if you want to modify the contents of the data.
public delegate void BeforeNodeRenderEventHandler(ref XmlTree sender, ref XmlTreeNode node, EventArgs e);
public delegate void AfterNodeRenderEventHandler(ref XmlTree sender, ref XmlTreeNode node, EventArgs e);
public static event BeforeNodeRenderEventHandler BeforeNodeRender;
public static event AfterNodeRenderEventHandler AfterNodeRender;
public static event EventHandler<TreeEventArgs> BeforeTreeRender;
public static event EventHandler<TreeEventArgs> AfterTreeRender;
/// <summary>
/// Raises the <see cref="E:BeforeNodeRender"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected virtual void OnBeforeNodeRender(ref XmlTree sender, ref XmlTreeNode node, EventArgs e)
{
if (BeforeNodeRender != null)
BeforeNodeRender(ref sender, ref node, e);
}
/// <summary>
/// Raises the <see cref="E:AfterNodeRender"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected virtual void OnAfterNodeRender(ref XmlTree sender, ref XmlTreeNode node, EventArgs e)
{
if (AfterNodeRender != null)
AfterNodeRender(ref sender, ref node, e);
}
protected virtual void OnBeforeTreeRender(object sender, TreeEventArgs e)
{
if (BeforeTreeRender != null)
BeforeTreeRender(sender, e);
}
protected virtual void OnAfterTreeRender(object sender, TreeEventArgs e)
{
if (AfterTreeRender != null)
AfterTreeRender(sender, e);
}
/// <summary>
/// Event that is raised once actions are assigned to nodes
/// </summary>
public static event EventHandler<NodeActionsEventArgs> NodeActionsCreated;
protected virtual void OnNodeActionsCreated(NodeActionsEventArgs e)
{
if (NodeActionsCreated != null)
NodeActionsCreated(this, e);
}
#endregion
}
}