Merge branch '6.2.0-pubcontent' into 7.0.0-pubcontent

Conflicts:
	src/Umbraco.Core/Cache/CacheProviderBase.cs
	src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs
	src/Umbraco.Core/Cache/NullCacheProvider.cs
	src/Umbraco.Core/Cache/StaticCacheProvider.cs
	src/Umbraco.Core/Configuration/UmbracoSettings.cs
	src/Umbraco.Core/CoreBootManager.cs
	src/Umbraco.Core/Dynamics/PropertyResult.cs
	src/Umbraco.Core/Models/IPublishedContentProperty.cs
	src/Umbraco.Core/Models/PublishedItemType.cs
	src/Umbraco.Core/PropertyEditors/IPropertyEditorValueConverter.cs
	src/Umbraco.Core/PropertyEditors/PropertyEditorValueConvertersResolver.cs
	src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs
	src/Umbraco.Core/PublishedContentExtensions.cs
	src/Umbraco.Core/PublishedContentHelper.cs
	src/Umbraco.Core/Umbraco.Core.csproj
	src/Umbraco.Tests/CodeFirst/StronglyTypedMapperTest.cs
	src/Umbraco.Tests/LibraryTests.cs
	src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs
	src/Umbraco.Tests/PublishedCache/PublishedContentCacheTests.cs
	src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs
	src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs
	src/Umbraco.Web/ExamineExtensions.cs
	src/Umbraco.Web/Models/DynamicPublishedContent.cs
	src/Umbraco.Web/Models/XmlPublishedContent.cs
	src/Umbraco.Web/Models/XmlPublishedContentProperty.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
	src/Umbraco.Web/PublishedContentExtensions.cs
	src/Umbraco.Web/Templates/TemplateUtilities.cs
	src/Umbraco.Web/Umbraco.Web.csproj
	src/Umbraco.Web/WebBootManager.cs
	src/Umbraco.Web/umbraco.presentation/macro.cs
	src/umbraco.MacroEngines/RazorDynamicNode/PropertyResult.cs
	src/umbraco.MacroEngines/RazorDynamicNode/PublishedContentExtensions.cs
This commit is contained in:
Stephan
2013-09-23 21:57:22 +02:00
138 changed files with 10417 additions and 6251 deletions

View File

@@ -9,6 +9,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Xml;
using Umbraco.Web.Routing;
using umbraco;
@@ -237,14 +238,17 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
#region Converters
private static IPublishedContent ConvertToDocument(XmlNode xmlNode)
private static IPublishedContent ConvertToDocument(XmlNode xmlNode, bool isPreviewing)
{
return xmlNode == null ? null : new Models.XmlPublishedContent(xmlNode);
}
return xmlNode == null
? null
: PublishedContentModelFactory.CreateModel(new XmlPublishedContent(xmlNode, isPreviewing));
}
private static IEnumerable<IPublishedContent> ConvertToDocuments(XmlNodeList xmlNodes)
private static IEnumerable<IPublishedContent> ConvertToDocuments(XmlNodeList xmlNodes, bool isPreviewing)
{
return xmlNodes.Cast<XmlNode>().Select(xmlNode => new Models.XmlPublishedContent(xmlNode));
return xmlNodes.Cast<XmlNode>()
.Select(xmlNode => PublishedContentModelFactory.CreateModel(new XmlPublishedContent(xmlNode, isPreviewing)));
}
#endregion
@@ -253,12 +257,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
public virtual IPublishedContent GetById(UmbracoContext umbracoContext, bool preview, int nodeId)
{
return ConvertToDocument(GetXml(umbracoContext, preview).GetElementById(nodeId.ToString(CultureInfo.InvariantCulture)));
return ConvertToDocument(GetXml(umbracoContext, preview).GetElementById(nodeId.ToString(CultureInfo.InvariantCulture)), preview);
}
public virtual IEnumerable<IPublishedContent> GetAtRoot(UmbracoContext umbracoContext, bool preview)
{
return ConvertToDocuments(GetXml(umbracoContext, preview).SelectNodes(XPathStrings.RootDocuments));
return ConvertToDocuments(GetXml(umbracoContext, preview).SelectNodes(XPathStrings.RootDocuments), preview);
}
public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, string xpath, params XPathVariable[] vars)
@@ -270,7 +274,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
var node = vars == null
? xml.SelectSingleNode(xpath)
: xml.SelectSingleNode(xpath, vars);
return ConvertToDocument(node);
return ConvertToDocument(node, preview);
}
public virtual IPublishedContent GetSingleByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, params XPathVariable[] vars)
@@ -281,7 +285,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
var node = vars == null
? xml.SelectSingleNode(xpath)
: xml.SelectSingleNode(xpath, vars);
return ConvertToDocument(node);
return ConvertToDocument(node, preview);
}
public virtual IEnumerable<IPublishedContent> GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, params XPathVariable[] vars)
@@ -293,7 +297,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
var nodes = vars == null
? xml.SelectNodes(xpath)
: xml.SelectNodes(xpath, vars);
return ConvertToDocuments(nodes);
return ConvertToDocuments(nodes, preview);
}
public virtual IEnumerable<IPublishedContent> GetByXPath(UmbracoContext umbracoContext, bool preview, XPathExpression xpath, params XPathVariable[] vars)
@@ -304,7 +308,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
var nodes = vars == null
? xml.SelectNodes(xpath)
: xml.SelectNodes(xpath, vars);
return ConvertToDocuments(nodes);
return ConvertToDocuments(nodes, preview);
}
public virtual bool HasContent(UmbracoContext umbracoContext, bool preview)
@@ -322,6 +326,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
return xml.CreateNavigator();
}
public virtual bool XPathNavigatorIsNavigable { get { return false; } }
#endregion
#region Legacy Xml

View File

@@ -13,6 +13,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Xml;
using Umbraco.Web.Models;
using UmbracoExamine;
@@ -28,7 +29,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// <remarks>
/// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly.
/// </remarks>
internal class PublishedMediaCache : IPublishedMediaCache
internal class PublishedMediaCache : IPublishedMediaCache
{
public PublishedMediaCache()
{
@@ -93,6 +94,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
throw new NotImplementedException("PublishedMediaCache does not support XPath.");
}
public bool XPathNavigatorIsNavigable { get { return false; } }
public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); }
private ExamineManager GetExamineManagerSafe()
@@ -238,7 +241,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
return new DictionaryPublishedContent(values,
var content = new DictionaryPublishedContent(values,
d => d.ParentId != -1 //parent should be null if -1
? GetUmbracoMedia(d.ParentId)
: null,
@@ -246,6 +249,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
d => GetChildrenMedia(d.Id),
GetProperty,
true);
return PublishedContentModelFactory.CreateModel(content);
}
internal IPublishedContent ConvertFromXPathNavigator(XPathNavigator xpath)
@@ -296,7 +300,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
}
return new DictionaryPublishedContent(values,
var content = new DictionaryPublishedContent(values,
d => d.ParentId != -1 //parent should be null if -1
? GetUmbracoMedia(d.ParentId)
: null,
@@ -304,6 +308,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
d => GetChildrenMedia(d.Id, xpath),
GetProperty,
false);
return PublishedContentModelFactory.CreateModel(content);
}
/// <summary>
@@ -313,19 +318,23 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// <param name="dd"> </param>
/// <param name="alias"></param>
/// <returns></returns>
private IPublishedContentProperty GetProperty(DictionaryPublishedContent dd, string alias)
private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias)
{
if (dd.LoadedFromExamine)
{
//if this is from Examine, lets check if the alias does not exist on the document
if (dd.Properties.All(x => x.Alias != alias))
if (dd.Properties.All(x => x.PropertyTypeAlias != alias))
{
//ok it doesn't exist, we might assume now that Examine didn't index this property because the index is not set up correctly
//so before we go loading this from the database, we can check if the alias exists on the content type at all, this information
//is cached so will be quicker to look up.
if (dd.Properties.Any(x => x.Alias == UmbracoContentIndexer.NodeTypeAliasFieldName))
if (dd.Properties.Any(x => x.PropertyTypeAlias == UmbracoContentIndexer.NodeTypeAliasFieldName))
{
var aliasesAndNames = ContentType.GetAliasesAndNames(dd.Properties.First(x => x.Alias.InvariantEquals(UmbracoContentIndexer.NodeTypeAliasFieldName)).Value.ToString());
// so in dd.Properties, there is an IPublishedProperty with property type alias "__NodeTypeAlias" and
// that special property would contain the node type alias, which we use to get "aliases & names". That
// special property is going to be a PropertyResult (with ObjectValue == DataValue) and we
// want its value in the most simple way = it is OK to use DataValue here.
var aliasesAndNames = ContentType.GetAliasesAndNames(dd.Properties.First(x => x.PropertyTypeAlias.InvariantEquals(UmbracoContentIndexer.NodeTypeAliasFieldName)).DataValue.ToString());
if (aliasesAndNames != null)
{
if (!aliasesAndNames.ContainsKey(alias))
@@ -342,7 +351,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
media.MoveNext();
var mediaDoc = ConvertFromXPathNavigator(media.Current);
return mediaDoc.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
return mediaDoc.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
}
}
@@ -350,9 +359,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
//We've made it here which means that the value is stored in the Examine index.
//We are going to check for a special field however, that is because in some cases we store a 'Raw'
//value in the index such as for xml/html.
var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoContentIndexer.RawFieldPrefix + alias));
var rawValue = dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(UmbracoContentIndexer.RawFieldPrefix + alias));
return rawValue
?? dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
?? dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
/// <summary>
@@ -465,12 +474,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// </remarks>
internal class DictionaryPublishedContent : PublishedContentBase
{
// note: I'm not sure this class fully complies with IPublishedContent rules especially
// I'm not sure that _properties contains all properties including those without a value,
// neither that GetProperty will return a property without a value vs. null... @zpqrtbnk
public DictionaryPublishedContent(
IDictionary<string, string> valueDictionary,
Func<DictionaryPublishedContent, IPublishedContent> getParent,
Func<DictionaryPublishedContent, IEnumerable<IPublishedContent>> getChildren,
Func<DictionaryPublishedContent, string, IPublishedContentProperty> getProperty,
Func<DictionaryPublishedContent, string, IPublishedProperty> getProperty,
bool fromExamine)
{
if (valueDictionary == null) throw new ArgumentNullException("valueDictionary");
@@ -508,15 +520,27 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
}, "parentID");
_properties = new Collection<IPublishedContentProperty>();
_contentType = PublishedContentType.Get(PublishedItemType.Media, _documentTypeAlias);
_properties = new Collection<IPublishedProperty>();
//loop through remaining values that haven't been applied
foreach (var i in valueDictionary.Where(x => !_keysAdded.Contains(x.Key)))
{
//this is taken from examine
_properties.Add(i.Key.InvariantStartsWith("__")
? new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty)
: new PropertyResult(i.Key, i.Value, PropertyResultType.UserProperty));
IPublishedProperty property;
if (i.Key.InvariantStartsWith("__"))
{
// no type for that one, dunno how to convert
property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty);
}
else
{
// use property type to ensure proper conversion
var propertyType = _contentType.GetPropertyType(i.Key);
property = new XmlPublishedProperty(propertyType, false, i.Value); // false :: never preview a media
}
_properties.Add(property);
}
}
@@ -545,7 +569,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private readonly Func<DictionaryPublishedContent, IPublishedContent> _getParent;
private readonly Func<DictionaryPublishedContent, IEnumerable<IPublishedContent>> _getChildren;
private readonly Func<DictionaryPublishedContent, string, IPublishedContentProperty> _getProperty;
private readonly Func<DictionaryPublishedContent, string, IPublishedProperty> _getProperty;
/// <summary>
/// Returns 'Media' as the item type
@@ -645,7 +669,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
get { return _level; }
}
public override ICollection<IPublishedContentProperty> Properties
public override bool IsDraft
{
get { return false; }
}
public override ICollection<IPublishedProperty> Properties
{
get { return _properties; }
}
@@ -655,11 +684,49 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
get { return _getChildren(this); }
}
public override IPublishedContentProperty GetProperty(string alias)
public override IPublishedProperty GetProperty(string alias)
{
return _getProperty(this, alias);
}
public override PublishedContentType ContentType
{
get { return _contentType; }
}
// override to implement cache
// cache at context level, ie once for the whole request
// but cache is not shared by requests because we wouldn't know how to clear it
public override IPublishedProperty GetProperty(string alias, bool recurse)
{
if (recurse == false) return GetProperty(alias);
IPublishedProperty property;
string key = null;
var cache = UmbracoContextCache.Current;
if (cache != null)
{
key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant());
object o;
if (cache.TryGetValue(key, out o))
{
property = o as IPublishedProperty;
if (property == null)
throw new InvalidOperationException("Corrupted cache.");
return property;
}
}
// else get it for real, no cache
property = base.GetProperty(alias, true);
if (cache != null)
cache[key] = property;
return property;
}
private readonly List<string> _keysAdded = new List<string>();
private int _id;
private int _templateId;
@@ -677,7 +744,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private DateTime _updateDate;
private Guid _version;
private int _level;
private readonly ICollection<IPublishedContentProperty> _properties;
private readonly ICollection<IPublishedProperty> _properties;
private readonly PublishedContentType _contentType;
private void ValidateAndSetProperty(IDictionary<string, string> valueDictionary, Action<string> setProperty, params string[] potentialKeys)
{
@@ -690,6 +758,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
setProperty(valueDictionary[key]);
_keysAdded.Add(key);
}
}
}
}
}

View File

@@ -48,7 +48,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
global::umbraco.content.AfterUpdateDocumentCache += (sender, e) => Clear();
global::umbraco.content.AfterClearDocumentCache += (sender, e) => Clear();
// fixme - refactor when content events are refactored
// fixme - should refactor once content events are refactored
// the content class needs to be refactored - at the moment
// content.XmlContentInternal setter does not trigger any event
// content.UpdateDocumentCache(List<Document> Documents) does not trigger any event

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
static class UmbracoContextCache
{
static readonly ConditionalWeakTable<UmbracoContext, ConcurrentDictionary<string, object>> Caches
= new ConditionalWeakTable<UmbracoContext, ConcurrentDictionary<string, object>>();
public static ConcurrentDictionary<string, object> Current
{
get
{
var umbracoContext = UmbracoContext.Current;
// will get or create a value
// a ConditionalWeakTable is thread-safe
// does not prevent the context from being disposed, and then the dictionary will be disposed too
return umbracoContext == null ? null : Caches.GetOrCreateValue(umbracoContext);
}
}
}
}

View File

@@ -0,0 +1,449 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Models;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
/// <summary>
/// Represents an IPublishedContent which is created based on an Xml structure.
/// </summary>
[Serializable]
[XmlType(Namespace = "http://umbraco.org/webservices/")]
internal class XmlPublishedContent : PublishedContentBase
{
/// <summary>
/// Initializes a new instance of the <c>XmlPublishedContent</c> class with an Xml node.
/// </summary>
/// <param name="xmlNode">The Xml node.</param>
/// <param name="isPreviewing">A value indicating whether the published content is being previewed.</param>
public XmlPublishedContent(XmlNode xmlNode, bool isPreviewing)
{
_xmlNode = xmlNode;
_isPreviewing = isPreviewing;
InitializeStructure();
Initialize();
InitializeChildren();
}
/// <summary>
/// Initializes a new instance of the <c>XmlPublishedContent</c> class with an Xml node,
/// and a value indicating whether to lazy-initialize the instance.
/// </summary>
/// <param name="xmlNode">The Xml node.</param>
/// <param name="isPreviewing">A value indicating whether the published content is being previewed.</param>
/// <param name="lazyInitialize">A value indicating whether to lazy-initialize the instance.</param>
/// <remarks>Lazy-initializationg is NOT thread-safe.</remarks>
internal XmlPublishedContent(XmlNode xmlNode, bool isPreviewing, bool lazyInitialize)
{
_xmlNode = xmlNode;
_isPreviewing = isPreviewing;
InitializeStructure();
if (lazyInitialize == false)
{
Initialize();
InitializeChildren();
}
}
private readonly XmlNode _xmlNode;
private bool _initialized;
private bool _childrenInitialized;
private readonly ICollection<IPublishedContent> _children = new Collection<IPublishedContent>();
private IPublishedContent _parent;
private int _id;
private int _template;
private string _name;
private string _docTypeAlias;
private int _docTypeId;
private string _writerName;
private string _creatorName;
private int _writerId;
private int _creatorId;
private string _urlName;
private string _path;
private DateTime _createDate;
private DateTime _updateDate;
private Guid _version;
private IPublishedProperty[] _properties;
private int _sortOrder;
private int _level;
private bool _isDraft;
private readonly bool _isPreviewing;
private PublishedContentType _contentType;
public override IEnumerable<IPublishedContent> Children
{
get
{
if (_initialized == false)
Initialize();
if (_childrenInitialized == false)
InitializeChildren();
return _children.OrderBy(x => x.SortOrder);
}
}
public override IPublishedProperty GetProperty(string alias)
{
return Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
}
// override to implement cache
// cache at context level, ie once for the whole request
// but cache is not shared by requests because we wouldn't know how to clear it
public override IPublishedProperty GetProperty(string alias, bool recurse)
{
if (recurse == false) return GetProperty(alias);
var cache = UmbracoContextCache.Current;
if (cache == null)
return base.GetProperty(alias, true);
var key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant());
var value = cache.GetOrAdd(key, k => base.GetProperty(alias, true));
if (value == null)
return null;
var property = value as IPublishedProperty;
if (property == null)
throw new InvalidOperationException("Corrupted cache.");
return property;
}
public override PublishedItemType ItemType
{
get { return PublishedItemType.Content; }
}
public override IPublishedContent Parent
{
get
{
if (_initialized == false)
Initialize();
return _parent;
}
}
public override int Id
{
get
{
if (_initialized == false)
Initialize();
return _id;
}
}
public override int TemplateId
{
get
{
if (_initialized == false)
Initialize();
return _template;
}
}
public override int SortOrder
{
get
{
if (_initialized == false)
Initialize();
return _sortOrder;
}
}
public override string Name
{
get
{
if (_initialized == false)
Initialize();
return _name;
}
}
public override string DocumentTypeAlias
{
get
{
if (_initialized == false)
Initialize();
return _docTypeAlias;
}
}
public override int DocumentTypeId
{
get
{
if (_initialized == false)
Initialize();
return _docTypeId;
}
}
public override string WriterName
{
get
{
if (_initialized == false)
Initialize();
return _writerName;
}
}
public override string CreatorName
{
get
{
if (_initialized == false)
Initialize();
return _creatorName;
}
}
public override int WriterId
{
get
{
if (_initialized == false)
Initialize();
return _writerId;
}
}
public override int CreatorId
{
get
{
if (_initialized == false)
Initialize();
return _creatorId;
}
}
public override string Path
{
get
{
if (_initialized == false)
Initialize();
return _path;
}
}
public override DateTime CreateDate
{
get
{
if (_initialized == false)
Initialize();
return _createDate;
}
}
public override DateTime UpdateDate
{
get
{
if (_initialized == false)
Initialize();
return _updateDate;
}
}
public override Guid Version
{
get
{
if (_initialized == false)
Initialize();
return _version;
}
}
public override string UrlName
{
get
{
if (_initialized == false)
Initialize();
return _urlName;
}
}
public override int Level
{
get
{
if (_initialized == false)
Initialize();
return _level;
}
}
public override bool IsDraft
{
get
{
if (_initialized == false)
Initialize();
return _isDraft;
}
}
public override ICollection<IPublishedProperty> Properties
{
get
{
if (_initialized == false)
Initialize();
return _properties;
}
}
public override PublishedContentType ContentType
{
get
{
if (_initialized == false)
Initialize();
return _contentType;
}
}
private void InitializeStructure()
{
// load parent if it exists and is a node
var parent = _xmlNode == null ? null : _xmlNode.ParentNode;
if (parent == null) return;
if (parent.Name == "node" || (parent.Attributes != null && parent.Attributes.GetNamedItem("isDoc") != null))
_parent = PublishedContentModelFactory.CreateModel(new XmlPublishedContent(parent, _isPreviewing, true));
}
private void Initialize()
{
if (_xmlNode == null) return;
if (_xmlNode.Attributes != null)
{
_id = int.Parse(_xmlNode.Attributes.GetNamedItem("id").Value);
if (_xmlNode.Attributes.GetNamedItem("template") != null)
_template = int.Parse(_xmlNode.Attributes.GetNamedItem("template").Value);
if (_xmlNode.Attributes.GetNamedItem("sortOrder") != null)
_sortOrder = int.Parse(_xmlNode.Attributes.GetNamedItem("sortOrder").Value);
if (_xmlNode.Attributes.GetNamedItem("nodeName") != null)
_name = _xmlNode.Attributes.GetNamedItem("nodeName").Value;
if (_xmlNode.Attributes.GetNamedItem("writerName") != null)
_writerName = _xmlNode.Attributes.GetNamedItem("writerName").Value;
if (_xmlNode.Attributes.GetNamedItem("urlName") != null)
_urlName = _xmlNode.Attributes.GetNamedItem("urlName").Value;
// Creatorname is new in 2.1, so published xml might not have it!
try
{
_creatorName = _xmlNode.Attributes.GetNamedItem("creatorName").Value;
}
catch
{
_creatorName = _writerName;
}
//Added the actual userID, as a user cannot be looked up via full name only...
if (_xmlNode.Attributes.GetNamedItem("creatorID") != null)
_creatorId = int.Parse(_xmlNode.Attributes.GetNamedItem("creatorID").Value);
if (_xmlNode.Attributes.GetNamedItem("writerID") != null)
_writerId = int.Parse(_xmlNode.Attributes.GetNamedItem("writerID").Value);
if (UmbracoSettings.UseLegacyXmlSchema)
{
if (_xmlNode.Attributes.GetNamedItem("nodeTypeAlias") != null)
_docTypeAlias = _xmlNode.Attributes.GetNamedItem("nodeTypeAlias").Value;
}
else
{
_docTypeAlias = _xmlNode.Name;
}
if (_xmlNode.Attributes.GetNamedItem("nodeType") != null)
_docTypeId = int.Parse(_xmlNode.Attributes.GetNamedItem("nodeType").Value);
if (_xmlNode.Attributes.GetNamedItem("path") != null)
_path = _xmlNode.Attributes.GetNamedItem("path").Value;
if (_xmlNode.Attributes.GetNamedItem("version") != null)
_version = new Guid(_xmlNode.Attributes.GetNamedItem("version").Value);
if (_xmlNode.Attributes.GetNamedItem("createDate") != null)
_createDate = DateTime.Parse(_xmlNode.Attributes.GetNamedItem("createDate").Value);
if (_xmlNode.Attributes.GetNamedItem("updateDate") != null)
_updateDate = DateTime.Parse(_xmlNode.Attributes.GetNamedItem("updateDate").Value);
if (_xmlNode.Attributes.GetNamedItem("level") != null)
_level = int.Parse(_xmlNode.Attributes.GetNamedItem("level").Value);
_isDraft = (_xmlNode.Attributes.GetNamedItem("isDraft") != null);
}
// load data
var dataXPath = UmbracoSettings.UseLegacyXmlSchema ? "data" : "* [not(@isDoc)]";
var nodes = _xmlNode.SelectNodes(dataXPath);
_contentType = PublishedContentType.Get(PublishedItemType.Content, _docTypeAlias);
var propertyNodes = new Dictionary<string, XmlNode>();
if (nodes != null)
foreach (XmlNode n in nodes)
{
var alias = UmbracoSettings.UseLegacyXmlSchema
? n.Attributes.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
_properties = _contentType.PropertyTypes.Select(p =>
{
XmlNode n;
return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n)
? new XmlPublishedProperty(p, _isPreviewing, n)
: new XmlPublishedProperty(p, _isPreviewing);
}).Cast<IPublishedProperty>().ToArray();
// warn: this is not thread-safe...
_initialized = true;
}
private void InitializeChildren()
{
if (_xmlNode == null) return;
// load children
var childXPath = UmbracoSettings.UseLegacyXmlSchema ? "node" : "* [@isDoc]";
var nav = _xmlNode.CreateNavigator();
var expr = nav.Compile(childXPath);
expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
var iterator = nav.Select(expr);
while (iterator.MoveNext())
_children.Add(PublishedContentModelFactory.CreateModel(
new XmlPublishedContent(((IHasXmlNode)iterator.Current).GetNode(), _isPreviewing, true)));
// warn: this is not thread-safe
_childrenInitialized = true;
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Xml;
using System.Xml.Serialization;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Models;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
/// <summary>
/// Represents an IDocumentProperty which is created based on an Xml structure.
/// </summary>
[Serializable]
[XmlType(Namespace = "http://umbraco.org/webservices/")]
internal class XmlPublishedProperty : PublishedPropertyBase
{
private readonly string _xmlValue; // the raw, xml node value
private readonly Lazy<object> _sourceValue;
private readonly Lazy<object> _objectValue;
private readonly Lazy<object> _xpathValue;
private readonly bool _isPreviewing;
/// <summary>
/// Gets the raw value of the property.
/// </summary>
public override object DataValue { get { return _xmlValue; } }
// in the Xml cache, everything is a string, and to have a value
// you want to have a non-null, non-empty string.
public override bool HasValue
{
get { return _xmlValue.Trim().Length > 0; }
}
public override object ObjectValue { get { return _objectValue.Value; } }
public override object XPathValue { get { return _xpathValue.Value; } }
public XmlPublishedProperty(PublishedPropertyType propertyType, bool isPreviewing, XmlNode propertyXmlData)
: this(propertyType, isPreviewing)
{
if (propertyXmlData == null)
throw new ArgumentNullException("propertyXmlData", "Property xml source is null");
_xmlValue = XmlHelper.GetNodeValue(propertyXmlData);
}
public XmlPublishedProperty(PublishedPropertyType propertyType, bool isPreviewing, string propertyData)
: this(propertyType, isPreviewing)
{
if (propertyData == null)
throw new ArgumentNullException("propertyData");
_xmlValue = propertyData;
}
public XmlPublishedProperty(PublishedPropertyType propertyType, bool isPreviewing)
: base(propertyType)
{
_xmlValue = string.Empty;
_isPreviewing = isPreviewing;
_sourceValue = new Lazy<object>(() => PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing));
_objectValue = new Lazy<object>(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, _isPreviewing));
_xpathValue = new Lazy<object>(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, _isPreviewing));
}
}
}