PageRenderTime 17ms CodeModel.GetById 1ms RepoModel.GetById 1ms app.codeStats 0ms

/umbraco_6f33e2b81175/umbraco/cms/businesslogic/Content.cs

https://github.com/jracabado/justEdit-
C# | 732 lines | 454 code | 97 blank | 181 comment | 64 complexity | e21e8c1a111630bcc880ce26f30890a5 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Data;
  5. using System.Xml;
  6. using umbraco.cms.businesslogic.property;
  7. using umbraco.cms.businesslogic.propertytype;
  8. using umbraco.DataLayer;
  9. using System.Runtime.CompilerServices;
  10. using umbraco.cms.helpers;
  11. using umbraco.BusinessLogic;
  12. using umbraco.interfaces;
  13. using System.IO;
  14. using umbraco.IO;
  15. using umbraco.cms.businesslogic.datatype.controls;
  16. namespace umbraco.cms.businesslogic
  17. {
  18. /// <summary>
  19. /// Content is an intermediate layer between CMSNode and class'es which will use generic data.
  20. ///
  21. /// Content is a datastructure that holds generic data defined in its corresponding ContentType. Content can in some
  22. /// sence be compared to a row in a database table, it's contenttype hold a definition of the columns and the Content
  23. /// contains the data
  24. ///
  25. /// Note that Content data in umbraco is *not* tablular but in a treestructure.
  26. ///
  27. /// </summary>
  28. public class Content : CMSNode
  29. {
  30. #region Private Members
  31. private Guid _version;
  32. private DateTime _versionDate;
  33. private XmlNode _xml;
  34. private bool _versionDateInitialized;
  35. private string _contentTypeIcon;
  36. private ContentType _contentType;
  37. private Properties m_LoadedProperties = null;
  38. #endregion
  39. #region Constructors
  40. public Content(int id) : base(id) { }
  41. protected Content(int id, bool noSetup) : base(id, noSetup) { }
  42. protected Content(Guid id) : base(id) { }
  43. protected Content(Guid id, bool noSetup) : base(id, noSetup) { }
  44. #endregion
  45. #region Static Methods
  46. /// <summary>
  47. /// Retrive a list of Content sharing the ContentType
  48. /// </summary>
  49. /// <param name="ct">The ContentType</param>
  50. /// <returns>A list of Content objects sharing the ContentType defined.</returns>
  51. public static Content[] getContentOfContentType(ContentType ct)
  52. {
  53. IRecordsReader dr = SqlHelper.ExecuteReader("Select nodeId from cmsContent INNER JOIN umbracoNode ON cmsContent.nodeId = umbracoNode.id where ContentType = " + ct.Id + " ORDER BY umbracoNode.text ");
  54. System.Collections.ArrayList tmp = new System.Collections.ArrayList();
  55. while (dr.Read()) tmp.Add(dr.GetInt("nodeId"));
  56. dr.Close();
  57. Content[] retval = new Content[tmp.Count];
  58. for (int i = 0; i < tmp.Count; i++) retval[i] = new Content((int)tmp[i]);
  59. return retval;
  60. }
  61. /// <summary>
  62. /// Initialize a contentobject given a version.
  63. /// </summary>
  64. /// <param name="version">The version identifier</param>
  65. /// <returns>The Content object from the given version</returns>
  66. public static Content GetContentFromVersion(Guid version)
  67. {
  68. int tmpContentId = SqlHelper.ExecuteScalar<int>("Select ContentId from cmsContentVersion where versionId = '" + version.ToString() + "'");
  69. return new Content(tmpContentId);
  70. }
  71. #endregion
  72. #region Public Properties
  73. /// <summary>
  74. /// The current Content objects ContentType, which defines the Properties of the Content (data)
  75. /// </summary>
  76. public ContentType ContentType
  77. {
  78. get
  79. {
  80. if (_contentType == null)
  81. {
  82. object o = SqlHelper.ExecuteScalar<object>(
  83. "Select ContentType from cmsContent where nodeId=@nodeid",
  84. SqlHelper.CreateParameter("@nodeid", this.Id));
  85. if (o == null)
  86. return null;
  87. int contentTypeId;
  88. if (!int.TryParse(o.ToString(), out contentTypeId))
  89. return null;
  90. try
  91. {
  92. _contentType = new ContentType(contentTypeId);
  93. }
  94. catch
  95. {
  96. return null;
  97. }
  98. }
  99. return _contentType;
  100. }
  101. set
  102. {
  103. _contentType = value;
  104. }
  105. }
  106. /// <summary>
  107. /// The icon used in the tree - placed in this layer for performance reasons.
  108. /// </summary>
  109. /// <remarks>
  110. /// This is here for performance reasons only. If the _contentTypeIcon is manually set
  111. /// then a database call is not made to initialize the ContentType.
  112. ///
  113. /// The data layer has slightly changed in 4.1 so that for Document and Media, the ContentType
  114. /// is automatically initialized with one SQL call when creating the documents/medias so using this
  115. /// method or the ContentType.IconUrl property when accessing the icon from Media or Document
  116. /// won't affect performance.
  117. /// </remarks>
  118. public string ContentTypeIcon
  119. {
  120. get
  121. {
  122. if (_contentTypeIcon == null && this.ContentType != null)
  123. _contentTypeIcon = this.ContentType.IconUrl;
  124. return _contentTypeIcon;
  125. }
  126. set
  127. {
  128. _contentTypeIcon = value;
  129. }
  130. }
  131. /// <summary>
  132. /// The createtimestamp on this version
  133. /// </summary>
  134. public DateTime VersionDate
  135. {
  136. get
  137. {
  138. if (!_versionDateInitialized)
  139. {
  140. object o = SqlHelper.ExecuteScalar<object>(
  141. "select VersionDate from cmsContentVersion where versionId = '" + this.Version.ToString() + "'");
  142. if (o == null)
  143. {
  144. _versionDate = DateTime.Now;
  145. }
  146. else
  147. {
  148. _versionDateInitialized = DateTime.TryParse(o.ToString(), out _versionDate);
  149. }
  150. }
  151. return _versionDate;
  152. }
  153. set
  154. {
  155. _versionDate = value;
  156. _versionDateInitialized = true;
  157. }
  158. }
  159. /// <summary>
  160. /// Retrieve a list of generic properties of the content
  161. /// </summary>
  162. public Properties GenericProperties
  163. {
  164. get
  165. {
  166. EnsureProperties();
  167. return m_LoadedProperties;
  168. }
  169. }
  170. /// <summary>
  171. /// Retrieve a list of generic properties of the content
  172. /// </summary>
  173. [Obsolete("Use the GenericProperties property instead")]
  174. public Property[] getProperties
  175. {
  176. get
  177. {
  178. EnsureProperties();
  179. return m_LoadedProperties.ToArray();
  180. //if (this.ContentType == null)
  181. // return new Property[0];
  182. //List<Property> result = new List<Property>();
  183. //foreach (PropertyType prop in this.ContentType.PropertyTypes)
  184. //{
  185. // if (prop == null)
  186. // continue;
  187. // Property p = getProperty(prop);
  188. // if (p == null)
  189. // continue;
  190. // result.Add(p);
  191. //}
  192. //return result.ToArray();
  193. }
  194. }
  195. /// <summary>
  196. /// Content is under version control, you are able to programatically create new versions
  197. /// </summary>
  198. public Guid Version
  199. {
  200. get
  201. {
  202. if (_version == Guid.Empty)
  203. {
  204. string sql = "Select versionId from cmsContentVersion where contentID = " + this.Id +
  205. " order by id desc ";
  206. using (IRecordsReader dr = SqlHelper.ExecuteReader(sql))
  207. {
  208. if (!dr.Read())
  209. _version = Guid.Empty;
  210. else
  211. _version = dr.GetGuid("versionId");
  212. }
  213. }
  214. return _version;
  215. }
  216. set { _version = value; }
  217. }
  218. #endregion
  219. #region Public Methods
  220. /// <summary>
  221. /// Used to persist object changes to the database. This ensures that the properties are re-loaded from the database.
  222. /// </summary>
  223. public override void Save()
  224. {
  225. base.Save();
  226. ClearLoadedProperties();
  227. }
  228. /// <summary>
  229. /// Retrieve a Property given the alias
  230. /// </summary>
  231. /// <param name="alias">Propertyalias (defined in the documenttype)</param>
  232. /// <returns>The property with the given alias</returns>
  233. public Property getProperty(string alias)
  234. {
  235. ContentType ct = this.ContentType;
  236. if (ct == null)
  237. return null;
  238. propertytype.PropertyType pt = ct.getPropertyType(alias);
  239. if (pt == null)
  240. return null;
  241. return getProperty(pt);
  242. }
  243. /// <summary>
  244. /// Retrieve a property given the propertytype
  245. /// </summary>
  246. /// <param name="pt">PropertyType</param>
  247. /// <returns>The property with the given propertytype</returns>
  248. public Property getProperty(PropertyType pt)
  249. {
  250. //object o = SqlHelper.ExecuteScalar<object>(
  251. // "select id from cmsPropertyData where versionId=@version and propertyTypeId=@propertyTypeId",
  252. // SqlHelper.CreateParameter("@version", this.Version),
  253. // SqlHelper.CreateParameter("@propertyTypeId", pt.Id));
  254. //if (o == null)
  255. // return null;
  256. //int propertyId;
  257. //if (!int.TryParse(o.ToString(), out propertyId))
  258. // return null;
  259. //try
  260. //{
  261. // return new Property(propertyId, pt);
  262. //}
  263. //catch (Exception ex)
  264. //{
  265. // Log.Add(LogTypes.Error, this.Id, "An error occurred retreiving property. EXCEPTION: " + ex.Message);
  266. // return null;
  267. //}
  268. EnsureProperties();
  269. var prop = m_LoadedProperties
  270. .Where(x => x.PropertyType.Id == pt.Id)
  271. .SingleOrDefault();
  272. return prop;
  273. }
  274. /// <summary>
  275. /// Add a property to the Content
  276. /// </summary>
  277. /// <param name="pt">The PropertyType of the Property</param>
  278. /// <param name="versionId">The version of the document on which the property should be add'ed</param>
  279. /// <returns>The new Property</returns>
  280. public Property addProperty(PropertyType pt, Guid versionId)
  281. {
  282. ClearLoadedProperties();
  283. return property.Property.MakeNew(pt, this, versionId);
  284. }
  285. /// <summary>
  286. /// An Xmlrepresentation of a Content object.
  287. /// </summary>
  288. /// <param name="xd">Xmldocument context</param>
  289. /// <param name="Deep">If true, the Contents children are appended to the Xmlnode recursive</param>
  290. /// <returns>The Xmlrepresentation of the data on the Content object</returns>
  291. public override XmlNode ToXml(XmlDocument xd, bool Deep)
  292. {
  293. if (_xml == null)
  294. {
  295. XmlDocument xmlDoc = new XmlDocument();
  296. // we add a try/catch clause here, as the xmlreader will throw an exception if there's no xml in the table
  297. // after the empty catch we'll generate the xml which is why we don't do anything in the catch part
  298. try
  299. {
  300. XmlReader xr = SqlHelper.ExecuteXmlReader("select xml from cmsContentXml where nodeID = " + this.Id.ToString());
  301. if (xr.MoveToContent() != System.Xml.XmlNodeType.None)
  302. {
  303. xmlDoc.Load(xr);
  304. _xml = xmlDoc.FirstChild;
  305. }
  306. xr.Close();
  307. }
  308. catch
  309. {
  310. }
  311. // Generate xml if xml still null (then it hasn't been initialized before)
  312. if (_xml == null)
  313. {
  314. this.XmlGenerate(new XmlDocument());
  315. _xml = importXml();
  316. }
  317. }
  318. XmlNode x = xd.ImportNode(_xml, true);
  319. if (Deep)
  320. {
  321. var childs = this.Children;
  322. foreach (BusinessLogic.console.IconI c in childs)
  323. {
  324. try
  325. {
  326. x.AppendChild(new Content(c.Id).ToXml(xd, true));
  327. }
  328. catch (Exception mExp)
  329. {
  330. System.Web.HttpContext.Current.Trace.Warn("Content", "Error adding node to xml: " + mExp.ToString());
  331. }
  332. }
  333. }
  334. return x;
  335. }
  336. /// <summary>
  337. /// Removes the Xml cached in the database - unpublish and cleaning
  338. /// </summary>
  339. public virtual void XmlRemoveFromDB()
  340. {
  341. SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
  342. }
  343. /// <summary>
  344. /// Generates the Content XmlNode
  345. /// </summary>
  346. /// <param name="xd"></param>
  347. public virtual void XmlGenerate(XmlDocument xd)
  348. {
  349. XmlNode node = generateXmlWithoutSaving(xd);
  350. SaveXmlDocument(node);
  351. }
  352. public virtual void XmlPopulate(XmlDocument xd, ref XmlNode x, bool Deep)
  353. {
  354. var props = this.getProperties;
  355. foreach (property.Property p in props)
  356. if (p != null)
  357. x.AppendChild(p.ToXml(xd));
  358. // attributes
  359. x.Attributes.Append(xmlHelper.addAttribute(xd, "id", this.Id.ToString()));
  360. x.Attributes.Append(xmlHelper.addAttribute(xd, "version", this.Version.ToString()));
  361. if (this.Level > 1)
  362. x.Attributes.Append(xmlHelper.addAttribute(xd, "parentID", this.Parent.Id.ToString()));
  363. else
  364. x.Attributes.Append(xmlHelper.addAttribute(xd, "parentID", "-1"));
  365. x.Attributes.Append(xmlHelper.addAttribute(xd, "level", this.Level.ToString()));
  366. x.Attributes.Append(xmlHelper.addAttribute(xd, "writerID", this.User.Id.ToString()));
  367. if (this.ContentType != null)
  368. x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeType", this.ContentType.Id.ToString()));
  369. x.Attributes.Append(xmlHelper.addAttribute(xd, "template", "0"));
  370. x.Attributes.Append(xmlHelper.addAttribute(xd, "sortOrder", this.sortOrder.ToString()));
  371. x.Attributes.Append(xmlHelper.addAttribute(xd, "createDate", this.CreateDateTime.ToString("s")));
  372. x.Attributes.Append(xmlHelper.addAttribute(xd, "updateDate", this.VersionDate.ToString("s")));
  373. x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeName", this.Text));
  374. if (this.Text != null)
  375. x.Attributes.Append(xmlHelper.addAttribute(xd, "urlName", this.Text.Replace(" ", "").ToLower()));
  376. x.Attributes.Append(xmlHelper.addAttribute(xd, "writerName", this.User.Name));
  377. if (this.ContentType != null)
  378. x.Attributes.Append(xmlHelper.addAttribute(xd, "nodeTypeAlias", this.ContentType.Alias));
  379. x.Attributes.Append(xmlHelper.addAttribute(xd, "path", this.Path));
  380. if (Deep)
  381. {
  382. //store children array here because iterating over an Array property object is very inneficient.
  383. var children = this.Children;
  384. foreach (Content c in children)
  385. x.AppendChild(c.ToXml(xd, true));
  386. }
  387. }
  388. /// <summary>
  389. /// Deletes the current Content object, must be overridden in the child class.
  390. /// </summary>
  391. public override void delete()
  392. {
  393. ClearLoadedProperties();
  394. // Delete all data associated with this content
  395. this.deleteAllProperties();
  396. // Remove all content preview xml
  397. SqlHelper.ExecuteNonQuery("delete from cmsPreviewXml where nodeId = " + Id);
  398. // Delete version history
  399. SqlHelper.ExecuteNonQuery("Delete from cmsContentVersion where ContentId = " + this.Id);
  400. // Delete xml
  401. SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeID = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
  402. // Delete Contentspecific data ()
  403. SqlHelper.ExecuteNonQuery("Delete from cmsContent where NodeId = " + this.Id);
  404. // Delete Nodeinformation!!
  405. base.delete();
  406. }
  407. #endregion
  408. #region Protected Methods
  409. /// <summary>
  410. /// Sets up the ContentType property for this content item and sets the addition content properties manually.
  411. /// If the ContentType property is not already set, then this will get the ContentType from Cache.
  412. /// </summary>
  413. /// <param name="InitContentType"></param>
  414. /// <param name="InitVersion"></param>
  415. /// <param name="InitVersionDate"></param>
  416. /// <param name="InitContentTypeIcon"></param>
  417. protected void InitializeContent(int InitContentType, Guid InitVersion, DateTime InitVersionDate, string InitContentTypeIcon)
  418. {
  419. ClearLoadedProperties();
  420. if (_contentType == null)
  421. _contentType = ContentType.GetContentType(InitContentType);
  422. _version = InitVersion;
  423. _versionDate = InitVersionDate;
  424. _contentTypeIcon = InitContentTypeIcon;
  425. }
  426. /// <summary>
  427. /// Creates a new Content object from the ContentType.
  428. /// </summary>
  429. /// <param name="ct"></param>
  430. protected void CreateContent(ContentType ct)
  431. {
  432. SqlHelper.ExecuteNonQuery("insert into cmsContent (nodeId,ContentType) values (" + this.Id + "," + ct.Id + ")");
  433. createNewVersion();
  434. }
  435. /// <summary>
  436. /// Method for creating a new version of the data associated to the Content.
  437. ///
  438. /// </summary>
  439. /// <returns>The new version Id</returns>
  440. protected Guid createNewVersion()
  441. {
  442. ClearLoadedProperties();
  443. Guid newVersion = Guid.NewGuid();
  444. bool tempHasVersion = hasVersion();
  445. foreach (propertytype.PropertyType pt in this.ContentType.PropertyTypes)
  446. {
  447. object oldValue = "";
  448. if (tempHasVersion)
  449. {
  450. try
  451. {
  452. oldValue = this.getProperty(pt.Alias).Value;
  453. }
  454. catch { }
  455. }
  456. property.Property p = this.addProperty(pt, newVersion);
  457. if (oldValue != null && oldValue.ToString() != "") p.Value = oldValue;
  458. }
  459. SqlHelper.ExecuteNonQuery("Insert into cmsContentVersion (ContentId,versionId) values (" + this.Id + ",'" + newVersion + "')");
  460. this.Version = newVersion;
  461. return newVersion;
  462. }
  463. protected virtual XmlNode generateXmlWithoutSaving(XmlDocument xd)
  464. {
  465. string nodeName = UmbracoSettings.UseLegacyXmlSchema ? "node" : Casing.SafeAliasWithForcingCheck(ContentType.Alias);
  466. XmlNode x = xd.CreateNode(XmlNodeType.Element, nodeName, "");
  467. XmlPopulate(xd, ref x, false);
  468. return x;
  469. }
  470. /// <summary>
  471. /// Saves the XML document to the data source.
  472. /// </summary>
  473. /// <param name="node">The XML Document.</param>
  474. [MethodImpl(MethodImplOptions.Synchronized)]
  475. protected virtual void SaveXmlDocument(XmlNode node)
  476. {
  477. // Method is synchronized so exists remains consistent (avoiding race condition)
  478. bool exists = SqlHelper.ExecuteScalar<int>("SELECT COUNT(nodeId) FROM cmsContentXml WHERE nodeId = @nodeId",
  479. SqlHelper.CreateParameter("@nodeId", Id)) > 0;
  480. string query;
  481. if (exists)
  482. query = "UPDATE cmsContentXml SET xml = @xml WHERE nodeId = @nodeId";
  483. else
  484. query = "INSERT INTO cmsContentXml(nodeId, xml) VALUES (@nodeId, @xml)";
  485. SqlHelper.ExecuteNonQuery(query,
  486. SqlHelper.CreateParameter("@nodeId", Id),
  487. SqlHelper.CreateParameter("@xml", node.OuterXml));
  488. }
  489. /// <summary>
  490. /// Deletes all files and the folder that have been saved with this content item which are based on the Upload data
  491. /// type. This is called when a media or content tree node is deleted.
  492. /// </summary>
  493. protected void DeleteAssociatedMediaFiles()
  494. {
  495. // Remove all files
  496. IDataType uploadField = new Factory().GetNewObject(new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c"));
  497. foreach (Property p in GenericProperties)
  498. {
  499. var isUploadField = false;
  500. try
  501. {
  502. if (p.PropertyType.DataTypeDefinition.DataType.Id == uploadField.Id
  503. && p.Value.ToString() != ""
  504. && File.Exists(IOHelper.MapPath(p.Value.ToString())))
  505. {
  506. isUploadField = true;
  507. }
  508. }
  509. catch (ArgumentException)
  510. {
  511. //the data type definition may not exist anymore at this point because another thread may
  512. //have deleted it.
  513. isUploadField = false;
  514. }
  515. if (isUploadField)
  516. {
  517. var fi = new FileInfo(IOHelper.MapPath(p.Value.ToString()));
  518. fi.Directory.GetFiles().ToList().ForEach(x =>
  519. {
  520. x.Delete();
  521. });
  522. fi.Directory.Delete(true);
  523. }
  524. }
  525. }
  526. #endregion
  527. #region Private Methods
  528. /// <summary>
  529. /// Clears the locally loaded properties which forces them to be reloaded next time they requested
  530. /// </summary>
  531. private void ClearLoadedProperties()
  532. {
  533. m_LoadedProperties = null;
  534. }
  535. /// <summary>
  536. /// Makes sure that the properties are initialized. If they are already initialized, this does nothing.
  537. /// </summary>
  538. private void EnsureProperties()
  539. {
  540. if (m_LoadedProperties == null)
  541. {
  542. InitializeProperties();
  543. }
  544. }
  545. /// <summary>
  546. /// Loads all properties from database into objects. If this method is re-called, it will re-query the database.
  547. /// </summary>
  548. /// <remarks>
  549. /// This optimizes sql calls. This will first check if all of the properties have been loaded. If not,
  550. /// then it will query for all property types for the current version from the db. It will then iterate over each
  551. /// cmdPropertyData row and store the id and propertyTypeId in a list for lookup later. Once the records have been
  552. /// read, we iterate over the cached property types for this ContentType and create a new property based on
  553. /// our stored list of proeprtyTypeIds. We then have a cached list of Property objects which will get returned
  554. /// on all subsequent calls and is also used to return a property with calls to getProperty.
  555. /// </remarks>
  556. private void InitializeProperties()
  557. {
  558. m_LoadedProperties = new Properties();
  559. if (this.ContentType == null)
  560. return;
  561. //Create anonymous typed list with 2 props, Id and PropertyTypeId of type Int.
  562. //This will still be an empty list since the props list is empty.
  563. var propData = m_LoadedProperties.Select(x => new { Id = 0, PropertyTypeId = 0 }).ToList();
  564. string sql = @"select id, propertyTypeId from cmsPropertyData where versionId=@versionId";
  565. using (IRecordsReader dr = SqlHelper.ExecuteReader(sql,
  566. SqlHelper.CreateParameter("@versionId", Version)))
  567. {
  568. while (dr.Read())
  569. {
  570. //add the item to our list
  571. propData.Add(new { Id = dr.Get<int>("id"), PropertyTypeId = dr.Get<int>("propertyTypeId") });
  572. }
  573. }
  574. foreach (PropertyType pt in this.ContentType.PropertyTypes)
  575. {
  576. if (pt == null)
  577. continue;
  578. //get the propertyId
  579. var property = propData
  580. .Where(x => x.PropertyTypeId == pt.Id)
  581. .SingleOrDefault();
  582. if (property == null)
  583. continue;
  584. var propertyId = property.Id;
  585. Property p = null;
  586. try
  587. {
  588. p = new Property(propertyId, pt);
  589. }
  590. catch
  591. {
  592. continue; //this remains from old code... not sure why we would do this?
  593. }
  594. m_LoadedProperties.Add(p);
  595. }
  596. }
  597. /// <summary>
  598. /// Optimized method for bulk deletion of properties´┐Żon a Content object.
  599. /// </summary>
  600. protected void deleteAllProperties()
  601. {
  602. SqlHelper.ExecuteNonQuery("Delete from cmsPropertyData where contentNodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
  603. }
  604. private XmlNode importXml()
  605. {
  606. XmlDocument xmlDoc = new XmlDocument();
  607. xmlDoc.Load(SqlHelper.ExecuteXmlReader("select xml from cmsContentXml where nodeID = " + this.Id.ToString()));
  608. return xmlDoc.FirstChild;
  609. }
  610. /// <summary>
  611. /// Indication if the Content exists in at least one version.
  612. /// </summary>
  613. /// <returns>Returns true if the Content has a version</returns>
  614. private bool hasVersion()
  615. {
  616. int versionCount = SqlHelper.ExecuteScalar<int>("select Count(Id) as tmp from cmsContentVersion where contentId = " + this.Id.ToString());
  617. return (versionCount > 0);
  618. }
  619. #endregion
  620. #region XmlPreivew
  621. public override XmlNode ToPreviewXml(XmlDocument xd)
  622. {
  623. if (!PreviewExists(Version))
  624. {
  625. saveXmlPreview(xd);
  626. }
  627. return GetPreviewXml(xd, Version);
  628. }
  629. private void saveXmlPreview(XmlDocument xd)
  630. {
  631. SavePreviewXml(generateXmlWithoutSaving(xd), Version);
  632. }
  633. #endregion
  634. }
  635. }