Files
Umbraco-CMS/umbraco/cms/businesslogic/Content.cs
slace eed74f864e DO NOT DOWNLOAD. DOWNLOAD LATEST STABLE FROM RELEASE TAB
support for a full cancelable event model on document

[TFS Changeset #56025]
2009-07-06 12:04:51 +00:00

535 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
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
{
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();
public Content()
{
throw new NotSupportedException();
}
/// <summary>
///
/// </summary>
/// <param name="id"></param>
public Content(int id) : base(id) { }
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="noSetup"></param>
protected Content(int id, bool noSetup) : base(id, noSetup) { }
/// <summary>
///
/// </summary>
/// <param name="id"></param>
protected Content(Guid id) : base(id) { }
protected void InitializeContent(int InitContentType, Guid InitVersion, DateTime InitVersionDate, string InitContentTypeIcon)
{
_contentType = ContentType.GetContentType(InitContentType);
_version = InitVersion;
_versionDate = InitVersionDate;
_contentTypeIcon = InitContentTypeIcon;
}
/// <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;
}
}
public Properties GenericProperties
{
get
{
if (!_propertiesInitialized)
InitializeProperties();
return _properties;
}
}
/// <summary>
/// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility
/// </summary>
public override void Save()
{
base.Save();
}
// This is for performance only (used in tree)
/// <summary>
/// The icon used in the tree - placed in this layer for performance reasons.
/// </summary>
public string ContentTypeIcon
{
get
{
if (_contentTypeIcon == null && this.ContentType != null)
_contentTypeIcon = this.ContentType.IconUrl;
return _contentTypeIcon;
}
set
{
_contentTypeIcon = value;
}
}
/// <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.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
{
return null;
}
}
/// <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>
/// Optimized method for bulk deletion of properties´on a Content object.
/// </summary>
private void deleteAllProperties()
{
SqlHelper.ExecuteNonQuery("Delete from cmsPropertyData where contentNodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
}
/// <summary>
/// Retrieve a list of generic properties of the content
/// </summary>
public Property[] getProperties
{
get
{
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();
}
}
private void InitializeProperties()
{
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")));
}
}
/// <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>
/// 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.Property addProperty(propertytype.PropertyType pt, Guid versionId) {
return property.Property.MakeNew(pt, this, versionId);
}
/// <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;}
}
/// <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>
/// 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);
}
/// <summary>
/// Method for creating a new version of the data associated to the Content.
///
/// </summary>
/// <returns>The new version Id</returns>
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;
}
/// <summary>
/// Deletes the current Content object, must be overridden in the child class.
/// </summary>
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();
}
/// <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);
}
private XmlNode importXml()
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(SqlHelper.ExecuteXmlReader("select xml from cmsContentXml where nodeID = " + this.Id.ToString()));
return xmlDoc.FirstChild;
}
/// <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 new virtual 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)
{
foreach(BusinessLogic.console.IconI c in this.Children)
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 = xd.CreateNode(XmlNodeType.Element, "node", String.Empty);
XmlPopulate(xd, ref node, false);
SaveXmlDocument(node);
}
public virtual void XmlPopulate(XmlDocument xd, ref XmlNode x, bool Deep)
{
foreach (property.Property p in this.getProperties)
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)
{
foreach(Content c in this.Children)
x.AppendChild(c.ToXml(xd, true));
}
}
/// <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>
/// Not implemented
/// </summary>
public interface ISaveHandlerContents
{
/// <summary>
/// Not implemented
/// </summary>
bool Execute(cms.businesslogic.Content contentObject);
}
}