Files
Umbraco-CMS/src/Umbraco.Web/Strategies/UpdateContentCache.cs
Morten Christensen e3d5fa766d Starting implementation of the UserRepository.
Fixing unit tests.
2012-11-09 14:45:28 -01:00

238 lines
9.4 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
using System.Xml;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Web.Publishing;
using Umbraco.Web.Services;
using umbraco.interfaces;
using umbraco.presentation.nodeFactory;
using Node = umbraco.NodeFactory.Node;
namespace Umbraco.Web.Strategies
{
internal class UpdateContentCache : IApplicationStartupHandler
{
// Sync access to internal cache
private static readonly object XmlContentInternalSyncLock = new object();
private const string XmlContextContentItemKey = "UmbracoXmlContextContent";
private readonly HttpContextBase _httpContext;
private readonly ServiceContext _serviceContext;
public UpdateContentCache()
{
_httpContext = new HttpContextWrapper(HttpContext.Current);
_serviceContext = ServiceContext.Current;
PublishingStrategy.Published += PublishingStrategy_Published;
}
public UpdateContentCache(HttpContextBase httpContext)
{
_httpContext = httpContext;
_serviceContext = ServiceContext.Current;
PublishingStrategy.Published += PublishingStrategy_Published;
}
void PublishingStrategy_Published(object sender, PublishingEventArgs e)
{
if((sender is IContent) == false) return;
//var e = new DocumentCacheEventArgs();
//FireBeforeUpdateDocumentCache(d, e);
var content = sender as IContent;
// lock the xml cache so no other thread can write to it at the same time
// note that some threads could read from it while we hold the lock, though
lock (XmlContentInternalSyncLock)
{
XmlDocument wip = XmlContent;
ClearContextCache();
XmlContent = UpdateXmlAndSitemap(content, wip, false);//Update sitemap is usually set to true
}
// clear cached field values
if (_httpContext != null)
{
Cache httpCache = _httpContext.Cache;
string cachedFieldKeyStart = String.Format("contentItem{0}_", content.Id);
var 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);
}
}
//Action.RunActionHandlers(d, ActionPublish.Instance);
//FireAfterUpdateDocumentCache(d, e);
}
private XmlDocument UpdateXmlAndSitemap(IContent content, XmlDocument xmlContentCopy, bool updateSitemapProvider)
{
// check if document *is* published, it could be unpublished by an event
if (content.Published)
{
int parentId = content.Level == 1 ? -1 : content.ParentId;
var contentXmlNode = content.ToXml(false).GetXmlNode();
var xmlNode = xmlContentCopy.ImportNode(contentXmlNode, true);
xmlContentCopy = AppendContentXml(content.Id, content.Level, parentId, xmlNode, xmlContentCopy);
// update sitemapprovider
if (updateSitemapProvider && SiteMap.Provider is UmbracoSiteMapProvider)
{
try
{
var prov = (UmbracoSiteMapProvider)SiteMap.Provider;
var n = new Node(content.Id, true);
if (!String.IsNullOrEmpty(n.Url) && n.Url != "/#")
{
prov.UpdateNode(n);
}
else
{
//Log.Add(LogTypes.Error, content.Id, "Can't update Sitemap Provider due to empty Url in node");
}
}
catch (Exception ee)
{
//Log.Add(LogTypes.Error, content.Id, string.Format("Error adding node to Sitemap Provider in PublishNodeDo(): {0}", ee));
}
}
}
return xmlContentCopy;
}
private XmlDocument AppendContentXml(int id, int level, int parentId, XmlNode docNode, XmlDocument xmlContentCopy)
{
// Find the document in the xml cache
XmlNode x = xmlContentCopy.GetElementById(id.ToString());
// if the document is not there already then it's a new document
// we must make sure that its document type exists in the schema
var xmlContentCopy2 = xmlContentCopy;
if (x == null && UmbracoSettings.UseLegacyXmlSchema == false)
{
//TODO Look into the validate schema method - seems a bit odd
//Move to Contract ?
xmlContentCopy = ValidateSchema(docNode.Name, xmlContentCopy);
if (xmlContentCopy != xmlContentCopy2)
docNode = xmlContentCopy.ImportNode(docNode, true);
}
// Find the parent (used for sortering and maybe creation of new node)
XmlNode parentNode;
if (level == 1)
parentNode = xmlContentCopy.DocumentElement;
else
parentNode = xmlContentCopy.GetElementById(parentId.ToString());
if (parentNode != null)
{
if (x == null)
{
x = docNode;
parentNode.AppendChild(x);
}
else
{
//TODO
//TransferValuesFromDocumentXmlToPublishedXml(docNode, x);
}
// TODO: Update with new schema!
string xpath = UmbracoSettings.UseLegacyXmlSchema ? "./node" : "./* [@id]";
XmlNodeList childNodes = parentNode.SelectNodes(xpath);
// 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);
}
}
}
return xmlContentCopy;
}
/// <summary>
/// Clear HTTPContext cache if any
/// </summary>
private void ClearContextCache()
{
// If running in a context very important to reset context cache or else new nodes are missing
if (_httpContext != null && _httpContext.Items.Contains(XmlContextContentItemKey))
_httpContext.Items.Remove(XmlContextContentItemKey);
}
private XmlDocument ValidateSchema(string docTypeAlias, XmlDocument xmlDoc)
{
// check if doctype is defined in schema else add it
// can't edit the doctype of an xml document, must create a new document
var doctype = xmlDoc.DocumentType;
var subset = doctype.InternalSubset;
if (!subset.Contains(string.Format("<!ATTLIST {0} id ID #REQUIRED>", docTypeAlias)))
{
subset = string.Format("<!ELEMENT {0} ANY>\r\n<!ATTLIST {0} id ID #REQUIRED>\r\n{1}", docTypeAlias, subset);
var xmlDoc2 = new XmlDocument();
doctype = xmlDoc2.CreateDocumentType("root", null, null, subset);
xmlDoc2.AppendChild(doctype);
var root = xmlDoc2.ImportNode(xmlDoc.DocumentElement, true);
xmlDoc2.AppendChild(root);
// apply
xmlDoc = xmlDoc2;
}
return xmlDoc;
}
private XmlDocument XmlContent
{
get
{
var content = _httpContext.Items[XmlContextContentItemKey] as XmlDocument;
if (content == null)
{
//content = global::umbraco.content.Instance.XmlContent;
var dtd = _serviceContext.ContentTypeService.GetDtd();
content = new XmlDocument();
content.LoadXml(
String.Format("<?xml version=\"1.0\" encoding=\"utf-8\" ?>{0}{1}{0}<root id=\"-1\"/>",
Environment.NewLine,
dtd));
_httpContext.Items[XmlContextContentItemKey] = content;
}
return content;
}
set { _httpContext.Items[XmlContextContentItemKey] = value; }
}
internal void Unsubscribe()
{
PublishingStrategy.Published -= PublishingStrategy_Published;
}
}
}