/// <changelog>
/// <item who="Esben" when="18. november 2006">Rewrote</item>
/// </changelog>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using umbraco.BusinessLogic;
using umbraco.BusinessLogic.Actions;
using umbraco.cms.businesslogic.cache;
using umbraco.cms.businesslogic.web;
using umbraco.DataLayer;
namespace umbraco{
/// <summary>
/// Handles umbraco content
/// </summary>
public class content
{
#region Declarations
private readonly string UmbracoXmlDiskCacheFileName = HttpRuntime.AppDomainAppPath +
GlobalSettings.ContentXML.Replace('/', '\\').TrimStart(
'\\');
private readonly string XmlContextContentItemKey = "UmbracoXmlContextContent";
// Current content
private volatile XmlDocument _xmlContent = null;
// Sync access to disk file
private object _readerWriterSyncLock = new object();
// Sync access to internal cache
private object _xmlContentInternalSyncLock = new object();
// Sync database access
private object _dbReadSyncLock = new object();
#endregion
#region Constructors
public content()
{
;
}
static content()
{
Trace.Write("Initializing content");
ThreadPool.QueueUserWorkItem(
delegate
{
XmlDocument xmlDoc = Instance.XmlContentInternal;
Trace.WriteLine("Content initialized");
});
}
#endregion
#region Singleton
public static content Instance
{
get { return Singleton<content>.Instance; }
}
#endregion
#region Properties
/// <summary>
/// Get content. First call to this property will initialize xmldoc
/// subsequent calls will be blocked until initialization is done
/// Further we cache(in context) xmlContent for each request to ensure that
/// we always have the same XmlDoc throughout the whole request.
/// Note that context cache does not need to be locked, because all access
/// to it is done from a web request which runs in a single thread
/// </summary>
public virtual XmlDocument XmlContent
{
get
{
if (HttpContext.Current == null)
return XmlContentInternal;
XmlDocument content = HttpContext.Current.Items[XmlContextContentItemKey] as XmlDocument;
if (content == null)
{
content = XmlContentInternal;
HttpContext.Current.Items[XmlContextContentItemKey] = content;
}
return content;
}
}
[Obsolete("Please use: content.Instance.XmlContent")]
public static XmlDocument xmlContent
{
get { return Instance.XmlContent; }
}
public virtual bool isInitializing
{
get { return _xmlContent == null; }
}
/// <summary>
/// Internal reference to XmlContent
/// </summary>
protected virtual XmlDocument XmlContentInternal
{
get
{
if (isInitializing)
{
lock (_xmlContentInternalSyncLock)
{
if (isInitializing)
{
_xmlContent = LoadContent();
if (!UmbracoSettings.isXmlContentCacheDisabled && !IsValidDiskCachePresent())
SaveContentToDiskAsync(_xmlContent);
}
}
}
return _xmlContent;
}
set
{
lock (_xmlContentInternalSyncLock)
{
// Clear macro cache
Cache.ClearCacheObjectTypes("umbraco.MacroCacheContent");
requestHandler.ClearProcessedRequests();
_xmlContent = value;
if (!UmbracoSettings.isXmlContentCacheDisabled && UmbracoSettings.continouslyUpdateXmlDiskCache)
SaveContentToDiskAsync(_xmlContent);
else
// Clear cache...
ClearDiskCacheAsync();
}
}
}
#endregion
protected static ISqlHelper SqlHelper
{
get { return Application.SqlHelper; }
}
#region Public Methods
/// <summary>
/// Load content from database in a background thread
/// Replaces active content when done.
/// </summary>
public virtual void RefreshContentFromDatabaseAsync()
{
cms.businesslogic.RefreshContentEventArgs e = new umbraco.cms.businesslogic.RefreshContentEventArgs();
FireBeforeRefreshContent(e);
if (!e.Cancel)
{
ThreadPool.QueueUserWorkItem(
delegate
{
XmlDocument xmlDoc = LoadContentFromDatabase(true);
XmlContentInternal = xmlDoc;
if (!UmbracoSettings.isXmlContentCacheDisabled)
SaveContentToDisk(xmlDoc);
});
FireAfterRefreshContent(e);
}
}
private void TransferValuesFromDocumentXmlToPublishedXml(XmlNode DocumentNode, XmlNode PublishedNode)
{
// Remove all attributes and data nodes from the published node
PublishedNode.Attributes.RemoveAll();
foreach (XmlNode n in PublishedNode.SelectNodes("./data"))
PublishedNode.RemoveChild(n);
// Append all attributes and datanodes from the documentnode to the publishednode
foreach (XmlAttribute att in DocumentNode.Attributes)
((XmlElement)PublishedNode).SetAttribute(att.Name, att.Value);
foreach (XmlElement el in DocumentNode.SelectNodes("./data"))
{
XmlNode newDatael = PublishedNode.OwnerDocument.ImportNode(el, true);
PublishedNode.AppendChild(newDatael);
}
}
/// <summary>
/// Used by all overloaded publish methods to do the actual "noderepresentation to xml"
/// </summary>
/// <param name="d"></param>
/// <param name="xmlContentCopy"></param>
private void PublishNodeDo(Document d, XmlDocument xmlContentCopy)
{
// check if document *is* published, it could be unpublished by an event
if (d.Published)
{
// Find the document in the xml cache
XmlNode x = xmlContentCopy.GetElementById(d.Id.ToString());
// Find the parent (used for sortering and maybe creation of new node)
XmlNode parentNode;
if (d.Level == 1)
parentNode = xmlContentCopy.DocumentElement;
else
parentNode = xmlContentCopy.GetElementById(d.Parent.Id.ToString());
if (parentNode != null)
{
if (x == null)
{
x = d.ToXml(xmlContentCopy, false);
parentNode.AppendChild(x);
}
else
TransferValuesFromDocumentXmlToPublishedXml(d.ToXml(xmlContentCopy, false), x);
XmlNodeList childNodes = parentNode.SelectNodes("./node");
// Maybe sort the nodes if the added node has a lower sortorder than the last
if (childNodes.Count > 0)
{
int siblingSortOrder = int.Parse(childNodes[childNodes.Count - 1].Attributes.GetNamedItem("sortOrder").Value);
int currentSortOrder = int.Parse(x.Attributes.GetNamedItem("sortOrder").Value);
if (childNodes.Count > 1 && siblingSortOrder > currentSortOrder)
{
SortNodes(ref parentNode);
}
}
}
// update sitemapprovider
if (SiteMap.Provider is presentation.nodeFactory.UmbracoSiteMapProvider)
{
presentation.nodeFactory.UmbracoSiteMapProvider prov = (presentation.nodeFactory.UmbracoSiteMapProvider)SiteMap.Provider;
prov.UpdateNode(new umbraco.presentation.nodeFactory.Node(d.Id));
}
}
}
/// <summary>
/// Sorts the documents.
/// </summary>
/// <param name="parentNode">The parent node.</param>
public static void SortNodes(ref XmlNode parentNode)
{
XmlNode n = parentNode.CloneNode(true);
// remove all children from original node
foreach (XmlNode child in parentNode.SelectNodes("./node"))
parentNode.RemoveChild(child);
XPathNavigator nav = n.CreateNavigator();
XPathExpression expr = nav.Compile("./node");
expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
XPathNodeIterator iterator = nav.Select(expr);
while (iterator.MoveNext())
parentNode.AppendChild(
((IHasXmlNode)iterator.Current).GetNode());
}
/// <summary>
/// Updates the document cache.
/// </summary>
/// <param name="pageId">The page id.</param>
public virtual void UpdateDocumentCache(int pageId)
{
Document d = new Document(pageId);
UpdateDocumentCache(d);
}
/// <summary>
/// Updates the document cache.
/// </summary>
/// <param name="d">The d.</param>
public virtual void UpdateDocumentCache(Document d)
{
cms.businesslogic.DocumentCacheEventArgs e = new umbraco.cms.businesslogic.DocumentCacheEventArgs();
FireBeforeUpdateDocumentCache(d, e);
if (!e.Cancel)
{
// We need to lock content cache here, because we cannot allow other threads
// making changes at the same time, they need to be queued
// Adding log entry before locking the xmlfile
lock (_xmlContentInternalSyncLock)
{
// Make copy of memory content, we cannot make changes to the same document
// the is read from elsewhere
if (UmbracoSettings.CloneXmlCacheOnPublish)
{
XmlDocument xmlContentCopy = CloneXmlDoc(XmlContentInternal);
PublishNodeDo(d, xmlContentCopy);
XmlContentInternal = xmlContentCopy;
}
else
{
PublishNodeDo(d, XmlContentInternal);
XmlContentInternal = _xmlContent;
}
ClearContextCache();
}
// clear cached field values
System.Web.Caching.Cache httpCache = HttpContext.Current.Cache;
string cachedFieldKeyStart = String.Format("contentItem{0}_", d.Id);
List<string> foundKeys = new List<string>();
foreach (DictionaryEntry cacheItem in httpCache)
{
string key = cacheItem.Key.ToString();
if (key.StartsWith(cachedFieldKeyStart))
foundKeys.Add(key);
}
foreach (string foundKey in foundKeys)
{
httpCache.Remove(foundKey);
}
umbraco.BusinessLogic.Actions.Action.RunActionHandlers(d, ActionPublish.Instance);
FireAfterUpdateDocumentCache(d, e);
}
}
/// <summary>
/// Updates the document cache for multiple documents
/// </summary>
/// <param name="Documents">The documents.</param>
public virtual void UpdateDocumentCache(List<Document> Documents)
{
// We need to lock content cache here, because we cannot allow other threads
// making changes at the same time, they need to be queued
int parentid = Documents[0].Id;
lock (_xmlContentInternalSyncLock)
{
// Make copy of memory content, we cannot make changes to the same document
// the is read from elsewhere
XmlDocument xmlContentCopy = CloneXmlDoc(XmlContentInternal);
foreach (Document d in Documents)
{
PublishNodeDo(d, xmlContentCopy);
}
XmlContentInternal = xmlContentCopy;
ClearContextCache();
}
foreach (Document d in Documents)
{
umbraco.BusinessLogic.Actions.Action.RunActionHandlers(d, ActionPublish.Instance);
}
}
/// <summary>
/// Updates the document cache async.
/// </summary>
/// <param name="documentId">The document id.</param>
public virtual void UpdateDocumentCacheAsync(int documentId)
{
ThreadPool.QueueUserWorkItem(delegate { UpdateDocumentCache(documentId); });
}
/// <summary>
/// Clears the document cache async.
/// </summary>
/// <param name="documentId">The document id.</param>
public virtual void ClearDocumentCacheAsync(int documentId)
{
ThreadPool.QueueUserWorkItem(delegate { ClearDocumentCache(documentId); });
}
/// <summary>
/// Clears the document cache and removes the document from the xml db cache.
/// This means the node gets unpublished from the website.
/// </summary>
/// <param name="documentId">The document id.</param>
public virtual void ClearDocumentCache(int documentId)
{
// Get the document
Document d = new Document(documentId);
cms.businesslogic.DocumentCacheEventArgs e = new umbraco.cms.businesslogic.DocumentCacheEventArgs();
FireBeforeClearDocumentCache(d, e);
if (!e.Cancel)
{
XmlNode x;
// remove from xml db cache
d.XmlRemoveFromDB();
// Check if node present, before cloning
x = XmlContentInternal.GetElementById(d.Id.ToString());
if (x == null)
return;
// We need to lock content cache here, because we cannot allow other threads
// making changes at the same time, they need to be queued
lock (_xmlContentInternalSyncLock)
{
// Make copy of memory content, we cannot make changes to the same document
// the is read from elsewhere
XmlDocument xmlContentCopy = CloneXmlDoc(XmlContentInternal);
// Find the document in the xml cache
x = xmlContentCopy.GetElementById(d.Id.ToString());
if (x != null)
{
// The document already exists in cache, so repopulate it
x.ParentNode.RemoveChild(x);
XmlContentInternal = xmlContentCopy;
ClearContextCache();
}
}
if (x != null)
{
// Run Handler
umbraco.BusinessLogic.Actions.Action.RunActionHandlers(d, ActionUnPublish.Instance);
}
// update sitemapprovider
if (SiteMap.Provider is presentation.nodeFactory.UmbracoSiteMapProvider)
{
presentation.nodeFactory.UmbracoSiteMapProvider prov = (presentation.nodeFactory.UmbracoSiteMapProvider)SiteMap.Provider;
prov.RemoveNode(d.Id);
}
FireAfterClearDocumentCache(d, e);
}
}
/// <summary>
/// Unpublishes the node.
/// </summary>
/// <param name="documentId">The document id.</param>
[Obsolete("Please use: umbraco.content.ClearDocumentCache")]
public virtual void UnPublishNode(int documentId)
{
ClearDocumentCache(documentId);
}
/// <summary>
/// Uns the publish node async.
/// </summary>
/// <param name="documentId">The document id.</param>
[Obsolete("Please use: umbraco.content.ClearDocumentCacheAsync")]
public virtual void UnPublishNodeAsync(int documentId)
{
ThreadPool.QueueUserWorkItem(delegate { ClearDocumentCache(documentId); });
}
/// <summary>
/// Legacy method - you should use the overloaded publishnode(document d) method whenever possible
/// </summary>
/// <param name="documentId"></param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCache")]
public virtual void PublishNode(int documentId)
{
// Get the document
Document d = new Document(documentId);
PublishNode(d);
}
/// <summary>
/// Publishes the node async.
/// </summary>
/// <param name="documentId">The document id.</param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCacheAsync")]
public virtual void PublishNodeAsync(int documentId)
{
UpdateDocumentCacheAsync(documentId);
}
/// <summary>
/// Publishes the node.
/// </summary>
/// <param name="Documents">The documents.</param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCache")]
public virtual void PublishNode(List<Document> Documents)
{
UpdateDocumentCache(Documents);
}
/// <summary>
/// Publishes the node.
/// </summary>
/// <param name="d">The document.</param>
[Obsolete("Please use: umbraco.content.UpdateDocumentCache")]
public virtual void PublishNode(Document d)
{
UpdateDocumentCache(d);
}
public delegate void DocumentCacheEventHandler(Document sender, cms.businesslogic.DocumentCacheEventArgs e);
public delegate void RefreshContentEventHandler(Document sender, cms.businesslogic.RefreshContentEventArgs e);
/// <summary>
/// Occurs when [before document cache update].
/// </summary>
public static event DocumentCacheEventHandler BeforeUpdateDocumentCache;
/// <summary>
/// Fires the before document cache.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="umbraco.cms.businesslogic.DocumentCacheEventArgs"/> instance containing the event data.</param>
protected virtual void FireBeforeUpdateDocumentCache(Document sender, cms.businesslogic.DocumentCacheEventArgs e)
{
if (BeforeUpdateDocumentCache != null)
{
BeforeUpdateDocumentCache(sender, e);
}
}
/// <summary>
/// Occurs when [after document cache update].
/// </summary>
public static event DocumentCacheEventHandler AfterUpdateDocumentCache;
/// <summary>
/// Fires after document cache updater.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="umbraco.cms.businesslogic.DocumentCacheEventArgs"/> instance containing the event data.</param>
protected virtual void FireAfterUpdateDocumentCache(Document sender, cms.businesslogic.DocumentCacheEventArgs e)
{
if (AfterUpdateDocumentCache != null)
{
AfterUpdateDocumentCache(sender, e);
}
}
/// <summary>
/// Occurs when [before document cache unpublish].
/// </summary>
public static event DocumentCacheEventHandler BeforeClearDocumentCache;
/// <summary>
/// Fires the before document cache unpublish.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="umbraco.cms.businesslogic.DocumentCacheEventArgs"/> instance containing the event data.</param>
protected virtual void FireBeforeClearDocumentCache(Document sender, cms.businesslogic.DocumentCacheEventArgs e)
{
if (BeforeClearDocumentCache != null)
{
BeforeClearDocumentCache(sender, e);
}
}
public static event DocumentCacheEventHandler AfterClearDocumentCache;
/// <summary>
/// Fires the after document cache unpublish.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="umbraco.cms.businesslogic.DocumentCacheEventArgs"/> instance containing the event data.</param>
protected virtual void FireAfterClearDocumentCache(Document sender, cms.businesslogic.DocumentCacheEventArgs e)
{
if (AfterClearDocumentCache != null)
{
AfterClearDocumentCache(sender, e);
}
}
/// <summary>
/// Occurs when [before refresh content].
/// </summary>
public static event RefreshContentEventHandler BeforeRefreshContent;
/// <summary>
/// Fires the content of the before refresh.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="umbraco.cms.businesslogic.RefreshContentEventArgs"/> instance containing the event data.</param>
protected virtual void FireBeforeRefreshContent(cms.businesslogic.RefreshContentEventArgs e)
{
if (BeforeRefreshContent != null)
{
BeforeRefreshContent(null, e);
}
}
/// <summary>
/// Occurs when [after refresh content].
/// </summary>
public static event RefreshContentEventHandler AfterRefreshContent;
/// <summary>
/// Fires the content of the after refresh.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="umbraco.cms.businesslogic.RefreshContentEventArgs"/> instance containing the event data.</param>
protected virtual void FireAfterRefreshContent(cms.businesslogic.RefreshContentEventArgs e)
{
if (AfterRefreshContent != null)
{
AfterRefreshContent(null, e);
}
}
#endregion
#region Protected & Private methods
/// <summary>
/// Invalidates the disk content cache file. Effectively just deletes it.
/// </summary>
private void ClearDiskCache()
{
lock (_readerWriterSyncLock)
{
if (File.Exists(UmbracoXmlDiskCacheFileName))
{
// Reset file attributes, to make sure we can delete file
File.SetAttributes(UmbracoXmlDiskCacheFileName, FileAttributes.Normal);
File.Delete(UmbracoXmlDiskCacheFileName);
}
}
}
/// <summary>
/// Clear HTTPContext cache if any
/// </summary>
private void ClearContextCache()
{
// If running in a context very important to reset context cache orelse new nodes are missing
if (HttpContext.Current != null && HttpContext.Current.Items.Contains(XmlContextContentItemKey))
HttpContext.Current.Items.Remove(XmlContextContentItemKey);
}
/// <summary>
/// Invalidates the disk content cache file. Effectively just deletes it.
/// </summary>
private void ClearDiskCacheAsync()
{
// Queue file deletion
// We queue this function, because there can be a write process running at the same time
// and we don't want this method to block web request
ThreadPool.QueueUserWorkItem(
delegate { ClearDiskCache(); });
}
/// <summary>
/// Load content from either disk or database
/// </summary>
/// <returns></returns>
private XmlDocument LoadContent()
{
if (!UmbracoSettings.isXmlContentCacheDisabled && IsValidDiskCachePresent())
{
try
{
return LoadContentFromDiskCache();
}
catch (Exception e)
{
// This is really bad, loading from cache file failed for some reason, now fallback to loading from database
Debug.WriteLine("Content file cache load failed: " + e);
ClearDiskCache();
}
}
return LoadContentFromDatabase(true);
}
private bool IsValidDiskCachePresent()
{
return File.Exists(UmbracoXmlDiskCacheFileName);
}
/// <summary>
/// Load content from cache file
/// </summary>
private XmlDocument LoadContentFromDiskCache()
{
lock (_readerWriterSyncLock)
{
XmlDocument xmlDoc = new XmlDocument();
Log.Add(LogTypes.System, User.GetUser(0), -1, "Loading content from disk cache...");
xmlDoc.Load(UmbracoXmlDiskCacheFileName);
return xmlDoc;
}
}
private void InitContentDocumentBase(XmlDocument xmlDoc)
{
// Create id -1 attribute
xmlDoc.LoadXml(@"<!DOCTYPE umbraco [ " +
"<!ELEMENT nodes ANY> " +
"<!ELEMENT node ANY> " +
"<!ATTLIST node id ID #REQUIRED> ]>" +
"<root id=\"-1\"/>");
}
/// <summary>
/// Load content from database
/// </summary>
private XmlDocument LoadContentFromDatabase(bool loadDocuments)
{
XmlDocument xmlDoc = new XmlDocument();
InitContentDocumentBase(xmlDoc);
Log.Add(LogTypes.System, User.GetUser(0), -1, "Loading content from database...");
Hashtable nodes = new Hashtable();
Hashtable parents = new Hashtable();
try
{
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Republishing starting");
// Esben Carlsen: At some point we really need to put all data access into to a tier of its own.
string sql = "";
if (loadDocuments)
{
sql = @"select umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, cmsContentXml.xml from umbracoNode
inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
inner join cmsDocument on cmsDocument.nodeId = umbracoNode.id and cmsDocument.published = 1
order by umbracoNode.level, umbracoNode.sortOrder";
}
else
{
sql =
@"select umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, cmsContentXml.xml from umbracoNode
inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
order by umbracoNode.level, umbracoNode.sortOrder";
}
lock (_dbReadSyncLock)
{
using (IRecordsReader dr = SqlHelper.ExecuteReader(sql, SqlHelper.CreateParameter("@type", new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"))))
{
while (dr.Read())
{
int currentId = dr.GetInt("id");
int parentId = dr.GetInt("parentId");
xmlDoc.LoadXml(xmlHelper.SanitizeXmlString(currentId, dr.GetString("xml"), true, " while republishing"));
nodes.Add(currentId, xmlDoc.FirstChild);
if (parents.ContainsKey(parentId))
((ArrayList)parents[parentId]).Add(currentId);
else
{
ArrayList a = new ArrayList();
a.Add(currentId);
parents.Add(parentId, a);
}
}
}
}
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Xml Pages loaded");
// Reset
InitContentDocumentBase(xmlDoc);
try
{
GenerateXmlDocument(parents, nodes, -1, xmlDoc.DocumentElement);
}
catch (Exception ee)
{
Log.Add(LogTypes.Error, User.GetUser(0), -1,
string.Format("Error while generating XmlDocument from database: {0}", ee));
}
}
catch (OutOfMemoryException)
{
Log.Add(LogTypes.Error, User.GetUser(0), -1,
string.Format("Error Republishin: Out Of Memory. Parents: {0}, Nodes: {1}",
parents.Count, nodes.Count));
}
catch (Exception ee)
{
Log.Add(LogTypes.Error, User.GetUser(0), -1, string.Format("Error Republishing: {0}", ee));
}
finally
{
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Done republishing Xml Index");
}
return xmlDoc;
}
private void GenerateXmlDocument(Hashtable parents, Hashtable nodes, int parentId, XmlNode parentNode)
{
if (parents.ContainsKey(parentId))
{
ArrayList children = (ArrayList)parents[parentId];
foreach (int i in children)
{
XmlNode childNode = (XmlNode)nodes[i];
parentNode.AppendChild(childNode);
GenerateXmlDocument(parents, nodes, i, childNode);
}
}
}
/// <summary>
/// Persist a XmlDocument to the Disk Cache
/// </summary>
/// <param name="xmlDoc"></param>
internal void SaveContentToDisk(XmlDocument xmlDoc)
{
lock (_readerWriterSyncLock)
{
try
{
Stopwatch stopWatch = Stopwatch.StartNew();
ClearDiskCache();
xmlDoc.Save(UmbracoXmlDiskCacheFileName);
Log.Add(LogTypes.Debug, User.GetUser(0), -1, string.Format("Xml saved in {0}", stopWatch.Elapsed));
}
catch (Exception ee)
{
// If for whatever reason something goes wrong here, invalidate disk cache
ClearDiskCache();
Log.Add(LogTypes.Error, User.GetUser(0), -1, string.Format("Xml wasn't saved: {0}", ee));
}
}
}
/// <summary>
/// Persist xml document to disk cache in a background thread
/// </summary>
/// <param name="xmlDoc"></param>
private void SaveContentToDiskAsync(XmlDocument xmlDoc)
{
// Save copy of content
if (UmbracoSettings.CloneXmlCacheOnPublish)
{
XmlDocument xmlContentCopy = CloneXmlDoc(xmlDoc);
ThreadPool.QueueUserWorkItem(
delegate { SaveContentToDisk(xmlContentCopy); });
}
else
ThreadPool.QueueUserWorkItem(
delegate { SaveContentToDisk(xmlDoc); });
}
/// <summary>
/// Make a copy of a XmlDocument
/// </summary>
/// <param name="xmlDoc"></param>
/// <returns></returns>
private XmlDocument CloneXmlDoc(XmlDocument xmlDoc)
{
Log.Add(LogTypes.Debug, -1, "Cloning...");
// Save copy of content
XmlDocument xmlCopy = new XmlDocument();
xmlCopy.LoadXml(xmlDoc.OuterXml);
Log.Add(LogTypes.Debug, -1, "Cloning ended...");
return xmlCopy;
}
#endregion
}
}
|