2009-06-19 07:39:16 +00:00
using System ;
using System.Collections.Generic ;
using System.Data ;
using System.Xml ;
2009-10-06 13:33:09 +00:00
2009-06-19 07:39:16 +00:00
using umbraco.cms.businesslogic.web ;
using umbraco.DataLayer ;
using umbraco.BusinessLogic ;
using System.IO ;
using System.Text.RegularExpressions ;
using System.ComponentModel ;
2010-01-28 12:51:46 +00:00
using umbraco.IO ;
2010-05-16 15:16:02 +00:00
using umbraco.cms.businesslogic.media ;
2010-05-19 15:55:00 +00:00
using System.Collections ;
2010-06-12 15:01:24 +00:00
using umbraco.cms.businesslogic.task ;
using umbraco.cms.businesslogic.workflow ;
2010-06-14 09:57:43 +00:00
using umbraco.cms.businesslogic.Tags ;
2009-06-19 07:39:16 +00:00
2010-02-10 14:00:47 +00:00
namespace umbraco.cms.businesslogic
{
2009-06-19 07:39:16 +00:00
/// <summary>
/// CMSNode class serves as the base class for many of the other components in the cms.businesslogic.xx namespaces.
/// Providing the basic hierarchical data structure and properties Text (name), Creator, Createdate, updatedate etc.
/// which are shared by most umbraco objects.
///
/// The child classes are required to implement an identifier (Guid) which is used as the objecttype identifier, for
/// distinguishing the different types of CMSNodes (ex. Documents/Medias/Stylesheets/documenttypes and so forth).
/// </summary>
2010-02-10 14:00:47 +00:00
public class CMSNode : BusinessLogic . console . IconI
{
2010-04-17 07:51:51 +00:00
#region Private Members
2009-06-19 07:39:16 +00:00
private string _text ;
private int _id = 0 ;
private Guid _uniqueID ;
2010-04-17 07:51:51 +00:00
2009-06-19 07:39:16 +00:00
/// <summary>
/// Private connectionstring
/// </summary>
protected static readonly string _ConnString = GlobalSettings . DbDSN ;
2010-04-17 07:51:51 +00:00
2009-06-19 07:39:16 +00:00
private int _parentid ;
private Guid _nodeObjectType ;
private int _level ;
private string _path ;
private bool _hasChildren ;
private int _sortOrder ;
private int _userId ;
private DateTime _createDate ;
private bool _hasChildrenInitialized ;
2010-05-16 15:16:02 +00:00
private string m_image = "default.png" ;
private bool? _isTrashed = null ;
2010-04-17 07:51:51 +00:00
#endregion
#region Private static
2010-05-19 15:55:00 +00:00
2010-01-28 12:51:46 +00:00
private static readonly string m_DefaultIconCssFile = IOHelper . MapPath ( SystemDirectories . Umbraco_client + "/Tree/treeIcons.css" );
2009-06-19 07:39:16 +00:00
private static List < string > m_DefaultIconClasses = new List < string >();
2010-02-10 14:00:47 +00:00
private static void initializeIconClasses ()
{
2009-06-19 07:39:16 +00:00
StreamReader re = File . OpenText ( m_DefaultIconCssFile );
string content = string . Empty ;
string input = null ;
2010-02-10 14:00:47 +00:00
while (( input = re . ReadLine ()) != null )
{
2009-06-19 07:39:16 +00:00
content += input . Replace ( "\n" , "" ) + "\n" ;
}
re . Close ();
// parse the classes
MatchCollection m = Regex . Matches ( content , "([^{]*){([^}]*)}" , RegexOptions . IgnoreCase | RegexOptions . IgnorePatternWhitespace );
2010-02-10 14:00:47 +00:00
foreach ( Match match in m )
{
2009-06-19 07:39:16 +00:00
GroupCollection groups = match . Groups ;
string cssClass = groups [ 1 ]. Value . Replace ( "\n" , "" ). Replace ( "\r" , "" ). Trim (). Trim ( Environment . NewLine . ToCharArray ());
2010-08-23 08:38:27 +00:00
if (! String . IsNullOrEmpty ( cssClass ))
{
m_DefaultIconClasses . Add ( cssClass );
}
2009-06-19 07:39:16 +00:00
}
}
2010-05-19 15:55:00 +00:00
private const string m_SQLSingle = "SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode WHERE id = @id" ;
private const string m_SQLDescendants = @"
SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text
FROM umbracoNode
WHERE path LIKE '%,{0},%'" ;
2010-04-17 07:51:51 +00:00
#endregion
#region Public static
/// <summary>
/// Get a count on all CMSNodes given the objecttype
/// </summary>
/// <param name="objectType">The objecttype identifier</param>
/// <returns>
/// The number of CMSNodes of the given objecttype
/// </returns>
public static int CountByObjectType ( Guid objectType )
{
return SqlHelper . ExecuteScalar < int >( "SELECT COUNT(*) from umbracoNode WHERE nodeObjectType = @type" , SqlHelper . CreateParameter ( "@type" , objectType ));
}
/// <summary>
/// Number of children of the current CMSNode
/// </summary>
/// <param name="Id">The CMSNode Id</param>
/// <returns>
/// The number of children from the given CMSNode
/// </returns>
public static int CountSubs ( int Id )
{
return SqlHelper . ExecuteScalar < int >( "SELECT COUNT(*) FROM umbracoNode WHERE ','+path+',' LIKE '%," + Id . ToString () + ",%'" );
}
/// <summary>
/// Gets the default icon classes.
/// </summary>
/// <value>The default icon classes.</value>
public static List < string > DefaultIconClasses
{
get
{
if ( m_DefaultIconClasses . Count == 0 )
initializeIconClasses ();
return m_DefaultIconClasses ;
}
}
/// <summary>
/// Method for checking if a CMSNode exits with the given Guid
/// </summary>
/// <param name="uniqueID">Identifier</param>
/// <returns>True if there is a CMSNode with the given Guid</returns>
public static bool IsNode ( Guid uniqueID )
{
return ( SqlHelper . ExecuteScalar < int >( "select count(id) from umbracoNode where uniqueID = @uniqueID" , SqlHelper . CreateParameter ( "@uniqueId" , uniqueID )) > 0 );
}
/// <summary>
/// Method for checking if a CMSNode exits with the given id
/// </summary>
/// <param name="Id">Identifier</param>
/// <returns>True if there is a CMSNode with the given id</returns>
public static bool IsNode ( int Id )
{
2010-05-16 15:16:02 +00:00
return ( SqlHelper . ExecuteScalar < int >( "select count(id) from umbracoNode where id = @id" , SqlHelper . CreateParameter ( "@id" , Id )) > 0 );
2010-04-17 07:51:51 +00:00
}
/// <summary>
/// Retrieve a list of the unique id's of all CMSNodes given the objecttype
/// </summary>
/// <param name="objectType">The objecttype identifier</param>
/// <returns>
/// A list of all unique identifiers which each are associated to a CMSNode
/// </returns>
public static Guid [] getAllUniquesFromObjectType ( Guid objectType )
{
IRecordsReader dr = SqlHelper . ExecuteReader ( "Select uniqueID from umbracoNode where nodeObjectType = @type" ,
SqlHelper . CreateParameter ( "@type" , objectType ));
System . Collections . ArrayList tmp = new System . Collections . ArrayList ();
while ( dr . Read ()) tmp . Add ( dr . GetGuid ( "uniqueID" ));
dr . Close ();
Guid [] retval = new Guid [ tmp . Count ];
for ( int i = 0 ; i < tmp . Count ; i ++) retval [ i ] = ( Guid ) tmp [ i ];
return retval ;
}
/// <summary>
/// Retrieve a list of the node id's of all CMSNodes given the objecttype
/// </summary>
/// <param name="objectType">The objecttype identifier</param>
/// <returns>
/// A list of all node ids which each are associated to a CMSNode
/// </returns>
public static int [] getAllUniqueNodeIdsFromObjectType ( Guid objectType )
{
IRecordsReader dr = SqlHelper . ExecuteReader ( "Select id from umbracoNode where nodeObjectType = @type" ,
SqlHelper . CreateParameter ( "@type" , objectType ));
System . Collections . ArrayList tmp = new System . Collections . ArrayList ();
while ( dr . Read ()) tmp . Add ( dr . GetInt ( "id" ));
dr . Close ();
return ( int []) tmp . ToArray ( typeof ( int ));
}
#endregion
#region Protected static
/// <summary>
/// Retrieves the top level nodes in the hierarchy
/// </summary>
/// <param name="ObjectType">The Guid identifier of the type of objects</param>
/// <returns>
/// A list of all top level nodes given the objecttype
/// </returns>
protected static Guid [] TopMostNodeIds ( Guid ObjectType )
{
IRecordsReader dr = SqlHelper . ExecuteReader ( "Select uniqueID from umbracoNode where nodeObjectType = @type And parentId = -1 order by sortOrder" ,
SqlHelper . CreateParameter ( "@type" , ObjectType ));
System . Collections . ArrayList tmp = new System . Collections . ArrayList ();
while ( dr . Read ()) tmp . Add ( dr . GetGuid ( "uniqueID" ));
dr . Close ();
Guid [] retval = new Guid [ tmp . Count ];
for ( int i = 0 ; i < tmp . Count ; i ++) retval [ i ] = ( Guid ) tmp [ i ];
return retval ;
}
/// <summary>
/// Given the protected modifier the CMSNode.MakeNew method can only be accessed by
/// derived classes > who by definition knows of its own objectType.
/// </summary>
/// <param name="parentId">The parent CMSNode id</param>
/// <param name="objectType">The objecttype identifier</param>
/// <param name="userId">Creator</param>
/// <param name="level">The level in the tree hieararchy</param>
/// <param name="text">The name of the CMSNode</param>
/// <param name="uniqueID">The unique identifier</param>
/// <returns></returns>
protected static CMSNode MakeNew ( int parentId , Guid objectType , int userId , int level , string text , Guid uniqueID )
{
CMSNode parent ;
string path = "" ;
int sortOrder = 0 ;
if ( level > 0 )
{
parent = new CMSNode ( parentId );
sortOrder = parent . ChildCount + 1 ;
path = parent . Path ;
}
else
path = "-1" ;
// Ruben 8/1/2007: I replace this with a parameterized version.
// But does anyone know what the 'level++' is supposed to be doing there?
// Nothing obviously, since it's a postfix.
2010-05-16 15:16:02 +00:00
SqlHelper . ExecuteNonQuery ( "INSERT INTO umbracoNode(trashed, parentID, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text, createDate) VALUES(@trashed, @parentID, @nodeObjectType, @nodeUser, @level, @path, @sortOrder, @uniqueID, @text, @createDate)" ,
2010-04-17 07:51:51 +00:00
SqlHelper . CreateParameter ( "@trashed" , 0 ),
SqlHelper . CreateParameter ( "@parentID" , parentId ),
SqlHelper . CreateParameter ( "@nodeObjectType" , objectType ),
SqlHelper . CreateParameter ( "@nodeUser" , userId ),
SqlHelper . CreateParameter ( "@level" , level ++),
SqlHelper . CreateParameter ( "@path" , path ),
SqlHelper . CreateParameter ( "@sortOrder" , sortOrder ),
SqlHelper . CreateParameter ( "@uniqueID" , uniqueID ),
2010-05-16 15:16:02 +00:00
SqlHelper . CreateParameter ( "@text" , text ),
SqlHelper . CreateParameter ( "@createDate" , DateTime . Now ));
2010-04-17 07:51:51 +00:00
CMSNode retVal = new CMSNode ( uniqueID );
retVal . Path = path + "," + retVal . Id . ToString ();
//event
NewEventArgs e = new NewEventArgs ();
retVal . FireAfterNew ( e );
return retVal ;
}
/// <summary>
/// Retrieve a list of the id's of all CMSNodes given the objecttype and the first letter of the name.
/// </summary>
/// <param name="objectType">The objecttype identifier</param>
/// <param name="letter">Firstletter</param>
/// <returns>
/// A list of all CMSNodes which has the objecttype and a name that starts with the given letter
/// </returns>
protected static int [] getUniquesFromObjectTypeAndFirstLetter ( Guid objectType , char letter )
{
using ( IRecordsReader dr = SqlHelper . ExecuteReader ( "Select id from umbracoNode where nodeObjectType = @objectType AND text like @letter" , SqlHelper . CreateParameter ( "@objectType" , objectType ), SqlHelper . CreateParameter ( "@letter" , letter . ToString () + "%" )))
{
List < int > tmp = new List < int >();
while ( dr . Read ()) tmp . Add ( dr . GetInt ( "id" ));
return tmp . ToArray ();
}
}
2009-06-19 07:39:16 +00:00
/// <summary>
/// Gets the SQL helper.
/// </summary>
/// <value>The SQL helper.</value>
2010-02-10 14:00:47 +00:00
protected static ISqlHelper SqlHelper
{
2009-06-19 07:39:16 +00:00
get { return Application . SqlHelper ; }
2010-04-17 07:51:51 +00:00
}
#endregion
2009-06-19 07:39:16 +00:00
2010-04-17 07:51:51 +00:00
#region Constructors
2010-06-01 15:46:25 +00:00
/// <summary>
/// Empty constructor that is not suported
/// ...why is it here?
/// </summary>
2009-07-06 12:04:51 +00:00
public CMSNode ()
{
throw new NotSupportedException ();
}
2009-06-19 07:39:16 +00:00
/// <summary>
/// Initializes a new instance of the <see cref="CMSNode"/> class.
/// </summary>
/// <param name="Id">The id.</param>
2010-02-10 14:00:47 +00:00
public CMSNode ( int Id )
{
2009-06-19 07:39:16 +00:00
_id = Id ;
setupNode ();
}
/// <summary>
/// Initializes a new instance of the <see cref="CMSNode"/> class.
/// </summary>
/// <param name="id">The id.</param>
/// <param name="noSetup">if set to <c>true</c> [no setup].</param>
2010-02-10 14:00:47 +00:00
public CMSNode ( int id , bool noSetup )
{
2009-06-19 07:39:16 +00:00
_id = id ;
2010-02-10 14:00:47 +00:00
2010-06-01 15:46:25 +00:00
if (! noSetup )
setupNode ();
2009-06-19 07:39:16 +00:00
}
/// <summary>
/// Initializes a new instance of the <see cref="CMSNode"/> class.
/// </summary>
/// <param name="uniqueID">The unique ID.</param>
2010-02-10 14:00:47 +00:00
public CMSNode ( Guid uniqueID )
{
2009-06-19 07:39:16 +00:00
_id = SqlHelper . ExecuteScalar < int >( "SELECT id FROM umbracoNode WHERE uniqueID = @uniqueId" , SqlHelper . CreateParameter ( "@uniqueId" , uniqueID ));
setupNode ();
}
2010-06-01 15:46:25 +00:00
public CMSNode ( Guid uniqueID , bool noSetup )
{
_id = SqlHelper . ExecuteScalar < int >( "SELECT id FROM umbracoNode WHERE uniqueID = @uniqueId" , SqlHelper . CreateParameter ( "@uniqueId" , uniqueID ));
if (! noSetup )
setupNode ();
}
protected internal CMSNode ( IRecordsReader reader )
2010-04-15 12:54:31 +00:00
{
2010-04-17 07:51:51 +00:00
_id = reader . GetInt ( "id" );
2010-04-20 15:36:11 +00:00
PopulateCMSNodeFromReader ( reader );
2010-04-17 07:51:51 +00:00
}
2010-06-01 15:46:25 +00:00
2010-04-17 07:51:51 +00:00
#endregion
#region Public Methods
2010-04-15 12:54:31 +00:00
2010-05-25 14:58:43 +00:00
/// <summary>
/// Ensures uniqueness by id
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals ( object obj )
{
var l = obj as CMSNode ;
if ( l != null )
{
return this . _id . Equals ( l . _id );
}
return false ;
}
/// <summary>
/// Ensures uniqueness by id
/// </summary>
/// <returns></returns>
public override int GetHashCode ()
{
return _id . GetHashCode ();
}
2010-05-16 15:16:02 +00:00
/// <summary>
/// An xml representation of the CMSNOde
/// </summary>
/// <param name="xd">Xmldocument context</param>
/// <param name="Deep">If true the xml will append the CMSNodes child xml</param>
/// <returns>The CMSNode Xmlrepresentation</returns>
public virtual XmlNode ToXml ( XmlDocument xd , bool Deep )
{
XmlNode x = xd . CreateNode ( XmlNodeType . Element , "node" , "" );
XmlPopulate ( xd , x , Deep );
return x ;
}
public virtual XmlNode ToPreviewXml ( XmlDocument xd )
{
// If xml already exists
if (! PreviewExists ( UniqueId ))
{
2010-06-14 15:04:13 +00:00
SavePreviewXml ( ToXml ( xd , false ), UniqueId );
2010-05-16 15:16:02 +00:00
}
return GetPreviewXml ( xd , UniqueId );
}
public virtual List < CMSPreviewNode > GetNodesForPreview ( bool childrenOnly )
{
List < CMSPreviewNode > nodes = new List < CMSPreviewNode >();
string sql = @"
select umbracoNode.id, umbracoNode.parentId, umbracoNode.level, umbracoNode.sortOrder, cmsPreviewXml.xml from umbracoNode
inner join cmsPreviewXml on cmsPreviewXml.nodeId = umbracoNode.id
where trashed = 0 and path like '{0}'
order by level,sortOrder" ;
string pathExp = childrenOnly ? Path + ",%" : Path ;
IRecordsReader dr = SqlHelper . ExecuteReader ( String . Format ( sql , pathExp ));
while ( dr . Read ())
nodes . Add ( new CMSPreviewNode ( dr . GetInt ( "id" ), dr . GetGuid ( "uniqueID" ), dr . GetInt ( "parentId" ), dr . GetShort ( "level" ), dr . GetInt ( "sortOrder" ), dr . GetString ( "xml" )));
dr . Close ();
return nodes ;
}
2009-06-19 07:39:16 +00:00
/// <summary>
/// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility
/// </summary>
2010-02-10 14:00:47 +00:00
public virtual void Save ()
{
2009-06-19 07:39:16 +00:00
SaveEventArgs e = new SaveEventArgs ();
this . FireBeforeSave ( e );
2010-02-10 14:00:47 +00:00
if (! e . Cancel )
{
2009-06-19 07:39:16 +00:00
//In the future there will be SQL stuff happening here...
this . FireAfterSave ( e );
}
}
2010-04-17 07:51:51 +00:00
public override string ToString ()
{
if ( Id != int . MinValue || ! string . IsNullOrEmpty ( Text ))
{
return string . Format ( "{{ Id: {0}, Text: {1}, ParentId: {2} }}" ,
Id ,
Text ,
_parentid
);
}
return base . ToString ();
2010-05-16 15:16:02 +00:00
}
2009-06-19 07:39:16 +00:00
2010-05-19 15:55:00 +00:00
private void Move ( CMSNode parent )
2010-02-10 14:00:47 +00:00
{
2010-04-17 07:51:51 +00:00
MoveEventArgs e = new MoveEventArgs ();
FireBeforeMove ( e );
2010-02-10 14:00:47 +00:00
2010-04-17 07:51:51 +00:00
if (! e . Cancel )
2009-06-19 07:39:16 +00:00
{
2010-05-19 15:55:00 +00:00
//first we need to establish if the node already exists under the parent node
var isSameParent = ( Path . Contains ( "," + parent . Id + "," ));
2010-05-16 15:16:02 +00:00
//if it's the same parent, we can save some SQL calls since we know these wont change.
//level and path might change even if it's the same parent because the parent could be moving somewhere.
if (! isSameParent )
{
2010-05-19 15:55:00 +00:00
int maxSortOrder = SqlHelper . ExecuteScalar < int >( "select coalesce(max(sortOrder),0) from umbracoNode where parentid = @parentId" ,
SqlHelper . CreateParameter ( "@parentId" , parent . Id ));
2009-06-19 07:39:16 +00:00
2010-05-19 15:55:00 +00:00
this . Parent = parent ;
2010-05-16 15:16:02 +00:00
this . sortOrder = maxSortOrder + 1 ;
2010-05-19 15:55:00 +00:00
}
2010-08-19 02:31:13 +00:00
this . Parent = parent ;
2010-05-19 15:55:00 +00:00
this . Level = parent . Level + 1 ;
this . Path = parent . Path + "," + this . Id . ToString ();
2010-04-17 07:51:51 +00:00
2010-05-16 15:16:02 +00:00
//this code block should not be here but since the class structure is very poor and doesn't use
//overrides (instead using shadows/new) for the Children property, when iterating over the children
//and calling Move(), the super classes overridden OnMove or Move methods never get fired, so
//we now need to hard code this here :(
2010-04-17 07:51:51 +00:00
2010-05-16 15:16:02 +00:00
if ( Path . Contains ( "," + (( int ) RecycleBin . RecycleBinType . Content ). ToString () + "," )
|| Path . Contains ( "," + (( int ) RecycleBin . RecycleBinType . Media ). ToString () + "," ))
{
//if we've moved this to the recyle bin, we need to update the trashed property
if (! IsTrashed ) IsTrashed = true ; //don't update if it's not necessary
}
else
{
if ( IsTrashed ) IsTrashed = false ; //don't update if it's not necessary
}
2010-04-17 07:51:51 +00:00
2010-05-19 15:55:00 +00:00
//make sure the node type is a document/media, if it is a recycle bin then this will not be equal
if (! IsTrashed && parent . nodeObjectType == Document . _objectType )
{
//regenerate the xml for the parent node
var d = new Document ( parent . Id );
d . XmlGenerate ( new XmlDocument ());
}
else if (! IsTrashed && parent . nodeObjectType == Media . _objectType )
{
//regenerate the xml for the parent node
var m = new Media ( parent . Id );
m . XmlGenerate ( new XmlDocument ());
}
2010-04-17 07:51:51 +00:00
var children = this . Children ;
foreach ( CMSNode c in children )
2010-05-16 15:16:02 +00:00
{
2010-05-19 15:55:00 +00:00
c . Move ( this );
2010-05-16 15:16:02 +00:00
}
2010-04-17 07:51:51 +00:00
FireAfterMove ( e );
2009-06-19 07:39:16 +00:00
}
2010-04-17 07:51:51 +00:00
}
2009-06-19 07:39:16 +00:00
2010-05-19 15:55:00 +00:00
/// <summary>
/// Moves the CMSNode from the current position in the hierarchy to the target
/// </summary>
/// <param name="NewParentId">Target CMSNode id</param>
public void Move ( int newParentId )
{
CMSNode parent = new CMSNode ( newParentId );
Move ( parent );
}
2010-04-17 07:51:51 +00:00
/// <summary>
/// Deletes this instance.
/// </summary>
2010-04-20 15:36:11 +00:00
public virtual void delete ()
2010-04-17 07:51:51 +00:00
{
DeleteEventArgs e = new DeleteEventArgs ();
FireBeforeDelete ( e );
if (! e . Cancel )
{
// remove relations
var rels = Relations ;
foreach ( relation . Relation rel in rels )
{
rel . Delete ();
}
2010-06-12 15:01:24 +00:00
//removes tasks
foreach ( Task t in Tasks )
{
t . Delete ();
}
//remove notifications
Notification . DeleteNotifications ( this );
//remove permissions
Permission . DeletePermissions ( this );
2010-06-14 09:57:43 +00:00
//removes tag associations (i know the key is set to cascade but do it anyways)
Tag . RemoveTagsFromNode ( this . Id );
2010-04-17 07:51:51 +00:00
SqlHelper . ExecuteNonQuery ( "DELETE FROM umbracoNode WHERE uniqueID= @uniqueId" , SqlHelper . CreateParameter ( "@uniqueId" , _uniqueID ));
FireAfterDelete ( e );
}
}
/// <summary>
/// Does the current CMSNode have any child nodes.
/// </summary>
/// <value>
/// <c>true</c> if this instance has children; otherwise, <c>false</c>.
/// </value>
public virtual bool HasChildren
{
get
{
if (! _hasChildrenInitialized )
{
2010-05-17 15:41:04 +00:00
int tmpChildrenCount = SqlHelper . ExecuteScalar < int >( "select count(id) from umbracoNode where ParentId = @id" ,
SqlHelper . CreateParameter ( "@id" , Id ));
2010-04-17 07:51:51 +00:00
HasChildren = ( tmpChildrenCount > 0 );
}
return _hasChildren ;
}
set
{
_hasChildrenInitialized = true ;
_hasChildren = value ;
}
2010-05-19 15:55:00 +00:00
}
/// <summary>
/// Returns all descendant nodes from this node.
/// </summary>
/// <returns></returns>
/// <remarks>
/// This doesn't return a strongly typed IEnumerable object so that we can override in in super clases
/// and since this class isn't a generic (thought it should be) this is not strongly typed.
/// </remarks>
public virtual IEnumerable GetDescendants ()
{
var descendants = new List < CMSNode >();
using ( IRecordsReader dr = SqlHelper . ExecuteReader ( string . Format ( m_SQLDescendants , Id )))
{
while ( dr . Read ())
{
var node = new CMSNode ( dr . GetInt ( "id" ), true );
node . PopulateCMSNodeFromReader ( dr );
descendants . Add ( node );
}
}
return descendants ;
}
2010-04-17 07:51:51 +00:00
#endregion
#region Public properties
/// <summary>
2010-05-16 15:16:02 +00:00
/// Determines if the node is in the recycle bin.
/// This is only relavent for node types that support a recyle bin (such as Document/Media)
2010-04-17 07:51:51 +00:00
/// </summary>
2010-05-16 15:16:02 +00:00
public bool IsTrashed
2010-04-17 07:51:51 +00:00
{
2010-05-16 15:16:02 +00:00
get
2010-04-17 07:51:51 +00:00
{
2010-05-16 15:16:02 +00:00
if (! _isTrashed . HasValue )
{
_isTrashed = Convert . ToBoolean ( SqlHelper . ExecuteScalar < object >( "SELECT trashed FROM umbracoNode where id=@id" ,
SqlHelper . CreateParameter ( "@id" , this . Id )));
}
return _isTrashed . Value ;
}
set
{
_isTrashed = value ;
SqlHelper . ExecuteNonQuery ( "update umbracoNode set trashed = @trashed where id = @id" ,
SqlHelper . CreateParameter ( "@trashed" , value ),
SqlHelper . CreateParameter ( "@id" , this . Id ));
2010-04-17 07:51:51 +00:00
}
2009-06-19 07:39:16 +00:00
}
/// <summary>
/// Gets or sets the sort order.
/// </summary>
/// <value>The sort order.</value>
2010-02-10 14:00:47 +00:00
public int sortOrder
{
2009-06-19 07:39:16 +00:00
get { return _sortOrder ; }
2010-02-10 14:00:47 +00:00
set
{
2009-06-19 07:39:16 +00:00
_sortOrder = value ;
SqlHelper . ExecuteNonQuery ( "update umbracoNode set sortOrder = '" + value + "' where id = " + this . Id . ToString ());
}
}
/// <summary>
/// Gets or sets the create date time.
/// </summary>
/// <value>The create date time.</value>
2010-02-10 14:00:47 +00:00
public DateTime CreateDateTime
{
2009-06-19 07:39:16 +00:00
get { return _createDate ; }
2010-02-10 14:00:47 +00:00
set
{
2009-06-19 07:39:16 +00:00
_createDate = value ;
SqlHelper . ExecuteNonQuery ( "update umbracoNode set createDate = @createDate where id = " + this . Id . ToString (), SqlHelper . CreateParameter ( "@createDate" , _createDate ));
}
}
/// <summary>
/// Gets the creator
/// </summary>
/// <value>The user.</value>
2010-02-10 14:00:47 +00:00
public BusinessLogic . User User
{
get
{
2009-06-19 07:39:16 +00:00
return BusinessLogic . User . GetUser ( _userId );
}
}
/// <summary>
/// Gets the id.
/// </summary>
/// <value>The id.</value>
2010-02-10 14:00:47 +00:00
public int Id
{
2009-06-19 07:39:16 +00:00
get { return _id ; }
}
2010-05-19 15:55:00 +00:00
/// <summary>
/// Get the parent id of the node
/// </summary>
public int ParentId
{
get { return _parentid ; }
}
2009-06-19 07:39:16 +00:00
/// <summary>
/// Given the hierarchical tree structure a CMSNode has only one parent but can have many children
/// </summary>
/// <value>The parent.</value>
2010-02-10 14:00:47 +00:00
public CMSNode Parent
{
get
{
2009-06-19 07:39:16 +00:00
if ( Level == 1 ) throw new ArgumentException ( "No parent node" );
return new CMSNode ( _parentid );
}
2010-02-10 14:00:47 +00:00
set
{
2009-06-19 07:39:16 +00:00
_parentid = value . Id ;
SqlHelper . ExecuteNonQuery ( "update umbracoNode set parentId = " + value . Id . ToString () + " where id = " + this . Id . ToString ());
}
}
2010-04-17 07:51:51 +00:00
/// <summary>
/// An comma separated string consisting of integer node id's
/// that indicates the path from the topmost node to the given node
/// </summary>
/// <value>The path.</value>
public string Path
{
get { return _path ; }
set
{
_path = value ;
SqlHelper . ExecuteNonQuery ( "update umbracoNode set path = '" + _path + "' where id = " + this . Id . ToString ());
}
}
/// <summary>
/// Returns an integer value that indicates in which level of the
/// tree structure the given node is
/// </summary>
/// <value>The level.</value>
public int Level
{
get { return _level ; }
set
{
_level = value ;
SqlHelper . ExecuteNonQuery ( "update umbracoNode set level = " + _level . ToString () + " where id = " + this . Id . ToString ());
}
}
/// <summary>
/// All CMSNodes has an objecttype ie. Webpage, StyleSheet etc., used to distinguish between the different
/// object types for for fast loading children to the tree.
/// </summary>
/// <value>The type of the node object.</value>
public Guid nodeObjectType
{
get { return _nodeObjectType ; }
}
/// <summary>
/// Besides the hierarchy it's possible to relate one CMSNode to another, use this for alternative
/// non-strict hierarchy
/// </summary>
/// <value>The relations.</value>
public relation . Relation [] Relations
{
get { return relation . Relation . GetRelations ( this . Id ); }
}
2010-06-12 15:01:24 +00:00
/// <summary>
/// Returns all tasks associated with this node
/// </summary>
public Tasks Tasks
{
get { return Task . GetTasks ( this . Id ); }
}
2010-04-17 07:51:51 +00:00
public virtual int ChildCount
{
get
{
return SqlHelper . ExecuteScalar < int >( "SELECT COUNT(*) FROM umbracoNode where ParentID = @parentId" ,
SqlHelper . CreateParameter ( "@parentId" , this . Id ));
}
}
/// <summary>
/// The basic recursive tree pattern
/// </summary>
/// <value>The children.</value>
public virtual BusinessLogic . console . IconI [] Children
{
get
{
System . Collections . ArrayList tmp = new System . Collections . ArrayList ();
2010-05-16 15:16:02 +00:00
using ( IRecordsReader dr = SqlHelper . ExecuteReader ( "SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode WHERE ParentID = @ParentID AND nodeObjectType = @type order by sortOrder" ,
2010-05-19 15:55:00 +00:00
SqlHelper . CreateParameter ( "@type" , this . nodeObjectType ),
SqlHelper . CreateParameter ( "ParentID" , this . Id )))
2010-04-17 07:51:51 +00:00
{
2010-05-16 15:16:02 +00:00
while ( dr . Read ())
{
tmp . Add ( new CMSNode ( dr ));
}
2010-04-17 07:51:51 +00:00
}
2010-05-16 15:16:02 +00:00
2010-04-17 07:51:51 +00:00
CMSNode [] retval = new CMSNode [ tmp . Count ];
for ( int i = 0 ; i < tmp . Count ; i ++)
{
retval [ i ] = ( CMSNode ) tmp [ i ];
}
return retval ;
}
}
/// <summary>
/// Retrieve all CMSNodes in the umbraco installation
/// Use with care.
/// </summary>
/// <value>The children of all object types.</value>
public BusinessLogic . console . IconI [] ChildrenOfAllObjectTypes
{
get
{
System . Collections . ArrayList tmp = new System . Collections . ArrayList ();
IRecordsReader dr = SqlHelper . ExecuteReader ( "select id from umbracoNode where ParentID = " + this . Id + " order by sortOrder" );
while ( dr . Read ())
tmp . Add ( dr . GetInt ( "Id" ));
dr . Close ();
CMSNode [] retval = new CMSNode [ tmp . Count ];
for ( int i = 0 ; i < tmp . Count ; i ++)
retval [ i ] = new CMSNode (( int ) tmp [ i ]);
return retval ;
}
}
2009-06-19 07:39:16 +00:00
#region IconI members
// Unique identifier of the given node
/// <summary>
/// Unique identifier of the CMSNode, used when locating data.
/// </summary>
2010-02-10 14:00:47 +00:00
public Guid UniqueId
{
2009-06-19 07:39:16 +00:00
get { return _uniqueID ; }
}
/// <summary>
/// Human readable name/label
/// </summary>
2010-02-10 14:00:47 +00:00
public virtual string Text
{
2009-06-19 07:39:16 +00:00
get { return _text ; }
2010-02-10 14:00:47 +00:00
set
{
2009-06-19 07:39:16 +00:00
_text = value ;
SqlHelper . ExecuteNonQuery ( "UPDATE umbracoNode SET text = @text WHERE id = @id" ,
2010-08-20 08:32:33 +00:00
SqlHelper . CreateParameter ( "@text" , value . Trim ()),
2009-06-19 07:39:16 +00:00
SqlHelper . CreateParameter ( "@id" , this . Id ));
}
}
/// <summary>
/// The menu items used in the tree view
/// </summary>
2009-10-13 15:00:49 +00:00
[Obsolete("this is not used anywhere")]
2010-02-10 14:00:47 +00:00
public virtual BusinessLogic . console . MenuItemI [] MenuItems
{
2009-06-19 07:39:16 +00:00
get { return new BusinessLogic . console . MenuItemI [ 0 ]; }
}
/// <summary>
/// Not implemented, always returns "about:blank"
/// </summary>
2010-02-10 14:00:47 +00:00
public virtual string DefaultEditorURL
{
2009-06-19 07:39:16 +00:00
get { return "about:blank" ; }
}
/// <summary>
/// The icon in the tree
/// </summary>
2010-02-10 14:00:47 +00:00
public virtual string Image
{
2009-06-19 07:39:16 +00:00
get { return m_image ; }
set { m_image = value ; }
}
/// <summary>
/// The "open/active" icon in the tree
/// </summary>
2010-02-10 14:00:47 +00:00
public virtual string OpenImage
{
2009-06-19 07:39:16 +00:00
get { return "" ; }
}
#endregion
2010-04-17 07:51:51 +00:00
#endregion
#region Protected methods
2009-06-19 07:39:16 +00:00
/// <summary>
2010-04-17 07:51:51 +00:00
/// This allows inheritors to set the underlying text property without persisting the change to the database.
2009-06-19 07:39:16 +00:00
/// </summary>
2010-04-17 07:51:51 +00:00
/// <param name="txt"></param>
protected void SetText ( string txt )
2010-02-10 14:00:47 +00:00
{
2010-04-17 07:51:51 +00:00
_text = txt ;
}
/// <summary>
/// Sets up the internal data of the CMSNode, used by the various constructors
/// </summary>
protected virtual void setupNode ()
{
2010-05-19 15:55:00 +00:00
using ( IRecordsReader dr = SqlHelper . ExecuteReader ( m_SQLSingle ,
SqlHelper . CreateParameter ( "@id" , this . Id )))
2010-02-10 14:00:47 +00:00
{
2010-04-20 15:36:11 +00:00
if ( dr . Read ())
{
PopulateCMSNodeFromReader ( dr );
}
else
{
throw new ArgumentException ( string . Format ( "No node exists with id '{0}'" , Id ));
}
2010-04-17 07:51:51 +00:00
}
}
/// <summary>
/// Sets up the node for the content tree, this makes no database calls, just sets the underlying properties
/// </summary>
/// <param name="uniqueID">The unique ID.</param>
/// <param name="nodeObjectType">Type of the node object.</param>
/// <param name="Level">The level.</param>
/// <param name="ParentId">The parent id.</param>
/// <param name="UserId">The user id.</param>
/// <param name="Path">The path.</param>
/// <param name="Text">The text.</param>
/// <param name="CreateDate">The create date.</param>
/// <param name="hasChildren">if set to <c>true</c> [has children].</param>
protected void SetupNodeForTree ( Guid uniqueID , Guid nodeObjectType , int leve , int parentId , int userId , string path , string text ,
DateTime createDate , bool hasChildren )
{
_uniqueID = uniqueID ;
_nodeObjectType = nodeObjectType ;
_level = leve ;
_parentid = parentId ;
_userId = userId ;
_path = path ;
_text = text ;
_createDate = createDate ;
HasChildren = hasChildren ;
2009-06-19 07:39:16 +00:00
}
/// <summary>
/// Updates the temp path for the content tree.
/// </summary>
/// <param name="Path">The path.</param>
2010-02-10 14:00:47 +00:00
protected void UpdateTempPathForTree ( string Path )
{
2009-06-19 07:39:16 +00:00
this . _path = Path ;
}
2010-02-10 14:00:47 +00:00
protected virtual XmlNode GetPreviewXml ( XmlDocument xd , Guid version )
{
XmlDocument xmlDoc = new XmlDocument ();
2010-05-16 15:16:02 +00:00
using ( XmlReader xmlRdr = SqlHelper . ExecuteXmlReader (
2010-02-10 14:00:47 +00:00
"select xml from cmsPreviewXml where nodeID = @nodeId and versionId = @versionId" ,
SqlHelper . CreateParameter ( "@nodeId" , Id ),
2010-05-16 15:16:02 +00:00
SqlHelper . CreateParameter ( "@versionId" , version )))
{
xmlDoc . Load ( xmlRdr );
}
2010-02-10 14:00:47 +00:00
return xd . ImportNode ( xmlDoc . FirstChild , true );
}
2010-06-14 15:04:13 +00:00
protected internal virtual bool PreviewExists ( Guid versionId )
2010-02-10 14:00:47 +00:00
{
return ( SqlHelper . ExecuteScalar < int >( "SELECT COUNT(nodeId) FROM cmsPreviewXml WHERE nodeId=@nodeId and versionId = @versionId" ,
SqlHelper . CreateParameter ( "@nodeId" , Id ), SqlHelper . CreateParameter ( "@versionId" , versionId )) != 0 );
}
2010-06-14 15:04:13 +00:00
protected void SavePreviewXml ( XmlNode x , Guid versionId )
2010-02-10 14:00:47 +00:00
{
string sql = PreviewExists ( versionId ) ? "UPDATE cmsPreviewXml SET xml = @xml, timestamp = @timestamp WHERE nodeId=@nodeId AND versionId = @versionId"
: "INSERT INTO cmsPreviewXml(nodeId, versionId, timestamp, xml) VALUES (@nodeId, @versionId, @timestamp, @xml)" ;
SqlHelper . ExecuteNonQuery ( sql ,
SqlHelper . CreateParameter ( "@nodeId" , Id ),
SqlHelper . CreateParameter ( "@versionId" , versionId ),
SqlHelper . CreateParameter ( "@timestamp" , DateTime . Now ),
SqlHelper . CreateParameter ( "@xml" , x . OuterXml ));
}
2010-04-20 15:36:11 +00:00
protected void PopulateCMSNodeFromReader ( IRecordsReader dr )
2010-04-15 12:54:31 +00:00
{
2010-04-20 15:36:11 +00:00
// testing purposes only > original umbraco data hasn't any unique values ;)
// And we need to have a parent in order to create a new node ..
// Should automatically add an unique value if no exists (or throw a decent exception)
2010-04-15 12:54:31 +00:00
if ( dr . IsNull ( "uniqueID" )) _uniqueID = Guid . NewGuid ();
else _uniqueID = dr . GetGuid ( "uniqueID" );
_nodeObjectType = dr . GetGuid ( "nodeObjectType" );
_level = dr . GetShort ( "level" );
_path = dr . GetString ( "path" );
_parentid = dr . GetInt ( "parentId" );
_text = dr . GetString ( "text" );
_sortOrder = dr . GetInt ( "sortOrder" );
_userId = dr . GetInt ( "nodeUser" );
_createDate = dr . GetDateTime ( "createDate" );
2010-05-16 15:16:02 +00:00
_isTrashed = dr . GetBoolean ( "trashed" );
2010-04-15 12:54:31 +00:00
}
2010-04-20 15:36:11 +00:00
#endregion
#region Private Methods
2010-02-10 14:00:47 +00:00
private void XmlPopulate ( XmlDocument xd , XmlNode x , bool Deep )
{
2009-06-19 07:39:16 +00:00
// attributes
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "id" , this . Id . ToString ()));
if ( this . Level > 1 )
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "parentID" , this . Parent . Id . ToString ()));
else
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "parentID" , "-1" ));
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "level" , this . Level . ToString ()));
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "writerID" , this . User . Id . ToString ()));
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "sortOrder" , this . sortOrder . ToString ()));
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "createDate" , this . CreateDateTime . ToString ( "s" )));
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "nodeName" , this . Text ));
x . Attributes . Append ( xmlHelper . addAttribute ( xd , "path" , this . Path ));
2010-02-10 14:00:47 +00:00
if ( Deep )
{
2009-10-06 13:33:09 +00:00
//store children array here because iterating over an Array property object is very inneficient.
var children = this . Children ;
foreach ( Content c in children )
2009-06-19 07:39:16 +00:00
x . AppendChild ( c . ToXml ( xd , true ));
}
}
2010-04-17 07:51:51 +00:00
#endregion
#region Events
2009-06-19 07:39:16 +00:00
/// <summary>
/// Calls the subscribers of a cancelable event handler,
/// stopping at the event handler which cancels the event (if any).
/// </summary>
/// <typeparam name="T">Type of the event arguments.</typeparam>
/// <param name="cancelableEvent">The event to fire.</param>
/// <param name="sender">Sender of the event.</param>
/// <param name="eventArgs">Event arguments.</param>
protected virtual void FireCancelableEvent < T >( EventHandler < T > cancelableEvent , object sender , T eventArgs ) where T : CancelEventArgs
{
if ( cancelableEvent != null )
{
foreach ( Delegate invocation in cancelableEvent . GetInvocationList ())
{
invocation . DynamicInvoke ( sender , eventArgs );
if ( eventArgs . Cancel )
break ;
}
}
}
/// <summary>
/// Occurs before a node is saved.
/// </summary>
public static event EventHandler < SaveEventArgs > BeforeSave ;
/// <summary>
/// Raises the <see cref="E:BeforeSave"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
2010-02-10 14:00:47 +00:00
protected virtual void FireBeforeSave ( SaveEventArgs e )
{
2009-06-19 07:39:16 +00:00
FireCancelableEvent ( BeforeSave , this , e );
}
/// <summary>
/// Occurs after a node is saved.
/// </summary>
public static event EventHandler < SaveEventArgs > AfterSave ;
/// <summary>
/// Raises the <see cref="E:AfterSave"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
2010-02-10 14:00:47 +00:00
protected virtual void FireAfterSave ( SaveEventArgs e )
{
if ( AfterSave != null )
2009-06-19 07:39:16 +00:00
AfterSave ( this , e );
}
/// <summary>
/// Occurs after a new node is created.
/// </summary>
public static event EventHandler < NewEventArgs > AfterNew ;
/// <summary>
/// Raises the <see cref="E:AfterNew"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
2010-02-10 14:00:47 +00:00
protected virtual void FireAfterNew ( NewEventArgs e )
{
2009-06-19 07:39:16 +00:00
if ( AfterNew != null )
AfterNew ( this , e );
}
/// <summary>
/// Occurs before a node is deleted.
/// </summary>
public static event EventHandler < DeleteEventArgs > BeforeDelete ;
/// <summary>
/// Raises the <see cref="E:BeforeDelete"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
2010-02-10 14:00:47 +00:00
protected virtual void FireBeforeDelete ( DeleteEventArgs e )
{
2009-06-19 07:39:16 +00:00
FireCancelableEvent ( BeforeDelete , this , e );
}
/// <summary>
/// Occurs after a node is deleted.
/// </summary>
public static event EventHandler < DeleteEventArgs > AfterDelete ;
/// <summary>
/// Raises the <see cref="E:AfterDelete"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
2010-02-10 14:00:47 +00:00
protected virtual void FireAfterDelete ( DeleteEventArgs e )
{
2009-06-19 07:39:16 +00:00
if ( AfterDelete != null )
AfterDelete ( this , e );
}
/// <summary>
/// Occurs before a node is moved.
/// </summary>
public static event EventHandler < MoveEventArgs > BeforeMove ;
/// <summary>
/// Raises the <see cref="E:BeforeMove"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
2010-02-10 14:00:47 +00:00
protected virtual void FireBeforeMove ( MoveEventArgs e )
{
2009-06-19 07:39:16 +00:00
FireCancelableEvent ( BeforeMove , this , e );
}
/// <summary>
/// Occurs after a node is moved.
/// </summary>
public static event EventHandler < MoveEventArgs > AfterMove ;
/// <summary>
/// Raises the <see cref="E:AfterMove"/> event.
/// </summary>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
2010-02-10 14:00:47 +00:00
protected virtual void FireAfterMove ( MoveEventArgs e )
{
2009-06-19 07:39:16 +00:00
if ( AfterMove != null )
AfterMove ( this , e );
}
2010-04-12 00:57:47 +00:00
2010-04-17 07:51:51 +00:00
#endregion
2009-06-19 07:39:16 +00:00
}
2010-04-17 07:51:51 +00:00
}