diff --git a/umbraco/cms/businesslogic/CMSNode.cs b/umbraco/cms/businesslogic/CMSNode.cs index de624c73a4..0d099fd145 100644 --- a/umbraco/cms/businesslogic/CMSNode.cs +++ b/umbraco/cms/businesslogic/CMSNode.cs @@ -805,7 +805,6 @@ order by level,sortOrder"; HasChildren = hasChildren; } - /// /// Updates the temp path for the content tree. /// diff --git a/umbraco/cms/businesslogic/Content.cs b/umbraco/cms/businesslogic/Content.cs index f8834a588c..99febce984 100644 --- a/umbraco/cms/businesslogic/Content.cs +++ b/umbraco/cms/businesslogic/Content.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; - +using System.Linq; using System.Data; using System.Xml; @@ -12,27 +12,31 @@ using umbraco.cms.helpers; namespace umbraco.cms.businesslogic { - /// - /// Content is an intermediate layer between CMSNode and class'es which will use generic data. - /// - /// Content is a datastructure that holds generic data defined in its corresponding ContentType. Content can in some - /// sence be compared to a row in a database table, it's contenttype hold a definition of the columns and the Content - /// contains the data - /// - /// Note that Content data in umbraco is *not* tablular but in a treestructure. - /// - /// - public class Content : CMSNode - { - private Guid _version; - private DateTime _versionDate; - private XmlNode _xml; - private bool _versionDateInitialized; + /// + /// Content is an intermediate layer between CMSNode and class'es which will use generic data. + /// + /// Content is a datastructure that holds generic data defined in its corresponding ContentType. Content can in some + /// sence be compared to a row in a database table, it's contenttype hold a definition of the columns and the Content + /// contains the data + /// + /// Note that Content data in umbraco is *not* tablular but in a treestructure. + /// + /// + public class Content : CMSNode + { + #region Private Members + + private Guid _version; + private DateTime _versionDate; + private XmlNode _xml; + private bool _versionDateInitialized; private string _contentTypeIcon; private ContentType _contentType; - private bool _propertiesInitialized = false; - private Properties _properties = new Properties(); + private Properties m_LoadedProperties = null; + #endregion + + #region Constructors /// /// /// @@ -51,51 +55,71 @@ namespace umbraco.cms.businesslogic /// /// protected Content(Guid id) : base(id) { } + #endregion - /// - /// Sets up the ContentType property for this content item and sets the addition content properties manually. - /// If the ContentType property is not already set, then this will get the ContentType from Cache. - /// - /// - /// - /// - /// - protected void InitializeContent(int InitContentType, Guid InitVersion, DateTime InitVersionDate, string InitContentTypeIcon) + #region Static Methods + + /// + /// Retrive a list of Content sharing the ContentType + /// + /// The ContentType + /// A list of Content objects sharing the ContentType defined. + public static Content[] getContentOfContentType(ContentType ct) { - if (_contentType == null) - _contentType = ContentType.GetContentType(InitContentType); - _version = InitVersion; - _versionDate = InitVersionDate; - _contentTypeIcon = InitContentTypeIcon; + IRecordsReader dr = SqlHelper.ExecuteReader("Select nodeId from cmsContent INNER JOIN umbracoNode ON cmsContent.nodeId = umbracoNode.id where ContentType = " + ct.Id + " ORDER BY umbracoNode.text "); + System.Collections.ArrayList tmp = new System.Collections.ArrayList(); + + while (dr.Read()) tmp.Add(dr.GetInt("nodeId")); + dr.Close(); + + Content[] retval = new Content[tmp.Count]; + for (int i = 0; i < tmp.Count; i++) retval[i] = new Content((int)tmp[i]); + + return retval; } - /// + /// + /// Initialize a contentobject given a version. + /// + /// The version identifier + /// The Content object from the given version + public static Content GetContentFromVersion(Guid version) + { + int tmpContentId = SqlHelper.ExecuteScalar("Select ContentId from cmsContentVersion where versionId = '" + version.ToString() + "'"); + return new Content(tmpContentId); + } + + #endregion + + #region Public Properties + + /// /// The current Content objects ContentType, which defines the Properties of the Content (data) /// public ContentType ContentType { get { - if (_contentType == null) - { - object o = SqlHelper.ExecuteScalar( - "Select ContentType from cmsContent where nodeId=@nodeid", - SqlHelper.CreateParameter("@nodeid", this.Id)); - if (o == null) - return null; - int contentTypeId; - if(!int.TryParse(o.ToString(), out contentTypeId)) - return null; - try - { - _contentType = new ContentType(contentTypeId); - } - catch - { - return null; - } - } - return _contentType; + if (_contentType == null) + { + object o = SqlHelper.ExecuteScalar( + "Select ContentType from cmsContent where nodeId=@nodeid", + SqlHelper.CreateParameter("@nodeid", this.Id)); + if (o == null) + return null; + int contentTypeId; + if (!int.TryParse(o.ToString(), out contentTypeId)) + return null; + try + { + _contentType = new ContentType(contentTypeId); + } + catch + { + return null; + } + } + return _contentType; } set { @@ -103,16 +127,130 @@ namespace umbraco.cms.businesslogic } } - public Properties GenericProperties - { + /// + /// The icon used in the tree - placed in this layer for performance reasons. + /// + /// + /// This is here for performance reasons only. If the _contentTypeIcon is manually set + /// then a database call is not made to initialize the ContentType. + /// + /// The data layer has slightly changed in 4.1 so that for Document and Media, the ContentType + /// is automatically initialized with one SQL call when creating the documents/medias so using this + /// method or the ContentType.IconUrl property when accessing the icon from Media or Document + /// won't affect performance. + /// + public string ContentTypeIcon + { get { - if (!_propertiesInitialized) - InitializeProperties(); - - return _properties; + if (_contentTypeIcon == null && this.ContentType != null) + _contentTypeIcon = this.ContentType.IconUrl; + return _contentTypeIcon; } - } + set + { + _contentTypeIcon = value; + } + } + + /// + /// The createtimestamp on this version + /// + public DateTime VersionDate + { + get + { + if (!_versionDateInitialized) + { + object o = SqlHelper.ExecuteScalar( + "select VersionDate from cmsContentVersion where versionId = '" + this.Version.ToString() + "'"); + if (o == null) + { + _versionDate = DateTime.Now; + } + else + { + _versionDateInitialized = DateTime.TryParse(o.ToString(), out _versionDate); + } + } + return _versionDate; + } + set + { + _versionDate = value; + _versionDateInitialized = true; + } + } + + /// + /// Retrieve a list of generic properties of the content + /// + public Properties GenericProperties + { + get + { + EnsureProperties(); + return m_LoadedProperties; + } + } + + /// + /// Retrieve a list of generic properties of the content + /// + [Obsolete("Use the GenericProperties property instead")] + public Property[] getProperties + { + get + { + //EnsureProperties(); + //return m_LoadedProperties.ToArray(); + + if (this.ContentType == null) + return new Property[0]; + + List result = new List(); + foreach (PropertyType prop in this.ContentType.PropertyTypes) + { + if (prop == null) + continue; + Property p = getProperty(prop); + if (p == null) + continue; + result.Add(p); + } + + return result.ToArray(); + } + } + + /// + /// Content is under version control, you are able to programatically create new versions + /// + public Guid Version + { + get + { + if (_version == Guid.Empty) + { + string sql = "Select versionId from cmsContentVersion where contentID = " + this.Id + + " order by id desc "; + + using (IRecordsReader dr = SqlHelper.ExecuteReader(sql)) + { + if (!dr.Read()) + _version = Guid.Empty; + else + _version = dr.GetGuid("versionId"); + } + } + return _version; + } + set { _version = value; } + } + + #endregion + + #region Public Methods /// /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility @@ -122,297 +260,81 @@ namespace umbraco.cms.businesslogic base.Save(); } - /// - /// The icon used in the tree - placed in this layer for performance reasons. - /// - /// - /// This is here for performance reasons only. If the _contentTypeIcon is manually set - /// then a database call is not made to initialize the ContentType. - /// - /// The data layer has slightly changed in 4.1 so that for Document and Media, the ContentType - /// is automatically initialized with one SQL call when creating the documents/medias so using this - /// method or the ContentType.IconUrl property when accessing the icon from Media or Document - /// won't affect performance. - /// - public string ContentTypeIcon - { - get - { - if (_contentTypeIcon == null && this.ContentType != null) - _contentTypeIcon = this.ContentType.IconUrl; - return _contentTypeIcon; - } - set - { - _contentTypeIcon = value; - } - } - - /// - /// Retrieve a Property given the alias - /// - /// Propertyalias (defined in the documenttype) - /// The property with the given alias - public Property getProperty(string alias) - { - ContentType ct = this.ContentType; - if(ct == null) - return null; - propertytype.PropertyType pt = ct.getPropertyType(alias); - if(pt == null) - return null; - return getProperty(pt); - } - - /// - /// Retrieve a property given the propertytype - /// - /// PropertyType - /// The property with the given propertytype - public Property getProperty(propertytype.PropertyType pt) - { - object o = SqlHelper.ExecuteScalar( - "select id from cmsPropertyData where versionId=@version and propertyTypeId=@propertyTypeId", - SqlHelper.CreateParameter("@version", this.Version), - SqlHelper.CreateParameter("@propertyTypeId", pt.Id)); - if(o == null) - return null; - int propertyId; - if(!int.TryParse(o.ToString(), out propertyId)) - return null; - try - { - return new Property(propertyId, pt); - } - catch - { - return null; - } - } - - /// - /// The createtimestamp on this version - /// - public DateTime VersionDate - { - get - { - if(!_versionDateInitialized) - { - object o = SqlHelper.ExecuteScalar( - "select VersionDate from cmsContentVersion where versionId = '" + this.Version.ToString() + "'"); - if(o == null) - { - _versionDate = DateTime.Now; - } - else - { - _versionDateInitialized = DateTime.TryParse(o.ToString(), out _versionDate); - } - } - return _versionDate; - } - set - { - _versionDate = value; - _versionDateInitialized = true; - } - } - - - /// - /// Optimized method for bulk deletion of properties´on a Content object. - /// - private void deleteAllProperties() - { - SqlHelper.ExecuteNonQuery("Delete from cmsPropertyData where contentNodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); - } - - /// - /// Retrieve a list of generic properties of the content - /// - public Property[] getProperties - { - get - { - if (this.ContentType == null) - return new Property[0]; - - List result = new List(); - foreach(PropertyType prop in this.ContentType.PropertyTypes) - { - if (prop == null) - continue; - Property p = getProperty(prop); - if(p == null) - continue; - result.Add(p); - } - - return result.ToArray(); - } - } - - - private void InitializeProperties() + /// + /// Retrieve a Property given the alias + /// + /// Propertyalias (defined in the documenttype) + /// The property with the given alias + public Property getProperty(string alias) { - using(IRecordsReader dr = SqlHelper.ExecuteReader("select id from cmsPropertyData where versionId = @versionId", - SqlHelper.CreateParameter("@versionId", Version))) - { - while(dr.Read()) - _properties.Add(new Property(dr.GetInt("id"))); - } + ContentType ct = this.ContentType; + if (ct == null) + return null; + propertytype.PropertyType pt = ct.getPropertyType(alias); + if (pt == null) + return null; + return getProperty(pt); } + /// + /// Retrieve a property given the propertytype + /// + /// PropertyType + /// The property with the given propertytype + public Property getProperty(PropertyType pt) + { - /// - /// Retrive a list of Content sharing the ContentType - /// - /// The ContentType - /// A list of Content objects sharing the ContentType defined. - public static Content[] getContentOfContentType(ContentType ct) { - IRecordsReader dr = SqlHelper.ExecuteReader("Select nodeId from cmsContent INNER JOIN umbracoNode ON cmsContent.nodeId = umbracoNode.id where ContentType = " + ct.Id + " ORDER BY umbracoNode.text "); - System.Collections.ArrayList tmp = new System.Collections.ArrayList(); - - while (dr.Read()) tmp.Add(dr.GetInt("nodeId")); - dr.Close(); - - Content[] retval = new Content[tmp.Count]; - for (int i = 0;i < tmp.Count; i++) retval[i] = new Content((int) tmp[i]); - - return retval; - } - - /// - /// Add a property to the Content - /// - /// The PropertyType of the Property - /// The version of the document on which the property should be add'ed - /// The new Property - public property.Property addProperty(propertytype.PropertyType pt, Guid versionId) { - return property.Property.MakeNew(pt, this, versionId); - } - - /// - /// Content is under version control, you are able to programatically create new versions - /// - public Guid Version { - get{ - if (_version == Guid.Empty) - { - string sql = "Select versionId from cmsContentVersion where contentID = " + this.Id + - " order by id desc "; - - using (IRecordsReader dr = SqlHelper.ExecuteReader(sql)) - { - if (!dr.Read()) - _version = Guid.Empty; - else - _version = dr.GetGuid("versionId"); - } - } - return _version; - } - set{_version = value;} - } - - /// - /// Creates a new Content object from the ContentType. - /// - /// - protected void CreateContent(ContentType ct) { - SqlHelper.ExecuteNonQuery("insert into cmsContent (nodeId,ContentType) values ("+this.Id+","+ct.Id+")"); - createNewVersion(); - } - - /// - /// Indication if the Content exists in at least one version. - /// - /// Returns true if the Content has a version - private bool hasVersion() - { - int versionCount = SqlHelper.ExecuteScalar("select Count(Id) as tmp from cmsContentVersion where contentId = " + this.Id.ToString()); - return (versionCount > 0); - } - - /// - /// Method for creating a new version of the data associated to the Content. - /// - /// - /// The new version Id - protected Guid createNewVersion() { - Guid newVersion = Guid.NewGuid(); - bool tempHasVersion = hasVersion(); - foreach (propertytype.PropertyType pt in this.ContentType.PropertyTypes) - { - object oldValue = ""; - if (tempHasVersion) - { - try - { - oldValue = this.getProperty(pt.Alias).Value; - } - catch {} - } - property.Property p = this.addProperty(pt, newVersion); - if (oldValue != null && oldValue.ToString() != "") p.Value = oldValue; - } - SqlHelper.ExecuteNonQuery("Insert into cmsContentVersion (ContentId,versionId) values ("+ this.Id +",'"+newVersion+"')"); - this.Version = newVersion; - return newVersion; - } - - /// - /// Deletes the current Content object, must be overridden in the child class. - /// - protected new void delete() { - - // Delete all data associated with this content - this.deleteAllProperties(); - - // Delete version history - SqlHelper.ExecuteNonQuery("Delete from cmsContentVersion where ContentId = " + this.Id); - - // Delete Contentspecific data () - SqlHelper.ExecuteNonQuery("Delete from cmsContent where NodeId = " + this.Id); - - // Delete xml - SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeID = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); - - // Delete Nodeinformation!! - base.delete(); - } - - /// - /// Initialize a contentobject given a version. - /// - /// The version identifier - /// The Content object from the given version - public static Content GetContentFromVersion(Guid version) { - int tmpContentId = SqlHelper.ExecuteScalar("Select ContentId from cmsContentVersion where versionId = '" + version.ToString() + "'"); - return new Content(tmpContentId); - } + object o = SqlHelper.ExecuteScalar( + "select id from cmsPropertyData where versionId=@version and propertyTypeId=@propertyTypeId", + SqlHelper.CreateParameter("@version", this.Version), + SqlHelper.CreateParameter("@propertyTypeId", pt.Id)); + if (o == null) + return null; + int propertyId; + if (!int.TryParse(o.ToString(), out propertyId)) + return null; + try + { + return new Property(propertyId, pt); + } + catch + { + return null; + } - private XmlNode importXml() - { - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.Load(SqlHelper.ExecuteXmlReader("select xml from cmsContentXml where nodeID = " + this.Id.ToString())); - return xmlDoc.FirstChild; - } + //EnsureProperties(); - /// - /// An Xmlrepresentation of a Content object. - /// - /// Xmldocument context - /// If true, the Contents children are appended to the Xmlnode recursive - /// The Xmlrepresentation of the data on the Content object - public new virtual XmlNode ToXml(XmlDocument xd, bool Deep) - { - if (_xml == null) - { - XmlDocument xmlDoc = new XmlDocument(); + //var prop = m_LoadedProperties + // .Where(x => x.PropertyType.Id == pt.Id) + // .SingleOrDefault(); + //return prop; + + } + + /// + /// Add a property to the Content + /// + /// The PropertyType of the Property + /// The version of the document on which the property should be add'ed + /// The new Property + public Property addProperty(PropertyType pt, Guid versionId) + { + return property.Property.MakeNew(pt, this, versionId); + } + + /// + /// An Xmlrepresentation of a Content object. + /// + /// Xmldocument context + /// If true, the Contents children are appended to the Xmlnode recursive + /// The Xmlrepresentation of the data on the Content object + public override XmlNode ToXml(XmlDocument xd, bool Deep) + { + if (_xml == null) + { + XmlDocument xmlDoc = new XmlDocument(); // we add a try/catch clause here, as the xmlreader will throw an exception if there's no xml in the table // after the empty catch we'll generate the xml which is why we don't do anything in the catch part try @@ -430,19 +352,19 @@ namespace umbraco.cms.businesslogic } - // Generate xml if xml still null (then it hasn't been initialized before) - if (_xml == null) - { - this.XmlGenerate(new XmlDocument()); - _xml = importXml(); - } + // Generate xml if xml still null (then it hasn't been initialized before) + if (_xml == null) + { + this.XmlGenerate(new XmlDocument()); + _xml = importXml(); + } - } + } - XmlNode x = xd.ImportNode(_xml, true); + XmlNode x = xd.ImportNode(_xml, true); - if (Deep) - { + if (Deep) + { //store children array here because iterating over an Array object is very inneficient. var childs = this.Children; foreach (BusinessLogic.console.IconI c in childs) @@ -455,30 +377,148 @@ namespace umbraco.cms.businesslogic { System.Web.HttpContext.Current.Trace.Warn("Content", "Error adding node to xml: " + mExp.ToString()); } - } - } + } + } - return x; + return x; - } + } - /// - /// Removes the Xml cached in the database - unpublish and cleaning - /// - public virtual void XmlRemoveFromDB() - { - SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); - } + /// + /// Removes the Xml cached in the database - unpublish and cleaning + /// + public virtual void XmlRemoveFromDB() + { + SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); + } - /// - /// Generates the Content XmlNode - /// - /// - public virtual void XmlGenerate(XmlDocument xd) - { + /// + /// Generates the Content XmlNode + /// + /// + public virtual void XmlGenerate(XmlDocument xd) + { XmlNode node = generateXmlWithoutSaving(xd); SaveXmlDocument(node); - } + } + + public virtual void XmlPopulate(XmlDocument xd, ref XmlNode x, bool Deep) + { + var props = this.getProperties; + foreach (property.Property p in props) + if (p != null) + x.AppendChild(p.ToXml(xd)); + + // attributes + x.Attributes.Append(xmlHelper.addAttribute(xd, "id", this.Id.ToString())); + x.Attributes.Append(xmlHelper.addAttribute(xd, "version", this.Version.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())); + if (this.ContentType != null) + x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeType", this.ContentType.Id.ToString())); + x.Attributes.Append(xmlHelper.addAttribute(xd, "template", "0")); + 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, "updateDate", this.VersionDate.ToString("s"))); + x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeName", this.Text)); + if (this.Text != null) + x.Attributes.Append(xmlHelper.addAttribute(xd, "urlName", this.Text.Replace(" ", "").ToLower())); + x.Attributes.Append(xmlHelper.addAttribute(xd, "writerName", this.User.Name)); + if (this.ContentType != null) + x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeTypeAlias", this.ContentType.Alias)); + 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 Protected Methods + /// + /// Sets up the ContentType property for this content item and sets the addition content properties manually. + /// If the ContentType property is not already set, then this will get the ContentType from Cache. + /// + /// + /// + /// + /// + protected void InitializeContent(int InitContentType, Guid InitVersion, DateTime InitVersionDate, string InitContentTypeIcon) + { + if (_contentType == null) + _contentType = ContentType.GetContentType(InitContentType); + _version = InitVersion; + _versionDate = InitVersionDate; + _contentTypeIcon = InitContentTypeIcon; + } + + /// + /// Creates a new Content object from the ContentType. + /// + /// + protected void CreateContent(ContentType ct) + { + SqlHelper.ExecuteNonQuery("insert into cmsContent (nodeId,ContentType) values (" + this.Id + "," + ct.Id + ")"); + createNewVersion(); + } + + /// + /// Method for creating a new version of the data associated to the Content. + /// + /// + /// The new version Id + protected Guid createNewVersion() + { + Guid newVersion = Guid.NewGuid(); + bool tempHasVersion = hasVersion(); + foreach (propertytype.PropertyType pt in this.ContentType.PropertyTypes) + { + object oldValue = ""; + if (tempHasVersion) + { + try + { + oldValue = this.getProperty(pt.Alias).Value; + } + catch { } + } + property.Property p = this.addProperty(pt, newVersion); + if (oldValue != null && oldValue.ToString() != "") p.Value = oldValue; + } + SqlHelper.ExecuteNonQuery("Insert into cmsContentVersion (ContentId,versionId) values (" + this.Id + ",'" + newVersion + "')"); + this.Version = newVersion; + return newVersion; + } + + /// + /// Deletes the current Content object, must be overridden in the child class. + /// + protected new void delete() + { + + // Delete all data associated with this content + this.deleteAllProperties(); + + // Delete version history + SqlHelper.ExecuteNonQuery("Delete from cmsContentVersion where ContentId = " + this.Id); + + // Delete Contentspecific data () + SqlHelper.ExecuteNonQuery("Delete from cmsContent where NodeId = " + this.Id); + + // Delete xml + SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeID = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); + + // Delete Nodeinformation!! + base.delete(); + } protected virtual XmlNode generateXmlWithoutSaving(XmlDocument xd) { @@ -488,46 +528,6 @@ namespace umbraco.cms.businesslogic return x; } - - public virtual void XmlPopulate(XmlDocument xd, ref XmlNode x, bool Deep) - { - var props = this.getProperties; - foreach (property.Property p in props) - if (p != null) - x.AppendChild(p.ToXml(xd)); - - // attributes - x.Attributes.Append(xmlHelper.addAttribute(xd, "id", this.Id.ToString())); - x.Attributes.Append(xmlHelper.addAttribute(xd, "version", this.Version.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())); - if (this.ContentType != null) - x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeType", this.ContentType.Id.ToString())); - x.Attributes.Append(xmlHelper.addAttribute( xd, "template", "0")); - 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, "updateDate", this.VersionDate.ToString("s"))); - x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeName", this.Text)); - if (this.Text != null) - x.Attributes.Append(xmlHelper.addAttribute(xd, "urlName", this.Text.Replace(" ", "").ToLower())); - x.Attributes.Append(xmlHelper.addAttribute(xd, "writerName", this.User.Name)); - if (this.ContentType != null) - x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeTypeAlias", this.ContentType.Alias)); - 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)); - } - } - /// /// Saves the XML document to the data source. /// @@ -548,6 +548,109 @@ namespace umbraco.cms.businesslogic SqlHelper.CreateParameter("@xml", node.OuterXml)); } + #endregion + + #region Private Methods + + /// + /// Makes sure that the properties are initialized. If they are already initialized, this does nothing. + /// + private void EnsureProperties() + { + if (m_LoadedProperties == null) + { + InitializeProperties(); + } + } + + /// + /// Loads all properties from database into objects. If this method is re-called, it will re-query the database. + /// + /// + /// This optimizes sql calls. This will first check if all of the properties have been loaded. If not, + /// then it will query for all property types for the current version from the db. It will then iterate over each + /// cmdPropertyData row and store the id and propertyTypeId in a list for lookup later. Once the records have been + /// read, we iterate over the cached property types for this ContentType and create a new property based on + /// our stored list of proeprtyTypeIds. We then have a cached list of Property objects which will get returned + /// on all subsequent calls and is also used to return a property with calls to getProperty. + /// + private void InitializeProperties() + { + m_LoadedProperties = new Properties(); + + if (this.ContentType == null) + return; + + //Create anonymous typed list with 2 props, Id and PropertyTypeId of type Int. + //This will still be an empty list since the props list is empty. + var propData = m_LoadedProperties.Select(x => new { Id = 0, PropertyTypeId = 0 }).ToList(); + + string sql = @"select id, propertyTypeId from cmsPropertyData where versionId=@versionId"; + + using (IRecordsReader dr = SqlHelper.ExecuteReader(sql, + SqlHelper.CreateParameter("@versionId", Version))) + { + while (dr.Read()) + { + //add the item to our list + propData.Add(new { Id = dr.Get("id"), PropertyTypeId = dr.Get("propertyTypeId") }); + } + } + + foreach (PropertyType pt in this.ContentType.PropertyTypes) + { + if (pt == null) + continue; + + //get the propertyId + var property = propData + .Where(x => x.PropertyTypeId == pt.Id) + .SingleOrDefault(); + if (property == null) + continue; + var propertyId = property.Id; + + Property p = null; + try + { + p = new Property(propertyId, pt); + } + catch + { + continue; //this remains from old code... not sure why we would do this? + } + + m_LoadedProperties.Add(p); + } + } + + /// + /// Optimized method for bulk deletion of properties´on a Content object. + /// + private void deleteAllProperties() + { + SqlHelper.ExecuteNonQuery("Delete from cmsPropertyData where contentNodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); + } + + private XmlNode importXml() + { + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(SqlHelper.ExecuteXmlReader("select xml from cmsContentXml where nodeID = " + this.Id.ToString())); + + return xmlDoc.FirstChild; + } + + /// + /// Indication if the Content exists in at least one version. + /// + /// Returns true if the Content has a version + private bool hasVersion() + { + int versionCount = SqlHelper.ExecuteScalar("select Count(Id) as tmp from cmsContentVersion where contentId = " + this.Id.ToString()); + return (versionCount > 0); + } + + #endregion #region XmlPreivew @@ -566,16 +669,5 @@ namespace umbraco.cms.businesslogic } #endregion - } - - /// - /// Not implemented - /// - public interface ISaveHandlerContents - { - /// - /// Not implemented - /// - bool Execute(cms.businesslogic.Content contentObject); - } + } } \ No newline at end of file diff --git a/umbraco/cms/businesslogic/ContentType.cs b/umbraco/cms/businesslogic/ContentType.cs index d4ae4ae78a..e7647475b6 100644 --- a/umbraco/cms/businesslogic/ContentType.cs +++ b/umbraco/cms/businesslogic/ContentType.cs @@ -85,11 +85,13 @@ namespace umbraco.cms.businesslogic #endregion + #region Constants private const string m_SQLOptimizedGetAll = @" SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text, masterContentType,Alias,icon,thumbnail,description FROM umbracoNode INNER JOIN cmsContentType ON umbracoNode.id = cmsContentType.nodeId - WHERE nodeObjectType = @nodeObjectType"; + WHERE nodeObjectType = @nodeObjectType"; + #endregion #region Static Methods @@ -181,7 +183,7 @@ namespace umbraco.cms.businesslogic #region Private Members - private bool _optimizedMode = false; + //private bool _optimizedMode = false; private string _alias; private string _iconurl; private string _description; @@ -195,9 +197,6 @@ namespace umbraco.cms.businesslogic #endregion - //private static Hashtable _analyzedContentTypes = new Hashtable(); - //private static Hashtable _optimizedContentTypes = new Hashtable(); - #region Public Properties /// @@ -238,15 +237,15 @@ namespace umbraco.cms.businesslogic } } - /// - /// Gets or sets a value indicating whether [optimized mode]. - /// - /// true if [optimized mode]; otherwise, false. - public bool OptimizedMode - { - get { return _optimizedMode; } - set { _optimizedMode = value; } - } + ///// + ///// Gets or sets a value indicating whether [optimized mode]. + ///// + ///// true if [optimized mode]; otherwise, false. + //public bool OptimizedMode + //{ + // get { return _optimizedMode; } + // set { _optimizedMode = value; } + //} /// /// Human readable name/label @@ -394,35 +393,7 @@ namespace umbraco.cms.businesslogic { get { - //optimize, lazy load the data only one time - if (m_VirtualTabs == null) - { - m_VirtualTabs = new List(); - using (IRecordsReader dr = SqlHelper.ExecuteReader( - string.Format( - "Select Id,text,sortOrder from cmsTab where contenttypeNodeId = {0} order by sortOrder", - Id))) - { - while (dr.Read()) - { - m_VirtualTabs.Add(new Tab(dr.GetInt("id"), dr.GetString("text"), dr.GetInt("sortOrder"), this)); - } - } - - // Master Content Type - if (MasterContentType != 0) - { - foreach (TabI t in ContentType.GetContentType(MasterContentType).getVirtualTabs.ToList()) - { - m_VirtualTabs.Add(t); - } - } - - // sort all tabs - m_VirtualTabs.Sort((a, b) => a.SortOrder.CompareTo(b.SortOrder)); - } - - + EnsureVirtualTabs(); return m_VirtualTabs.ToArray(); } } @@ -750,17 +721,72 @@ namespace umbraco.cms.businesslogic if (HttpRuntime.Cache[string.Format("ContentType_PropertyTypes_Content:{0}", Id.ToString())] != null) HttpRuntime.Cache.Remove(string.Format("ContentType_PropertyTypes_Content:{0}", Id.ToString())); + + ClearVirtualTabs(); } protected void FlushAllFromCache() { cache.Cache.ClearCacheByKeySearch("UmbracoContentType"); cache.Cache.ClearCacheByKeySearch("ContentType_PropertyTypes_Content"); + + ClearVirtualTabs(); } + #endregion #region Private Methods + /// + /// Clears the locally loaded tabs which forces them to be reloaded next time they requested + /// + private void ClearVirtualTabs() + { + m_VirtualTabs = null; + } + + /// + /// Checks if we've loaded the virtual tabs into memory and if not gets them from the databse. + /// + private void EnsureVirtualTabs() + { + //optimize, lazy load the data only one time + if (m_VirtualTabs == null) + { + InitializeVirtualTabs(); + } + } + + /// + /// Loads the tabs into memory from the database and stores them in a local list for retreival + /// + private void InitializeVirtualTabs() + { + m_VirtualTabs = new List(); + using (IRecordsReader dr = SqlHelper.ExecuteReader( + string.Format( + "Select Id,text,sortOrder from cmsTab where contenttypeNodeId = {0} order by sortOrder", + Id))) + { + while (dr.Read()) + { + m_VirtualTabs.Add(new Tab(dr.GetInt("id"), dr.GetString("text"), dr.GetInt("sortOrder"), this)); + } + } + + // Master Content Type + if (MasterContentType != 0) + { + foreach (TabI t in ContentType.GetContentType(MasterContentType).getVirtualTabs.ToList()) + { + m_VirtualTabs.Add(t); + } + } + + // sort all tabs + m_VirtualTabs.Sort((a, b) => a.SortOrder.CompareTo(b.SortOrder)); + } + private void PopulateContentTypeNodeFromReader(IRecordsReader dr) { _alias = dr.GetString("Alias"); diff --git a/umbraco/cms/businesslogic/ISaveHandlerContents.cs b/umbraco/cms/businesslogic/ISaveHandlerContents.cs new file mode 100644 index 0000000000..c1f495210d --- /dev/null +++ b/umbraco/cms/businesslogic/ISaveHandlerContents.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Xml; +using umbraco.cms.businesslogic.property; +using umbraco.cms.businesslogic.propertytype; +using umbraco.DataLayer; +using System.Runtime.CompilerServices; +using umbraco.cms.helpers; + +namespace umbraco.cms.businesslogic +{ + /// + /// Not implemented + /// + public interface ISaveHandlerContents + { + /// + /// Not implemented + /// + bool Execute(cms.businesslogic.Content contentObject); + } +} diff --git a/umbraco/cms/businesslogic/Property/Properties.cs b/umbraco/cms/businesslogic/Property/Properties.cs new file mode 100644 index 0000000000..8a683f45f4 --- /dev/null +++ b/umbraco/cms/businesslogic/Property/Properties.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Runtime.CompilerServices; +using System.Xml; +using umbraco.DataLayer; +using umbraco.BusinessLogic; + +namespace umbraco.cms.businesslogic.property +{ + public class Properties : List { } + +} diff --git a/umbraco/cms/businesslogic/Property/Property.cs b/umbraco/cms/businesslogic/Property/Property.cs index 8063836e82..23b51b4fc4 100644 --- a/umbraco/cms/businesslogic/Property/Property.cs +++ b/umbraco/cms/businesslogic/Property/Property.cs @@ -108,9 +108,4 @@ namespace umbraco.cms.businesslogic.property } } - public class Properties : List - { - - } - } \ No newline at end of file diff --git a/umbraco/cms/businesslogic/web/Document.cs b/umbraco/cms/businesslogic/web/Document.cs index 2203eb31a5..e9889d12c4 100644 --- a/umbraco/cms/businesslogic/web/Document.cs +++ b/umbraco/cms/businesslogic/web/Document.cs @@ -223,8 +223,8 @@ namespace umbraco.cms.businesslogic.web // special for tree performance private int _userId = -1; - private Dictionary _knownProperties = new Dictionary(); - private Func, string, bool> propertyTypeByAlias = (pt, alias) => pt.Key.PropertyType.Alias == alias; + //private Dictionary _knownProperties = new Dictionary(); + //private Func, string, bool> propertyTypeByAlias = (pt, alias) => pt.Key.PropertyType.Alias == alias; #endregion /// @@ -232,41 +232,41 @@ namespace umbraco.cms.businesslogic.web /// /// /// - public object this[string alias] - { - get - { - if (this._optimizedMode) - { - return this._knownProperties.Single(p => propertyTypeByAlias(p, alias)).Value; - } - else - { - return this.getProperty(alias).Value; - } - } - set - { - if (this._optimizedMode) - { - if (this._knownProperties.SingleOrDefault(p => propertyTypeByAlias(p, alias)).Key == null) - { - var pt = this.getProperty(alias); + //public object this[string alias] + //{ + // get + // { + // if (this._optimizedMode) + // { + // return this._knownProperties.Single(p => propertyTypeByAlias(p, alias)).Value; + // } + // else + // { + // return this.getProperty(alias).Value; + // } + // } + // set + // { + // if (this._optimizedMode) + // { + // if (this._knownProperties.SingleOrDefault(p => propertyTypeByAlias(p, alias)).Key == null) + // { + // var pt = this.getProperty(alias); - this._knownProperties.Add(pt, pt.Value); - } - else - { - var pt = this._knownProperties.Single(p => propertyTypeByAlias(p, alias)).Key; - this._knownProperties[pt] = value; - } - } - else - { - this.getProperty(alias).Value = value; - } - } - } + // this._knownProperties.Add(pt, pt.Value); + // } + // else + // { + // var pt = this._knownProperties.Single(p => propertyTypeByAlias(p, alias)).Key; + // this._knownProperties[pt] = value; + // } + // } + // else + // { + // this.getProperty(alias).Value = value; + // } + // } + //} /// /// Gets a value indicating whether the document was constructed for the optimized mode @@ -570,14 +570,14 @@ namespace umbraco.cms.businesslogic.web if (!e.Cancel) { - if (this._optimizedMode) - { - foreach (var property in this._knownProperties) - { - var pt = property.Key; - pt.Value = property.Value; - } - } + //if (this._optimizedMode) + //{ + // foreach (var property in this._knownProperties) + // { + // var pt = property.Key; + // pt.Value = property.Value; + // } + //} base.Save(); // update preview xml diff --git a/umbraco/cms/umbraco.cms.csproj b/umbraco/cms/umbraco.cms.csproj index 50a02676b1..5729b9c5b7 100644 --- a/umbraco/cms/umbraco.cms.csproj +++ b/umbraco/cms/umbraco.cms.csproj @@ -172,11 +172,13 @@ + True True PackageFiles.resx + diff --git a/umbraco/presentation/web.config.SHANDEMVAIO.xslt b/umbraco/presentation/web.config.SHANDEMVAIO.xslt index 192b891a82..9a8fe2197b 100644 --- a/umbraco/presentation/web.config.SHANDEMVAIO.xslt +++ b/umbraco/presentation/web.config.SHANDEMVAIO.xslt @@ -4,7 +4,7 @@ - server=.\sqlexpress;database=UmbracoTest5;user id=sa;password=test;Application Name=Umbraco41 + server=.\sqlexpress;database=UmbracoTest4;user id=sa;password=test;Application Name=Umbraco41