diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index 39a63b5093..d589b90d74 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Models
new XAttribute("writerID", content.Writer.Id),
new XAttribute("creatorID", content.Creator.Id),
new XAttribute("nodeType", content.ContentType.Id),
- new XAttribute("template", content.Template),//Template name versus Id
+ new XAttribute("template", content.Template ?? string.Empty),//Template name versus Id - note that the template name/alias isn't saved in the db.
new XAttribute("sortOrder", content.SortOrder),
new XAttribute("createDate", content.CreateDate),
new XAttribute("updateDate", content.UpdateDate),
@@ -82,5 +82,17 @@ namespace Umbraco.Core.Models
return xml;
}
+
+ ///
+ /// Creates the xml representation for the object
+ ///
+ /// to generate xml for
+ /// Boolean indicating whether the xml should be generated for preview
+ /// Xml representation of the passed in
+ public static XElement ToXml(this IContent content, bool isPreview)
+ {
+ //TODO Do a proper implementation of this
+ return content.ToXml();
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWorkRepository.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWorkRepository.cs
index e64bf79612..8281d63bae 100644
--- a/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWorkRepository.cs
+++ b/src/Umbraco.Core/Persistence/UnitOfWork/IUnitOfWorkRepository.cs
@@ -2,6 +2,9 @@
namespace Umbraco.Core.Persistence.UnitOfWork
{
+ ///
+ /// Defines the Unit Of Work-part of a repository
+ ///
public interface IUnitOfWorkRepository
{
void PersistNewItem(IEntity entity);
diff --git a/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs b/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs
index 3bc9afd7f1..79a151f874 100644
--- a/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs
+++ b/src/Umbraco.Core/Publishing/BasePublishingStrategy.cs
@@ -46,6 +46,17 @@ namespace Umbraco.Core.Publishing
Published(content, e);
}
+ ///
+ /// Raises the event
+ ///
+ ///
+ /// The instance containing the event data.
+ protected virtual void OnPublished(IEnumerable content, PublishingEventArgs e)
+ {
+ if (Published != null)
+ Published(content, e);
+ }
+
///
/// Occurs before unpublish
///
diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config
index 17540689bc..729b7c1809 100644
--- a/src/Umbraco.Tests/App.config
+++ b/src/Umbraco.Tests/App.config
@@ -9,6 +9,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
index 9e68a575ee..0236863d61 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
@@ -175,17 +175,17 @@ namespace Umbraco.Tests.Persistence.Repositories
var content = new Content(1048, contentType);
content.Name = "Textpage 2 Child Node";
content.Creator = new Profile(0, "Administrator");
+ content.Writer = new Profile(0, "Administrator");
// Act
repository.AddOrUpdate(content);
unitOfWork.Commit();
var id = content.Id;
- var unitOfWork2 = provider.GetUnitOfWork();
- var contentTypeRepository2 = new ContentTypeRepository(unitOfWork2);
- var repository2 = new ContentRepository(unitOfWork2, InMemoryCacheProvider.Current, contentTypeRepository2);
+ var contentTypeRepository2 = new ContentTypeRepository(unitOfWork);
+ var repository2 = new ContentRepository(unitOfWork, InMemoryCacheProvider.Current, contentTypeRepository2);
repository2.Delete(content);
- unitOfWork2.Commit();
+ unitOfWork.Commit();
var content1 = repository2.Get(id);
diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs
new file mode 100644
index 0000000000..88ab06d263
--- /dev/null
+++ b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Web;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Tests.TestHelpers.Entities;
+using Umbraco.Web.Strategies;
+using umbraco.editorControls.tinyMCE3;
+using umbraco.interfaces;
+
+namespace Umbraco.Tests.Publishing
+{
+ [TestFixture]
+ public class PublishingStrategyTests : BaseDatabaseFactoryTest
+ {
+ [SetUp]
+ public override void Initialize()
+ {
+ //this ensures its reset
+ PluginManager.Current = new PluginManager();
+
+ //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver
+ PluginManager.Current.AssembliesToScan = new[]
+ {
+ typeof(IDataType).Assembly,
+ typeof(tinyMCE3dataType).Assembly
+ };
+
+ DataTypesResolver.Current = new DataTypesResolver(
+ PluginManager.Current.ResolveDataTypes());
+
+ base.Initialize();
+
+ CreateTestData();
+ }
+
+ [TearDown]
+ public override void TearDown()
+ {
+ base.TearDown();
+ }
+
+ [Test, Ignore]
+ 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);
+ }
+
+ public void CreateTestData()
+ {
+ //NOTE Maybe not the best way to create/save test data as we are using the services, which are being tested.
+
+ //Create and Save ContentType "umbTextpage" -> 1045
+ ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage");
+ ServiceContext.ContentTypeService.Save(contentType);
+
+ //Create and Save Content "Homepage" based on "umbTextpage" -> 1046
+ Content textpage = MockedContent.CreateSimpleContent(contentType);
+ ServiceContext.ContentService.Save(textpage, 0);
+
+ //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1047
+ Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id);
+ ServiceContext.ContentService.Save(subpage, 0);
+
+ //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1048
+ Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Page 2", textpage.Id);
+ ServiceContext.ContentService.Save(subpage2, 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index abbb7caa1c..1a17cbc411 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -121,6 +121,7 @@
+
diff --git a/src/Umbraco.Web/Publishing/PublishingStrategy.cs b/src/Umbraco.Web/Publishing/PublishingStrategy.cs
index 44bfbb6ab4..16ebd693c5 100644
--- a/src/Umbraco.Web/Publishing/PublishingStrategy.cs
+++ b/src/Umbraco.Web/Publishing/PublishingStrategy.cs
@@ -132,6 +132,8 @@ namespace Umbraco.Web.Publishing
OnPublished(item, e);
}
+ OnPublished(content, e);
+
//NOTE: Ideally the xml cache should be refreshed here - as part of the publishing
return true;
diff --git a/src/Umbraco.Web/Strategies/TrashedContent.cs b/src/Umbraco.Web/Strategies/TrashedContent.cs
new file mode 100644
index 0000000000..bffbdf11cf
--- /dev/null
+++ b/src/Umbraco.Web/Strategies/TrashedContent.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Web.Strategies
+{
+ public class TrashedContent
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Strategies/UpdateContentCache.cs b/src/Umbraco.Web/Strategies/UpdateContentCache.cs
new file mode 100644
index 0000000000..ae70dc1ae9
--- /dev/null
+++ b/src/Umbraco.Web/Strategies/UpdateContentCache.cs
@@ -0,0 +1,241 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+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.interfaces;
+using umbraco.presentation.nodeFactory;
+using Node = umbraco.NodeFactory.Node;
+
+namespace Umbraco.Web.Strategies
+{
+ public class UpdateContentCache : IApplicationStartupHandler
+ {
+ // Sync access to internal cache
+ private static readonly object XmlContentInternalSyncLock = new object();
+ private const string XmlContextContentItemKey = "UmbracoXmlContextContent";
+ private readonly HttpContextBase _httpContext;
+
+ public UpdateContentCache()
+ {
+ _httpContext = new HttpContextWrapper(HttpContext.Current);
+
+ PublishingStrategy.Published += PublishingStrategy_Published;
+ }
+
+ public UpdateContentCache(HttpContextBase httpContext)
+ {
+ _httpContext = httpContext;
+
+ 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)
+ {
+ // modify a clone of the cache because even though we're into the write-lock
+ // we may have threads reading at the same time. why is this an option?
+ XmlDocument wip = UmbracoSettings.CloneXmlCacheOnPublish
+ ? CloneXmlDoc(XmlContent)
+ : XmlContent;
+
+ ClearContextCache();
+
+ XmlContent = UpdateXmlAndSitemap(content, wip, 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;
+ xmlContentCopy = AppendContentXml(content.Id, content.Level, parentId, content.ToXml(false).GetXmlNode(), xmlContentCopy);
+
+ // update sitemapprovider
+ if (updateSitemapProvider && SiteMap.Provider is UmbracoSiteMapProvider)
+ {
+ try
+ {
+ var prov = (UmbracoSiteMapProvider)SiteMap.Provider;
+ global::umbraco.NodeFactory.Node 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
+ //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
+ {
+ //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;
+ }
+
+ private XmlDocument CloneXmlDoc(XmlDocument xmlDoc)
+ {
+ var xmlCopy = (XmlDocument)xmlDoc.CloneNode(true);
+ return xmlCopy;
+ }
+
+ ///
+ /// 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;
+
+ //TODO Move this to an extension method for the ContentType
+ var dtd = new StringBuilder();
+ dtd.AppendLine(" ");
+ dtd.AppendLine("]>");
+
+ content = new XmlDocument();
+ content.LoadXml(
+ String.Format("{0}{1}{0}",
+ Environment.NewLine,
+ dtd));
+
+ _httpContext.Items[XmlContextContentItemKey] = content;
+ }
+ return content;
+ }
+ set { _httpContext.Items[XmlContextContentItemKey] = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Strategies/UpdateMultipleContentCache.cs b/src/Umbraco.Web/Strategies/UpdateMultipleContentCache.cs
new file mode 100644
index 0000000000..a964ab8e27
--- /dev/null
+++ b/src/Umbraco.Web/Strategies/UpdateMultipleContentCache.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using Umbraco.Core.Models;
+using Umbraco.Web.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 c4fbacf117..bd32a1259c 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -323,6 +323,9 @@
+
+
+
ASPXCodeBehind