diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index c4965d7fa2..049a5baa9a 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -32,8 +32,9 @@ namespace Umbraco.Core.Models Value = value; } - public Property(PropertyType propertyType, object value, Guid version) + public Property(int id, Guid version, PropertyType propertyType, object value) { + Id = id; _propertyType = propertyType; _version = version; Value = value; diff --git a/src/Umbraco.Core/Models/PropertyExtensions.cs b/src/Umbraco.Core/Models/PropertyExtensions.cs index 8f978673c9..15c4737715 100644 --- a/src/Umbraco.Core/Models/PropertyExtensions.cs +++ b/src/Umbraco.Core/Models/PropertyExtensions.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models /// Xml of the property and its value public static XElement ToXml(this Property property) { - string nodeName = property.Alias.ToUmbracoAlias(); + string nodeName = property.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); var xd = new XmlDocument(); XmlNode xmlNode = xd.CreateNode(XmlNodeType.Element, nodeName, ""); diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 7a1213820e..95fc2648ce 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -209,10 +209,12 @@ namespace Umbraco.Core.Models /// /// Can be used for the "old" values where no serialization type exists /// + /// + /// /// - internal Property CreatePropertyFromRawValue(object value) + internal Property CreatePropertyFromRawValue(object value, Guid version, int id) { - return new Property(this, value); + return new Property(id, version, this, value); } /// diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index f846db0f86..ccd2071b76 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -35,7 +35,9 @@ namespace Umbraco.Core.Persistence.Factories foreach (var dto in dtos) { var propertyType = _contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Id == dto.PropertyTypeId); - properties.Add(propertyType.CreatePropertyFromRawValue(dto.GetValue)); + var property = propertyType.CreatePropertyFromRawValue(dto.GetValue, dto.VersionId.Value, dto.Id); + property.ResetDirtyProperties(); + properties.Add(property); } return properties; } @@ -85,7 +87,9 @@ namespace Umbraco.Core.Persistence.Factories foreach (var dto in dtos) { var propertyType = _mediaType.PropertyTypes.FirstOrDefault(x => x.Id == dto.PropertyTypeId); - properties.Add(propertyType.CreatePropertyFromRawValue(dto.GetValue)); + var property = propertyType.CreatePropertyFromRawValue(dto.GetValue, dto.VersionId.Value, dto.Id); + property.ResetDirtyProperties(); + properties.Add(property); } return properties; } diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index f1881fea0c..d4d11493b8 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -115,5 +115,17 @@ namespace Umbraco.Core.Services /// Collection of to delete /// Deleting a will delete all the objects based on this void Delete(IEnumerable mediaTypes); + + /// + /// Generates the complete (simplified) XML DTD. + /// + /// The DTD as a string + string GetDtd(); + + /// + /// Generates the complete XML DTD without the root. + /// + /// The DTD as a string + string GetContentTypesDtd(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/XmlExtensions.cs b/src/Umbraco.Core/XmlExtensions.cs index 7757d25fda..2ef5ede802 100644 --- a/src/Umbraco.Core/XmlExtensions.cs +++ b/src/Umbraco.Core/XmlExtensions.cs @@ -39,7 +39,16 @@ namespace Umbraco.Core { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(xmlReader); - return xmlDoc; + return xmlDoc.FirstChild; + } + } + + public static XmlNode GetXmlNode(this XElement element, XmlDocument xmlDoc) + { + using (XmlReader xmlReader = element.CreateReader()) + { + xmlDoc.Load(xmlReader); + return xmlDoc.DocumentElement; } } } diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config index 729b7c1809..c899acb040 100644 --- a/src/Umbraco.Tests/App.config +++ b/src/Umbraco.Tests/App.config @@ -104,4 +104,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs index 88ab06d263..5035920fc8 100644 --- a/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs +++ b/src/Umbraco.Tests/Publishing/PublishingStrategyTests.cs @@ -1,8 +1,10 @@ using System; using System.Web; +using System.Xml; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Core.ObjectResolution; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Web.Strategies; @@ -38,10 +40,19 @@ namespace Umbraco.Tests.Publishing [TearDown] public override void TearDown() { - base.TearDown(); + DatabaseContext.Database.Dispose(); + + //TestHelper.ClearDatabase(); + + //reset the app context + ApplicationContext.Current = null; + Resolution.IsFrozen = false; + + string path = TestHelper.CurrentAssemblyDirectory; + AppDomain.CurrentDomain.SetData("DataDirectory", null); } - [Test, Ignore] + [Test] public void Can_Publish_And_Update_Xml_Cache() { // Arrange @@ -56,6 +67,11 @@ namespace Umbraco.Tests.Publishing // 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"); } public void CreateTestData() diff --git a/src/Umbraco.Web/Services/ContentTypeService.cs b/src/Umbraco.Web/Services/ContentTypeService.cs index 25e7e250c1..5519169237 100644 --- a/src/Umbraco.Web/Services/ContentTypeService.cs +++ b/src/Umbraco.Web/Services/ContentTypeService.cs @@ -1,11 +1,15 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Text; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Services; +using umbraco; namespace Umbraco.Web.Services { @@ -260,5 +264,61 @@ namespace Umbraco.Web.Services } _unitOfWork.Commit(); } + + /// + /// Generates the complete (simplified) XML DTD. + /// + /// The DTD as a string + public string GetDtd() + { + var dtd = new StringBuilder(); + dtd.AppendLine(""); + + return dtd.ToString(); + } + + /// + /// Generates the complete XML DTD without the root. + /// + /// The DTD as a string + public string GetContentTypesDtd() + { + var dtd = new StringBuilder(); + if (UmbracoSettings.UseLegacyXmlSchema) + { + dtd.AppendLine(" "); + } + else + { + try + { + var strictSchemaBuilder = new StringBuilder(); + + var contentTypes = GetAllContentTypes(); + foreach (ContentType contentType in contentTypes) + { + string safeAlias = contentType.Alias.ToUmbracoAlias(StringAliasCaseType.CamelCase, true); + if (safeAlias != null) + { + strictSchemaBuilder.AppendLine(String.Format("", safeAlias)); + strictSchemaBuilder.AppendLine(String.Format("", safeAlias)); + } + } + + // Only commit the strong schema to the container if we didn't generate an error building it + dtd.Append(strictSchemaBuilder); + } + catch (Exception exception) + { + // Note, Log.Add quietly swallows the exception if it can't write to the database + //Log.Add(LogTypes.System, -1, string.Format("{0} while trying to build DTD for Xml schema; is Umbraco installed correctly and the connection string configured?", exception.Message)); + } + + } + return dtd.ToString(); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/UpdateContentCache.cs b/src/Umbraco.Web/Strategies/UpdateContentCache.cs index ae70dc1ae9..b6642af010 100644 --- a/src/Umbraco.Web/Strategies/UpdateContentCache.cs +++ b/src/Umbraco.Web/Strategies/UpdateContentCache.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Text; using System.Web; using System.Web.Caching; using System.Xml; @@ -9,22 +8,25 @@ 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 { - public class UpdateContentCache : IApplicationStartupHandler + 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 = new ServiceContext(_httpContext); PublishingStrategy.Published += PublishingStrategy_Published; } @@ -32,6 +34,7 @@ namespace Umbraco.Web.Strategies public UpdateContentCache(HttpContextBase httpContext) { _httpContext = httpContext; + _serviceContext = new ServiceContext(_httpContext); PublishingStrategy.Published += PublishingStrategy_Published; } @@ -57,7 +60,7 @@ namespace Umbraco.Web.Strategies ClearContextCache(); - XmlContent = UpdateXmlAndSitemap(content, wip, true); + XmlContent = UpdateXmlAndSitemap(content, wip, false);//Update sitemap is usually set to true } // clear cached field values @@ -88,7 +91,9 @@ namespace Umbraco.Web.Strategies if (content.Published) { int parentId = content.Level == 1 ? -1 : content.ParentId; - xmlContentCopy = AppendContentXml(content.Id, content.Level, parentId, content.ToXml(false).GetXmlNode(), xmlContentCopy); + 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) @@ -96,7 +101,7 @@ namespace Umbraco.Web.Strategies try { var prov = (UmbracoSiteMapProvider)SiteMap.Provider; - global::umbraco.NodeFactory.Node n = new Node(content.Id, true); + var n = new Node(content.Id, true); if (!String.IsNullOrEmpty(n.Url) && n.Url != "/#") { prov.UpdateNode(n); @@ -127,7 +132,8 @@ namespace Umbraco.Web.Strategies if (x == null && UmbracoSettings.UseLegacyXmlSchema == false) { //TODO Look into the validate schema method - seems a bit odd - //xmlContentCopy = ValidateSchema(docNode.Name, xmlContentCopy); + //Move to Contract ? + xmlContentCopy = ValidateSchema(docNode.Name, xmlContentCopy); if (xmlContentCopy != xmlContentCopy2) docNode = xmlContentCopy.ImportNode(docNode, true); } @@ -148,6 +154,7 @@ namespace Umbraco.Web.Strategies } else { + //TODO //TransferValuesFromDocumentXmlToPublishedXml(docNode, x); } @@ -218,12 +225,7 @@ namespace Umbraco.Web.Strategies 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("]>"); + var dtd = _serviceContext.ContentTypeService.GetDtd(); content = new XmlDocument(); content.LoadXml(