From fa80356a4931faab71dff5ea0781278a9eddc775 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Wed, 12 Dec 2012 19:47:28 -0100 Subject: [PATCH] Implements two subscribers for the PublishingStrategy for updating cache after publish and unpublish. With this in place it should only be necessary to call Publish in the ContentService, and not worry about refreshing the cache. --- src/Umbraco.Core/EventArgs.cs | 9 +- .../Publishing/BasePublishingStrategy.cs | 3 +- .../Publishing/IPublishingStrategy.cs | 3 +- .../Publishing/PublishingStrategy.cs | 5 +- src/Umbraco.Core/Services/ContentService.cs | 4 +- .../Publishing/PublishingStrategyTests.cs | 23 +- .../Strategies/UpdateCacheAfterPublish.cs | 100 ++++++++ .../Strategies/UpdateCacheAfterUnPublish.cs | 59 +++++ .../Strategies/UpdateContentCache.cs | 238 ------------------ .../Strategies/UpdateMultipleContentCache.cs | 19 -- src/Umbraco.Web/Umbraco.Web.csproj | 4 +- 11 files changed, 176 insertions(+), 291 deletions(-) create mode 100644 src/Umbraco.Web/Strategies/UpdateCacheAfterPublish.cs create mode 100644 src/Umbraco.Web/Strategies/UpdateCacheAfterUnPublish.cs delete mode 100644 src/Umbraco.Web/Strategies/UpdateContentCache.cs delete mode 100644 src/Umbraco.Web/Strategies/UpdateMultipleContentCache.cs diff --git a/src/Umbraco.Core/EventArgs.cs b/src/Umbraco.Core/EventArgs.cs index 2d5fee1bcd..4b6a3fb2e3 100644 --- a/src/Umbraco.Core/EventArgs.cs +++ b/src/Umbraco.Core/EventArgs.cs @@ -1,9 +1,10 @@ -using Umbraco.Core.Models; - -namespace Umbraco.Core +namespace Umbraco.Core { //Publishing Events - public class PublishingEventArgs : System.ComponentModel.CancelEventArgs { } + public class PublishingEventArgs : System.ComponentModel.CancelEventArgs + { + public bool IsAllRepublished { get; set; } + } public class SendToPublishEventArgs : System.ComponentModel.CancelEventArgs { } //Moving object Events diff --git a/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs b/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs index e413732278..36656e5e27 100644 --- a/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs @@ -114,7 +114,8 @@ namespace Umbraco.Core.Publishing /// Call to fire event that updating the published content has finalized. /// /// An enumerable list of thats being published - public abstract void PublishingFinalized(IEnumerable content); + /// Boolean indicating whether its all content that is republished + public abstract void PublishingFinalized(IEnumerable content, bool isAllRepublished); /// /// Call to fire event that updating the unpublished content has finalized. diff --git a/src/Umbraco.Core/Publishing/IPublishingStrategy.cs b/src/Umbraco.Core/Publishing/IPublishingStrategy.cs index 4535e896cd..0408409488 100644 --- a/src/Umbraco.Core/Publishing/IPublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/IPublishingStrategy.cs @@ -54,7 +54,8 @@ namespace Umbraco.Core.Publishing /// Call to fire event that updating the published content has finalized. /// /// An enumerable list of thats being published - void PublishingFinalized(IEnumerable content); + /// Boolean indicating whether its all content that is republished + void PublishingFinalized(IEnumerable content, bool isAllRepublished); /// /// Call to fire event that updating the unpublished content has finalized. diff --git a/src/Umbraco.Core/Publishing/PublishingStrategy.cs b/src/Umbraco.Core/Publishing/PublishingStrategy.cs index 165a2339db..8b3bc8821a 100644 --- a/src/Umbraco.Core/Publishing/PublishingStrategy.cs +++ b/src/Umbraco.Core/Publishing/PublishingStrategy.cs @@ -252,9 +252,10 @@ namespace Umbraco.Core.Publishing /// Call to fire event that updating the published content has finalized. /// /// An enumerable list of thats being published - public override void PublishingFinalized(IEnumerable content) + /// Boolean indicating whether its all content that is republished + public override void PublishingFinalized(IEnumerable content, bool isAllRepublished) { - OnPublished(content, new PublishingEventArgs()); + OnPublished(content, new PublishingEventArgs{ IsAllRepublished = isAllRepublished}); } /// diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index fe25a8f58f..6a3e670fab 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -362,7 +362,7 @@ namespace Umbraco.Core.Services _unitOfWork.Commit(); //Updating content to published state is finished, so we fire event through PublishingStrategy to have cache updated - _publishingStrategy.PublishingFinalized(updated); + _publishingStrategy.PublishingFinalized(updated, true); Audit.Add(AuditTypes.Publish, "RePublish All performed by user", userId == -1 ? 0 : userId, -1); } @@ -433,7 +433,7 @@ namespace Umbraco.Core.Services _unitOfWork.Commit(); //Save xml to db and call following method to fire event: - _publishingStrategy.PublishingFinalized(updated); + _publishingStrategy.PublishingFinalized(updated, false); Audit.Add(AuditTypes.Publish, "Publish with Children performed by user", userId == -1 ? 0 : userId, content.Id); } diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs index 9e6ea58ed8..ec2fff63a7 100644 --- a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs +++ b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs @@ -1,7 +1,5 @@ using System; using System.IO; -using System.Web; -using System.Xml; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration; @@ -11,7 +9,6 @@ using Umbraco.Core.ObjectResolution; using Umbraco.Core.Persistence; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Web.Strategies; using umbraco.editorControls.tinyMCE3; using umbraco.interfaces; @@ -66,25 +63,7 @@ namespace Umbraco.Tests.Publishing [Test] public void Can_Publish_And_Update_Xml_Cache() { - // Arrange - var httpContext = base.GetUmbracoContext("/test", 1234).HttpContext; - var updateContentCache = new UpdateContentCache(httpContext); - var contentService = ServiceContext.ContentService; - var content = contentService.GetById(1046); - - // Act - bool published = contentService.Publish(content, 0); - - // Assert - Assert.That(published, Is.True); - Assert.That(content.Published, Is.True); - Assert.IsTrue(httpContext.Items.Contains("UmbracoXmlContextContent")); - - var document = httpContext.Items["UmbracoXmlContextContent"] as XmlDocument; - Console.Write(document.OuterXml); - document.Save("umbraco.config"); - - updateContentCache.Unsubscribe(); + //TODO Make new test } public void CreateTestData() diff --git a/src/Umbraco.Web/Strategies/UpdateCacheAfterPublish.cs b/src/Umbraco.Web/Strategies/UpdateCacheAfterPublish.cs new file mode 100644 index 0000000000..52bd6da466 --- /dev/null +++ b/src/Umbraco.Web/Strategies/UpdateCacheAfterPublish.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Publishing; +using umbraco; +using umbraco.cms.businesslogic.web; +using umbraco.interfaces; +using umbraco.presentation.cache; + +namespace Umbraco.Web.Strategies +{ + /// + /// Represents the UpdateCacheAfterPublish class, which subscribes to the Published event + /// of the class and is responsible for doing the actual + /// cache refresh after a content item has been published. + /// + /// + /// This implementation is meant as a seperation of the cache refresh from the ContentService + /// and PublishingStrategy. + /// + public class UpdateCacheAfterPublish : IApplicationStartupHandler + { + public UpdateCacheAfterPublish() + { + PublishingStrategy.Published += PublishingStrategy_Published; + } + + void PublishingStrategy_Published(object sender, Core.PublishingEventArgs e) + { + if (sender is IContent) + { + var content = sender as IContent; + UpdateSingleContentCache(content); + } + else if (sender is IEnumerable) + { + if (e.IsAllRepublished) + { + var content = sender as IEnumerable; + UpdateMultipleContentCache(content); + } + else + { + UpdateEntireCache(); + } + } + } + + /// + /// Refreshes the xml cache for all nodes + /// + private void UpdateEntireCache() + { + if (UmbracoSettings.UseDistributedCalls) + { + dispatcher.RefreshAll(new Guid("27ab3022-3dfa-47b6-9119-5945bc88fd66")); + } + else + { + content.Instance.RefreshContentFromDatabaseAsync(); + } + } + + /// + /// Refreshes the xml cache for nodes in list + /// + private void UpdateMultipleContentCache(IEnumerable content) + { + if (UmbracoSettings.UseDistributedCalls) + { + foreach (var c in content) + { + dispatcher.Refresh(new Guid("27ab3022-3dfa-47b6-9119-5945bc88fd66"), c.Id); + } + } + else + { + var documents = content.Select(x => new Document(x)).ToList(); + global::umbraco.content.Instance.UpdateDocumentCache(documents); + } + } + + /// + /// Refreshes the xml cache for a single node + /// + private void UpdateSingleContentCache(IContent content) + { + if (UmbracoSettings.UseDistributedCalls) + { + dispatcher.Refresh(new Guid("27ab3022-3dfa-47b6-9119-5945bc88fd66"), content.Id); + } + else + { + var doc = new Document(content); + global::umbraco.content.Instance.UpdateDocumentCache(doc); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/UpdateCacheAfterUnPublish.cs b/src/Umbraco.Web/Strategies/UpdateCacheAfterUnPublish.cs new file mode 100644 index 0000000000..22eefc70af --- /dev/null +++ b/src/Umbraco.Web/Strategies/UpdateCacheAfterUnPublish.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Publishing; +using umbraco; +using umbraco.interfaces; +using umbraco.presentation.cache; + +namespace Umbraco.Web.Strategies +{ + /// + /// Represents the UpdateCacheAfterUnPublish class, which subscribes to the UnPublished event + /// of the class and is responsible for doing the actual + /// cache refresh after a content item has been unpublished. + /// + /// + /// This implementation is meant as a seperation of the cache refresh from the ContentService + /// and PublishingStrategy. + /// + public class UpdateCacheAfterUnPublish : IApplicationStartupHandler + { + public UpdateCacheAfterUnPublish() + { + PublishingStrategy.UnPublished += PublishingStrategy_UnPublished; + } + + void PublishingStrategy_UnPublished(object sender, Core.PublishingEventArgs e) + { + if (sender is IContent) + { + var content = sender as IContent; + UnPublishSingle(content); + } + else if (sender is IEnumerable) + { + var content = sender as IEnumerable; + foreach (var c in content) + { + UnPublishSingle(c); + } + } + } + + /// + /// Refreshes the xml cache for a single node by removing it + /// + private void UnPublishSingle(IContent content) + { + if (UmbracoSettings.UseDistributedCalls) + { + dispatcher.Remove(new Guid("27ab3022-3dfa-47b6-9119-5945bc88fd66"), content.Id); + } + else + { + global::umbraco.content.Instance.ClearDocumentCache(content.Id); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/UpdateContentCache.cs b/src/Umbraco.Web/Strategies/UpdateContentCache.cs deleted file mode 100644 index 4dc81a5576..0000000000 --- a/src/Umbraco.Web/Strategies/UpdateContentCache.cs +++ /dev/null @@ -1,238 +0,0 @@ -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.Core.Publishing; -using Umbraco.Core.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(); - 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; - } - - /// - /// Clear HTTPContext cache if any - /// - 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("", docTypeAlias))) - { - subset = string.Format("\r\n\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("{0}{1}{0}", - Environment.NewLine, - dtd)); - - _httpContext.Items[XmlContextContentItemKey] = content; - } - return content; - } - set { _httpContext.Items[XmlContextContentItemKey] = value; } - } - - internal void Unsubscribe() - { - PublishingStrategy.Published -= PublishingStrategy_Published; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/UpdateMultipleContentCache.cs b/src/Umbraco.Web/Strategies/UpdateMultipleContentCache.cs deleted file mode 100644 index fa06e82927..0000000000 --- a/src/Umbraco.Web/Strategies/UpdateMultipleContentCache.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Core.Models; -using Umbraco.Core.Publishing; - -namespace Umbraco.Web.Strategies -{ - public class UpdateMultipleContentCache - { - public UpdateMultipleContentCache() - { - PublishingStrategy.Published += PublishingStrategy_Published; - } - - void PublishingStrategy_Published(object sender, Core.PublishingEventArgs e) - { - if ((sender is IEnumerable) == false) return; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index b46e7337e5..399a2e15bc 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -327,9 +327,9 @@ + + - - ASPXCodeBehind