/umbraco_6f33e2b81175/umbraco/cms/businesslogic/Content.cs
C# | 732 lines | 454 code | 97 blank | 181 comment | 64 complexity | e21e8c1a111630bcc880ce26f30890a5 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
-
- 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;
- using umbraco.BusinessLogic;
- using umbraco.interfaces;
- using System.IO;
- using umbraco.IO;
- using umbraco.cms.businesslogic.datatype.controls;
-
- namespace umbraco.cms.businesslogic
- {
- /// <summary>
- /// 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.
- ///
- /// </summary>
- 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 Properties m_LoadedProperties = null;
-
- #endregion
-
- #region Constructors
-
- public Content(int id) : base(id) { }
-
- protected Content(int id, bool noSetup) : base(id, noSetup) { }
-
- protected Content(Guid id) : base(id) { }
-
- protected Content(Guid id, bool noSetup) : base(id, noSetup) { }
-
- #endregion
-
- #region Static Methods
-
- /// <summary>
- /// Retrive a list of Content sharing the ContentType
- /// </summary>
- /// <param name="ct">The ContentType</param>
- /// <returns>A list of Content objects sharing the ContentType defined.</returns>
- 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;
- }
-
- /// <summary>
- /// Initialize a contentobject given a version.
- /// </summary>
- /// <param name="version">The version identifier</param>
- /// <returns>The Content object from the given version</returns>
- public static Content GetContentFromVersion(Guid version)
- {
- int tmpContentId = SqlHelper.ExecuteScalar<int>("Select ContentId from cmsContentVersion where versionId = '" + version.ToString() + "'");
- return new Content(tmpContentId);
- }
-
- #endregion
-
- #region Public Properties
-
- /// <summary>
- /// The current Content objects ContentType, which defines the Properties of the Content (data)
- /// </summary>
- public ContentType ContentType
- {
- get
- {
- if (_contentType == null)
- {
- object o = SqlHelper.ExecuteScalar<object>(
- "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
- {
- _contentType = value;
- }
- }
-
- /// <summary>
- /// The icon used in the tree - placed in this layer for performance reasons.
- /// </summary>
- /// <remarks>
- /// 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.
- /// </remarks>
- public string ContentTypeIcon
- {
- get
- {
- if (_contentTypeIcon == null && this.ContentType != null)
- _contentTypeIcon = this.ContentType.IconUrl;
- return _contentTypeIcon;
- }
- set
- {
- _contentTypeIcon = value;
- }
- }
-
- /// <summary>
- /// The createtimestamp on this version
- /// </summary>
- public DateTime VersionDate
- {
- get
- {
- if (!_versionDateInitialized)
- {
- object o = SqlHelper.ExecuteScalar<object>(
- "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;
- }
- }
-
- /// <summary>
- /// Retrieve a list of generic properties of the content
- /// </summary>
- public Properties GenericProperties
- {
- get
- {
- EnsureProperties();
- return m_LoadedProperties;
- }
- }
-
- /// <summary>
- /// Retrieve a list of generic properties of the content
- /// </summary>
- [Obsolete("Use the GenericProperties property instead")]
- public Property[] getProperties
- {
- get
- {
- EnsureProperties();
- return m_LoadedProperties.ToArray();
-
- //if (this.ContentType == null)
- // return new Property[0];
-
- //List<Property> result = new List<Property>();
- //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();
- }
- }
-
- /// <summary>
- /// Content is under version control, you are able to programatically create new versions
- /// </summary>
- 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
-
- /// <summary>
- /// Used to persist object changes to the database. This ensures that the properties are re-loaded from the database.
- /// </summary>
- public override void Save()
- {
- base.Save();
-
- ClearLoadedProperties();
- }
-
- /// <summary>
- /// Retrieve a Property given the alias
- /// </summary>
- /// <param name="alias">Propertyalias (defined in the documenttype)</param>
- /// <returns>The property with the given alias</returns>
- 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);
- }
-
- /// <summary>
- /// Retrieve a property given the propertytype
- /// </summary>
- /// <param name="pt">PropertyType</param>
- /// <returns>The property with the given propertytype</returns>
- public Property getProperty(PropertyType pt)
- {
-
- //object o = SqlHelper.ExecuteScalar<object>(
- // "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 (Exception ex)
- //{
- // Log.Add(LogTypes.Error, this.Id, "An error occurred retreiving property. EXCEPTION: " + ex.Message);
- // return null;
- //}
-
- EnsureProperties();
-
- var prop = m_LoadedProperties
- .Where(x => x.PropertyType.Id == pt.Id)
- .SingleOrDefault();
- return prop;
-
- }
-
- /// <summary>
- /// Add a property to the Content
- /// </summary>
- /// <param name="pt">The PropertyType of the Property</param>
- /// <param name="versionId">The version of the document on which the property should be add'ed</param>
- /// <returns>The new Property</returns>
- public Property addProperty(PropertyType pt, Guid versionId)
- {
- ClearLoadedProperties();
-
- return property.Property.MakeNew(pt, this, versionId);
-
- }
-
- /// <summary>
- /// An Xmlrepresentation of a Content object.
- /// </summary>
- /// <param name="xd">Xmldocument context</param>
- /// <param name="Deep">If true, the Contents children are appended to the Xmlnode recursive</param>
- /// <returns>The Xmlrepresentation of the data on the Content object</returns>
- 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
- {
- XmlReader xr = SqlHelper.ExecuteXmlReader("select xml from cmsContentXml where nodeID = " + this.Id.ToString());
- if (xr.MoveToContent() != System.Xml.XmlNodeType.None)
- {
- xmlDoc.Load(xr);
- _xml = xmlDoc.FirstChild;
- }
- xr.Close();
- }
- catch
- {
- }
-
-
- // 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);
-
- if (Deep)
- {
- var childs = this.Children;
- foreach (BusinessLogic.console.IconI c in childs)
- {
- try
- {
- x.AppendChild(new Content(c.Id).ToXml(xd, true));
- }
- catch (Exception mExp)
- {
- System.Web.HttpContext.Current.Trace.Warn("Content", "Error adding node to xml: " + mExp.ToString());
- }
- }
- }
-
- return x;
-
- }
-
- /// <summary>
- /// Removes the Xml cached in the database - unpublish and cleaning
- /// </summary>
- public virtual void XmlRemoveFromDB()
- {
- SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
- }
-
- /// <summary>
- /// Generates the Content XmlNode
- /// </summary>
- /// <param name="xd"></param>
- 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));
- }
- }
-
- /// <summary>
- /// Deletes the current Content object, must be overridden in the child class.
- /// </summary>
- public override void delete()
- {
- ClearLoadedProperties();
-
- // Delete all data associated with this content
- this.deleteAllProperties();
-
- // Remove all content preview xml
- SqlHelper.ExecuteNonQuery("delete from cmsPreviewXml where nodeId = " + Id);
-
- // Delete version history
- SqlHelper.ExecuteNonQuery("Delete from cmsContentVersion where ContentId = " + this.Id);
-
- // Delete xml
- SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeID = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
-
- // Delete Contentspecific data ()
- SqlHelper.ExecuteNonQuery("Delete from cmsContent where NodeId = " + this.Id);
-
- // Delete Nodeinformation!!
- base.delete();
- }
-
- #endregion
-
- #region Protected Methods
-
- /// <summary>
- /// 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.
- /// </summary>
- /// <param name="InitContentType"></param>
- /// <param name="InitVersion"></param>
- /// <param name="InitVersionDate"></param>
- /// <param name="InitContentTypeIcon"></param>
- protected void InitializeContent(int InitContentType, Guid InitVersion, DateTime InitVersionDate, string InitContentTypeIcon)
- {
- ClearLoadedProperties();
-
- if (_contentType == null)
- _contentType = ContentType.GetContentType(InitContentType);
- _version = InitVersion;
- _versionDate = InitVersionDate;
- _contentTypeIcon = InitContentTypeIcon;
- }
-
- /// <summary>
- /// Creates a new Content object from the ContentType.
- /// </summary>
- /// <param name="ct"></param>
- protected void CreateContent(ContentType ct)
- {
- SqlHelper.ExecuteNonQuery("insert into cmsContent (nodeId,ContentType) values (" + this.Id + "," + ct.Id + ")");
- createNewVersion();
- }
-
-
-
- /// <summary>
- /// Method for creating a new version of the data associated to the Content.
- ///
- /// </summary>
- /// <returns>The new version Id</returns>
- protected Guid createNewVersion()
- {
- ClearLoadedProperties();
-
- 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;
- }
-
- protected virtual XmlNode generateXmlWithoutSaving(XmlDocument xd)
- {
- string nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : Casing.SafeAliasWithForcingCheck(ContentType.Alias);
- XmlNode x = xd.CreateNode(XmlNodeType.Element, nodeName, "");
- XmlPopulate(xd, ref x, false);
- return x;
- }
-
- /// <summary>
- /// Saves the XML document to the data source.
- /// </summary>
- /// <param name="node">The XML Document.</param>
- [MethodImpl(MethodImplOptions.Synchronized)]
- protected virtual void SaveXmlDocument(XmlNode node)
- {
- // Method is synchronized so exists remains consistent (avoiding race condition)
- bool exists = SqlHelper.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsContentXml WHERE nodeId = @nodeId",
- SqlHelper.CreateParameter("@nodeId", Id)) > 0;
- string query;
- if (exists)
- query = "UPDATE cmsContentXml SET xml = @xml WHERE nodeId = @nodeId";
- else
- query = "INSERT INTO cmsContentXml(nodeId, xml) VALUES (@nodeId, @xml)";
- SqlHelper.ExecuteNonQuery(query,
- SqlHelper.CreateParameter("@nodeId", Id),
- SqlHelper.CreateParameter("@xml", node.OuterXml));
- }
-
- /// <summary>
- /// Deletes all files and the folder that have been saved with this content item which are based on the Upload data
- /// type. This is called when a media or content tree node is deleted.
- /// </summary>
- protected void DeleteAssociatedMediaFiles()
- {
- // Remove all files
- IDataType uploadField = new Factory().GetNewObject(new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c"));
-
- foreach (Property p in GenericProperties)
- {
- var isUploadField = false;
- try
- {
- if (p.PropertyType.DataTypeDefinition.DataType.Id == uploadField.Id
- && p.Value.ToString() != ""
- && File.Exists(IOHelper.MapPath(p.Value.ToString())))
- {
- isUploadField = true;
- }
- }
- catch (ArgumentException)
- {
- //the data type definition may not exist anymore at this point because another thread may
- //have deleted it.
- isUploadField = false;
- }
- if (isUploadField)
- {
- var fi = new FileInfo(IOHelper.MapPath(p.Value.ToString()));
-
- fi.Directory.GetFiles().ToList().ForEach(x =>
- {
- x.Delete();
- });
- fi.Directory.Delete(true);
- }
- }
- }
-
- #endregion
-
- #region Private Methods
-
- /// <summary>
- /// Clears the locally loaded properties which forces them to be reloaded next time they requested
- /// </summary>
- private void ClearLoadedProperties()
- {
- m_LoadedProperties = null;
- }
-
- /// <summary>
- /// Makes sure that the properties are initialized. If they are already initialized, this does nothing.
- /// </summary>
- private void EnsureProperties()
- {
- if (m_LoadedProperties == null)
- {
- InitializeProperties();
- }
- }
-
- /// <summary>
- /// Loads all properties from database into objects. If this method is re-called, it will re-query the database.
- /// </summary>
- /// <remarks>
- /// 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.
- /// </remarks>
- 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<int>("id"), PropertyTypeId = dr.Get<int>("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);
- }
- }
-
- /// <summary>
- /// Optimized method for bulk deletion of properties´on a Content object.
- /// </summary>
- protected 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;
- }
-
- /// <summary>
- /// Indication if the Content exists in at least one version.
- /// </summary>
- /// <returns>Returns true if the Content has a version</returns>
- private bool hasVersion()
- {
- int versionCount = SqlHelper.ExecuteScalar<int>("select Count(Id) as tmp from cmsContentVersion where contentId = " + this.Id.ToString());
- return (versionCount > 0);
- }
-
- #endregion
-
- #region XmlPreivew
-
- public override XmlNode ToPreviewXml(XmlDocument xd)
- {
- if (!PreviewExists(Version))
- {
- saveXmlPreview(xd);
- }
- return GetPreviewXml(xd, Version);
- }
-
- private void saveXmlPreview(XmlDocument xd)
- {
- SavePreviewXml(generateXmlWithoutSaving(xd), Version);
- }
-
- #endregion
- }
- }