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