Files
Umbraco-CMS/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs

431 lines
13 KiB
C#
Raw Normal View History

2013-11-07 17:16:22 +01:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using Umbraco.Core;
using Umbraco.Core.Cache;
2013-11-07 17:16:22 +01:00
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
2013-11-07 17:16:22 +01:00
{
2016-07-21 11:07:25 +02:00
private XmlPublishedContent(XmlNode xmlNode, bool isPreviewing, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache)
{
_xmlNode = xmlNode;
_isPreviewing = isPreviewing;
_cacheProvider = cacheProvider;
_contentTypeCache = contentTypeCache;
}
private readonly XmlNode _xmlNode;
2016-06-23 09:39:31 +02:00
private readonly bool _isPreviewing;
2016-06-29 14:46:53 +02:00
private readonly ICacheProvider _cacheProvider; // at facade/request level (see PublishedContentCache)
private readonly PublishedContentTypeCache _contentTypeCache;
2016-06-23 09:39:31 +02:00
private bool _nodeInitialized;
private bool _parentInitialized;
2013-11-07 17:16:22 +01:00
private bool _childrenInitialized;
2016-06-23 09:39:31 +02:00
private IEnumerable<IPublishedContent> _children = Enumerable.Empty<IPublishedContent>();
2013-11-07 17:16:22 +01:00
private IPublishedContent _parent;
2016-06-23 09:39:31 +02:00
private PublishedContentType _contentType;
private Dictionary<string, IPublishedProperty> _properties;
private int _id;
private Guid _key;
2013-11-07 17:16:22 +01:00
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 int _sortOrder;
private int _level;
private bool _isDraft;
public override IEnumerable<IPublishedContent> Children
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
if (_childrenInitialized == false) InitializeChildren();
return _children;
2013-11-07 17:16:22 +01:00
}
}
public override IPublishedProperty GetProperty(string alias)
{
2016-06-23 11:31:32 +02:00
if (_nodeInitialized == false) InitializeNode();
IPublishedProperty property;
2016-06-23 09:39:31 +02:00
return _properties.TryGetValue(alias, out property) ? property : null;
2013-11-07 17:16:22 +01:00
}
// 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);
2016-07-20 12:38:57 +02:00
var key = $"XmlPublishedCache.PublishedContentCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}";
var cacheProvider = _cacheProvider;
return cacheProvider.GetCacheItem<IPublishedProperty>(key, () => base.GetProperty(alias, true));
2013-11-07 17:16:22 +01:00
// note: cleared by PublishedContentCache.Resync - any change here must be applied there
2013-11-07 17:16:22 +01:00
}
2016-07-20 12:38:57 +02:00
public override PublishedItemType ItemType => PublishedItemType.Content;
2013-11-07 17:16:22 +01:00
2016-07-20 12:38:57 +02:00
public override IPublishedContent Parent
2013-11-07 17:16:22 +01:00
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
if (_parentInitialized == false) InitializeParent();
2013-11-07 17:16:22 +01:00
return _parent;
}
}
public override int Id
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _id;
}
}
public override Guid Key
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
return _key;
}
}
2013-11-07 17:16:22 +01:00
public override int TemplateId
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _template;
}
}
public override int SortOrder
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _sortOrder;
}
}
public override string Name
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _name;
}
}
public override string DocumentTypeAlias
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _docTypeAlias;
}
}
public override int DocumentTypeId
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _docTypeId;
}
}
public override string WriterName
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _writerName;
}
}
public override string CreatorName
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _creatorName;
}
}
public override int WriterId
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _writerId;
}
}
public override int CreatorId
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _creatorId;
}
}
public override string Path
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _path;
}
}
public override DateTime CreateDate
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _createDate;
}
}
public override DateTime UpdateDate
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _updateDate;
}
}
public override Guid Version
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _version;
}
}
public override string UrlName
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _urlName;
}
}
public override int Level
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _level;
}
}
public override bool IsDraft
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _isDraft;
}
}
public override IEnumerable<IPublishedProperty> Properties
2013-11-07 17:16:22 +01:00
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
return _properties.Values;
2013-11-07 17:16:22 +01:00
}
}
public override PublishedContentType ContentType
{
get
{
2016-06-23 09:39:31 +02:00
if (_nodeInitialized == false) InitializeNode();
2013-11-07 17:16:22 +01:00
return _contentType;
}
}
2016-06-23 09:39:31 +02:00
private void InitializeParent()
2013-11-07 17:16:22 +01:00
{
2016-07-20 12:38:57 +02:00
var parent = _xmlNode?.ParentNode;
2013-11-07 17:16:22 +01:00
if (parent == null) return;
2016-07-20 12:38:57 +02:00
if (parent.Attributes?.GetNamedItem("isDoc") != null)
_parent = Get(parent, _isPreviewing, _cacheProvider, _contentTypeCache);
2016-06-23 09:39:31 +02:00
// warn: this is not thread-safe...
_parentInitialized = true;
2013-11-07 17:16:22 +01:00
}
2016-06-23 09:39:31 +02:00
private void InitializeNode()
2013-11-07 17:16:22 +01:00
{
if (_xmlNode == null) return;
if (_xmlNode.Attributes != null)
{
_id = int.Parse(_xmlNode.Attributes.GetNamedItem("id").Value);
if (_xmlNode.Attributes.GetNamedItem("key") != null) // because, migration
_key = Guid.Parse(_xmlNode.Attributes.GetNamedItem("key").Value);
2013-11-07 17:16:22 +01:00
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;
}
2016-06-23 11:31:32 +02:00
//Added the actual userID, as a user cannot be looked up via full name only...
2013-11-07 17:16:22 +01:00
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);
_docTypeAlias = _xmlNode.Name;
2013-11-07 17:16:22 +01:00
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
2016-07-08 16:32:06 +02:00
const string dataXPath = "* [not(@isDoc)]";
2013-11-07 17:16:22 +01:00
var nodes = _xmlNode.SelectNodes(dataXPath);
_contentType = _contentTypeCache.Get(PublishedItemType.Content, _docTypeAlias);
2013-11-07 17:16:22 +01:00
var propertyNodes = new Dictionary<string, XmlNode>();
if (nodes != null)
foreach (XmlNode n in nodes)
{
var alias = n.Name;
2013-11-07 17:16:22 +01:00
propertyNodes[alias.ToLowerInvariant()] = n;
}
2016-06-29 14:46:53 +02:00
_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>().ToDictionary(
x => x.PropertyTypeAlias,
x => x,
StringComparer.OrdinalIgnoreCase);
2013-11-07 17:16:22 +01:00
// warn: this is not thread-safe...
2016-06-23 09:39:31 +02:00
_nodeInitialized = true;
2013-11-07 17:16:22 +01:00
}
private void InitializeChildren()
{
if (_xmlNode == null) return;
// load children
2016-07-08 16:32:06 +02:00
const string childXPath = "* [@isDoc]";
2013-11-07 17:16:22 +01:00
var nav = _xmlNode.CreateNavigator();
var expr = nav.Compile(childXPath);
2016-06-23 09:39:31 +02:00
//expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
2013-11-07 17:16:22 +01:00
var iterator = nav.Select(expr);
2016-06-23 09:39:31 +02:00
_children = iterator.Cast<XPathNavigator>()
2016-07-20 12:38:57 +02:00
.Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing, _cacheProvider, _contentTypeCache))
2016-06-23 09:39:31 +02:00
.OrderBy(x => x.SortOrder)
.ToList();
2013-11-07 17:16:22 +01:00
// warn: this is not thread-safe
_childrenInitialized = true;
}
2016-06-23 09:39:31 +02:00
/// <summary>
/// Gets an IPublishedContent corresponding to an Xml cache node.
/// </summary>
/// <param name="node">The Xml node.</param>
/// <param name="isPreviewing">A value indicating whether we are previewing or not.</param>
2016-07-20 12:38:57 +02:00
/// <param name="cacheProvider">A cache provider.</param>
/// <param name="contentTypeCache">A content type cache.</param>
2016-06-23 09:39:31 +02:00
/// <returns>The IPublishedContent corresponding to the Xml cache node.</returns>
/// <remarks>Maintains a per-request cache of IPublishedContent items in order to make
/// sure that we create only one instance of each for the duration of a request. The
/// returned IPublishedContent is a model, if models are enabled.</remarks>
2016-07-20 12:38:57 +02:00
public static IPublishedContent Get(XmlNode node, bool isPreviewing, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache)
2016-06-23 09:39:31 +02:00
{
// only 1 per request
var attrs = node.Attributes;
2016-07-20 12:38:57 +02:00
var id = attrs?.GetNamedItem("id").Value;
2016-06-23 09:39:31 +02:00
if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute.");
2016-06-29 14:46:53 +02:00
var key = CacheKeyPrefix + id; // dont bother with preview, wont change during request in Xml cache
return (IPublishedContent) cacheProvider.GetCacheItem(key, () => (new XmlPublishedContent(node, isPreviewing, cacheProvider, contentTypeCache)).CreateModel());
2016-06-23 09:39:31 +02:00
}
2016-07-04 18:21:36 +02:00
public static void ClearRequest()
{
Current.ApplicationCache.RequestCache.ClearCacheByKeySearch(CacheKeyPrefix);
2016-07-04 18:21:36 +02:00
}
private const string CacheKeyPrefix = "CONTENTCACHE_XMLPUBLISHEDCONTENT_";
}
2013-09-05 17:47:13 +02:00
}