using System; using System.Collections.Generic; using System.Data; using System.Xml; using umbraco.cms.businesslogic.web; using umbraco.DataLayer; using umbraco.BusinessLogic; using System.IO; using System.Text.RegularExpressions; using System.ComponentModel; using umbraco.IO; using umbraco.cms.businesslogic.media; using System.Collections; using umbraco.cms.businesslogic.task; using umbraco.cms.businesslogic.workflow; using umbraco.cms.businesslogic.Tags; namespace umbraco.cms.businesslogic { /// /// 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). /// public class CMSNode : BusinessLogic.console.IconI { #region Private Members private string _text; private int _id = 0; private Guid _uniqueID; /// /// Private connectionstring /// protected static readonly string _ConnString = GlobalSettings.DbDSN; 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; private string m_image = "default.png"; private bool? _isTrashed = null; #endregion #region Private static private static readonly string m_DefaultIconCssFile = IOHelper.MapPath(SystemDirectories.Umbraco_client + "/Tree/treeIcons.css"); private static List m_DefaultIconClasses = new List(); private static void initializeIconClasses() { StreamReader re = File.OpenText(m_DefaultIconCssFile); string content = string.Empty; string input = null; while ((input = re.ReadLine()) != null) { content += input.Replace("\n", "") + "\n"; } re.Close(); // parse the classes MatchCollection m = Regex.Matches(content, "([^{]*){([^}]*)}", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); foreach (Match match in m) { GroupCollection groups = match.Groups; string cssClass = groups[1].Value.Replace("\n", "").Replace("\r", "").Trim().Trim(Environment.NewLine.ToCharArray()); if (!String.IsNullOrEmpty(cssClass)) { m_DefaultIconClasses.Add(cssClass); } } } 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},%'"; #endregion #region Public static /// /// Get a count on all CMSNodes given the objecttype /// /// The objecttype identifier /// /// The number of CMSNodes of the given objecttype /// public static int CountByObjectType(Guid objectType) { return SqlHelper.ExecuteScalar("SELECT COUNT(*) from umbracoNode WHERE nodeObjectType = @type", SqlHelper.CreateParameter("@type", objectType)); } /// /// Number of children of the current CMSNode /// /// The CMSNode Id /// /// The number of children from the given CMSNode /// public static int CountSubs(int Id) { return SqlHelper.ExecuteScalar("SELECT COUNT(*) FROM umbracoNode WHERE ','+path+',' LIKE '%," + Id.ToString() + ",%'"); } /// /// Gets the default icon classes. /// /// The default icon classes. public static List DefaultIconClasses { get { if (m_DefaultIconClasses.Count == 0) initializeIconClasses(); return m_DefaultIconClasses; } } /// /// Method for checking if a CMSNode exits with the given Guid /// /// Identifier /// True if there is a CMSNode with the given Guid public static bool IsNode(Guid uniqueID) { return (SqlHelper.ExecuteScalar("select count(id) from umbracoNode where uniqueID = @uniqueID", SqlHelper.CreateParameter("@uniqueId", uniqueID)) > 0); } /// /// Method for checking if a CMSNode exits with the given id /// /// Identifier /// True if there is a CMSNode with the given id public static bool IsNode(int Id) { return (SqlHelper.ExecuteScalar("select count(id) from umbracoNode where id = @id", SqlHelper.CreateParameter("@id", Id)) > 0); } /// /// Retrieve a list of the unique id's of all CMSNodes given the objecttype /// /// The objecttype identifier /// /// A list of all unique identifiers which each are associated to a CMSNode /// 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; } /// /// Retrieve a list of the node id's of all CMSNodes given the objecttype /// /// The objecttype identifier /// /// A list of all node ids which each are associated to a CMSNode /// 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 /// /// Retrieves the top level nodes in the hierarchy /// /// The Guid identifier of the type of objects /// /// A list of all top level nodes given the objecttype /// 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; } /// /// Given the protected modifier the CMSNode.MakeNew method can only be accessed by /// derived classes > who by definition knows of its own objectType. /// /// The parent CMSNode id /// The objecttype identifier /// Creator /// The level in the tree hieararchy /// The name of the CMSNode /// The unique identifier /// 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. 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)", 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), SqlHelper.CreateParameter("@text", text), SqlHelper.CreateParameter("@createDate", DateTime.Now)); CMSNode retVal = new CMSNode(uniqueID); retVal.Path = path + "," + retVal.Id.ToString(); //event NewEventArgs e = new NewEventArgs(); retVal.FireAfterNew(e); return retVal; } /// /// Retrieve a list of the id's of all CMSNodes given the objecttype and the first letter of the name. /// /// The objecttype identifier /// Firstletter /// /// A list of all CMSNodes which has the objecttype and a name that starts with the given letter /// 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 tmp = new List(); while (dr.Read()) tmp.Add(dr.GetInt("id")); return tmp.ToArray(); } } /// /// Gets the SQL helper. /// /// The SQL helper. protected static ISqlHelper SqlHelper { get { return Application.SqlHelper; } } #endregion #region Constructors /// /// Empty constructor that is not suported /// ...why is it here? /// public CMSNode() { throw new NotSupportedException(); } /// /// Initializes a new instance of the class. /// /// The id. public CMSNode(int Id) { _id = Id; setupNode(); } /// /// Initializes a new instance of the class. /// /// The id. /// if set to true [no setup]. public CMSNode(int id, bool noSetup) { _id = id; if (!noSetup) setupNode(); } /// /// Initializes a new instance of the class. /// /// The unique ID. public CMSNode(Guid uniqueID) { _id = SqlHelper.ExecuteScalar("SELECT id FROM umbracoNode WHERE uniqueID = @uniqueId", SqlHelper.CreateParameter("@uniqueId", uniqueID)); setupNode(); } public CMSNode(Guid uniqueID, bool noSetup) { _id = SqlHelper.ExecuteScalar("SELECT id FROM umbracoNode WHERE uniqueID = @uniqueId", SqlHelper.CreateParameter("@uniqueId", uniqueID)); if (!noSetup) setupNode(); } protected internal CMSNode(IRecordsReader reader) { _id = reader.GetInt("id"); PopulateCMSNodeFromReader(reader); } #endregion #region Public Methods /// /// Ensures uniqueness by id /// /// /// public override bool Equals(object obj) { var l = obj as CMSNode; if (l != null) { return this._id.Equals(l._id); } return false; } /// /// Ensures uniqueness by id /// /// public override int GetHashCode() { return _id.GetHashCode(); } /// /// An xml representation of the CMSNOde /// /// Xmldocument context /// If true the xml will append the CMSNodes child xml /// The CMSNode Xmlrepresentation 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)) { SavePreviewXml(ToXml(xd, false), UniqueId); } return GetPreviewXml(xd, UniqueId); } public virtual List GetNodesForPreview(bool childrenOnly) { List nodes = new List(); 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; } /// /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility /// public virtual void Save() { SaveEventArgs e = new SaveEventArgs(); this.FireBeforeSave(e); if (!e.Cancel) { //In the future there will be SQL stuff happening here... this.FireAfterSave(e); } } 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(); } private void Move(CMSNode parent) { MoveEventArgs e = new MoveEventArgs(); FireBeforeMove(e); if (!e.Cancel) { //first we need to establish if the node already exists under the parent node var isSameParent = (Path.Contains("," + parent.Id + ",")); //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) { int maxSortOrder = SqlHelper.ExecuteScalar("select coalesce(max(sortOrder),0) from umbracoNode where parentid = @parentId", SqlHelper.CreateParameter("@parentId", parent.Id)); this.Parent = parent; this.sortOrder = maxSortOrder + 1; } this.Parent = parent; this.Level = parent.Level + 1; this.Path = parent.Path + "," + this.Id.ToString(); //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 :( 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 } //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()); } var children = this.Children; foreach (CMSNode c in children) { c.Move(this); } FireAfterMove(e); } } /// /// Moves the CMSNode from the current position in the hierarchy to the target /// /// Target CMSNode id public void Move(int newParentId) { CMSNode parent = new CMSNode(newParentId); Move(parent); } /// /// Deletes this instance. /// public virtual void delete() { DeleteEventArgs e = new DeleteEventArgs(); FireBeforeDelete(e); if (!e.Cancel) { // remove relations var rels = Relations; foreach (relation.Relation rel in rels) { rel.Delete(); } //removes tasks foreach (Task t in Tasks) { t.Delete(); } //remove notifications Notification.DeleteNotifications(this); //remove permissions Permission.DeletePermissions(this); //removes tag associations (i know the key is set to cascade but do it anyways) Tag.RemoveTagsFromNode(this.Id); SqlHelper.ExecuteNonQuery("DELETE FROM umbracoNode WHERE uniqueID= @uniqueId", SqlHelper.CreateParameter("@uniqueId", _uniqueID)); FireAfterDelete(e); } } /// /// Does the current CMSNode have any child nodes. /// /// /// true if this instance has children; otherwise, false. /// public virtual bool HasChildren { get { if (!_hasChildrenInitialized) { int tmpChildrenCount = SqlHelper.ExecuteScalar("select count(id) from umbracoNode where ParentId = @id", SqlHelper.CreateParameter("@id", Id)); HasChildren = (tmpChildrenCount > 0); } return _hasChildren; } set { _hasChildrenInitialized = true; _hasChildren = value; } } /// /// Returns all descendant nodes from this node. /// /// /// /// 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. /// public virtual IEnumerable GetDescendants() { var descendants = new List(); 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; } #endregion #region Public properties /// /// 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) /// public bool IsTrashed { get { if (!_isTrashed.HasValue) { _isTrashed = Convert.ToBoolean(SqlHelper.ExecuteScalar("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)); } } /// /// Gets or sets the sort order. /// /// The sort order. public int sortOrder { get { return _sortOrder; } set { _sortOrder = value; SqlHelper.ExecuteNonQuery("update umbracoNode set sortOrder = '" + value + "' where id = " + this.Id.ToString()); } } /// /// Gets or sets the create date time. /// /// The create date time. public DateTime CreateDateTime { get { return _createDate; } set { _createDate = value; SqlHelper.ExecuteNonQuery("update umbracoNode set createDate = @createDate where id = " + this.Id.ToString(), SqlHelper.CreateParameter("@createDate", _createDate)); } } /// /// Gets the creator /// /// The user. public BusinessLogic.User User { get { return BusinessLogic.User.GetUser(_userId); } } /// /// Gets the id. /// /// The id. public int Id { get { return _id; } } /// /// Get the parent id of the node /// public int ParentId { get { return _parentid; } } /// /// Given the hierarchical tree structure a CMSNode has only one parent but can have many children /// /// The parent. public CMSNode Parent { get { if (Level == 1) throw new ArgumentException("No parent node"); return new CMSNode(_parentid); } set { _parentid = value.Id; SqlHelper.ExecuteNonQuery("update umbracoNode set parentId = " + value.Id.ToString() + " where id = " + this.Id.ToString()); } } /// /// An comma separated string consisting of integer node id's /// that indicates the path from the topmost node to the given node /// /// The path. public string Path { get { return _path; } set { _path = value; SqlHelper.ExecuteNonQuery("update umbracoNode set path = '" + _path + "' where id = " + this.Id.ToString()); } } /// /// Returns an integer value that indicates in which level of the /// tree structure the given node is /// /// The level. public int Level { get { return _level; } set { _level = value; SqlHelper.ExecuteNonQuery("update umbracoNode set level = " + _level.ToString() + " where id = " + this.Id.ToString()); } } /// /// 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. /// /// The type of the node object. public Guid nodeObjectType { get { return _nodeObjectType; } } /// /// Besides the hierarchy it's possible to relate one CMSNode to another, use this for alternative /// non-strict hierarchy /// /// The relations. public relation.Relation[] Relations { get { return relation.Relation.GetRelations(this.Id); } } /// /// Returns all tasks associated with this node /// public Tasks Tasks { get { return Task.GetTasks(this.Id); } } public virtual int ChildCount { get { return SqlHelper.ExecuteScalar("SELECT COUNT(*) FROM umbracoNode where ParentID = @parentId", SqlHelper.CreateParameter("@parentId", this.Id)); } } /// /// The basic recursive tree pattern /// /// The children. public virtual BusinessLogic.console.IconI[] Children { get { System.Collections.ArrayList tmp = new System.Collections.ArrayList(); 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", SqlHelper.CreateParameter("@type", this.nodeObjectType), SqlHelper.CreateParameter("ParentID", this.Id))) { while (dr.Read()) { tmp.Add(new CMSNode(dr)); } } CMSNode[] retval = new CMSNode[tmp.Count]; for (int i = 0; i < tmp.Count; i++) { retval[i] = (CMSNode)tmp[i]; } return retval; } } /// /// Retrieve all CMSNodes in the umbraco installation /// Use with care. /// /// The children of all object types. 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; } } #region IconI members // Unique identifier of the given node /// /// Unique identifier of the CMSNode, used when locating data. /// public Guid UniqueId { get { return _uniqueID; } } /// /// Human readable name/label /// public virtual string Text { get { return _text; } set { _text = value; SqlHelper.ExecuteNonQuery("UPDATE umbracoNode SET text = @text WHERE id = @id", SqlHelper.CreateParameter("@text", value.Trim()), SqlHelper.CreateParameter("@id", this.Id)); } } /// /// The menu items used in the tree view /// [Obsolete("this is not used anywhere")] public virtual BusinessLogic.console.MenuItemI[] MenuItems { get { return new BusinessLogic.console.MenuItemI[0]; } } /// /// Not implemented, always returns "about:blank" /// public virtual string DefaultEditorURL { get { return "about:blank"; } } /// /// The icon in the tree /// public virtual string Image { get { return m_image; } set { m_image = value; } } /// /// The "open/active" icon in the tree /// public virtual string OpenImage { get { return ""; } } #endregion #endregion #region Protected methods /// /// This allows inheritors to set the underlying text property without persisting the change to the database. /// /// protected void SetText(string txt) { _text = txt; } /// /// Sets up the internal data of the CMSNode, used by the various constructors /// protected virtual void setupNode() { using (IRecordsReader dr = SqlHelper.ExecuteReader(m_SQLSingle, SqlHelper.CreateParameter("@id",this.Id))) { if (dr.Read()) { PopulateCMSNodeFromReader(dr); } else { throw new ArgumentException(string.Format("No node exists with id '{0}'", Id)); } } } /// /// Sets up the node for the content tree, this makes no database calls, just sets the underlying properties /// /// The unique ID. /// Type of the node object. /// The level. /// The parent id. /// The user id. /// The path. /// The text. /// The create date. /// if set to true [has children]. 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; } /// /// Updates the temp path for the content tree. /// /// The path. protected void UpdateTempPathForTree(string Path) { this._path = Path; } protected virtual XmlNode GetPreviewXml(XmlDocument xd, Guid version) { XmlDocument xmlDoc = new XmlDocument(); using (XmlReader xmlRdr = SqlHelper.ExecuteXmlReader( "select xml from cmsPreviewXml where nodeID = @nodeId and versionId = @versionId", SqlHelper.CreateParameter("@nodeId", Id), SqlHelper.CreateParameter("@versionId", version))) { xmlDoc.Load(xmlRdr); } return xd.ImportNode(xmlDoc.FirstChild, true); } protected internal virtual bool PreviewExists(Guid versionId) { return (SqlHelper.ExecuteScalar("SELECT COUNT(nodeId) FROM cmsPreviewXml WHERE nodeId=@nodeId and versionId = @versionId", SqlHelper.CreateParameter("@nodeId", Id), SqlHelper.CreateParameter("@versionId", versionId)) != 0); } protected void SavePreviewXml(XmlNode x, Guid versionId) { 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)); } protected void PopulateCMSNodeFromReader(IRecordsReader dr) { // 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) 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"); _isTrashed = dr.GetBoolean("trashed"); } #endregion #region Private Methods private void XmlPopulate(XmlDocument xd, XmlNode x, bool Deep) { // 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)); if (Deep) { //store children array here because iterating over an Array property object is very inneficient. var children = this.Children; foreach (Content c in children) x.AppendChild(c.ToXml(xd, true)); } } #endregion #region Events /// /// Calls the subscribers of a cancelable event handler, /// stopping at the event handler which cancels the event (if any). /// /// Type of the event arguments. /// The event to fire. /// Sender of the event. /// Event arguments. protected virtual void FireCancelableEvent(EventHandler 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; } } } /// /// Occurs before a node is saved. /// public static event EventHandler BeforeSave; /// /// Raises the event. /// /// The instance containing the event data. protected virtual void FireBeforeSave(SaveEventArgs e) { FireCancelableEvent(BeforeSave, this, e); } /// /// Occurs after a node is saved. /// public static event EventHandler AfterSave; /// /// Raises the event. /// /// The instance containing the event data. protected virtual void FireAfterSave(SaveEventArgs e) { if (AfterSave != null) AfterSave(this, e); } /// /// Occurs after a new node is created. /// public static event EventHandler AfterNew; /// /// Raises the event. /// /// The instance containing the event data. protected virtual void FireAfterNew(NewEventArgs e) { if (AfterNew != null) AfterNew(this, e); } /// /// Occurs before a node is deleted. /// public static event EventHandler BeforeDelete; /// /// Raises the event. /// /// The instance containing the event data. protected virtual void FireBeforeDelete(DeleteEventArgs e) { FireCancelableEvent(BeforeDelete, this, e); } /// /// Occurs after a node is deleted. /// public static event EventHandler AfterDelete; /// /// Raises the event. /// /// The instance containing the event data. protected virtual void FireAfterDelete(DeleteEventArgs e) { if (AfterDelete != null) AfterDelete(this, e); } /// /// Occurs before a node is moved. /// public static event EventHandler BeforeMove; /// /// Raises the event. /// /// The instance containing the event data. protected virtual void FireBeforeMove(MoveEventArgs e) { FireCancelableEvent(BeforeMove, this, e); } /// /// Occurs after a node is moved. /// public static event EventHandler AfterMove; /// /// Raises the event. /// /// The instance containing the event data. protected virtual void FireAfterMove(MoveEventArgs e) { if (AfterMove != null) AfterMove(this, e); } #endregion } }