Created
February 28, 2017 12:51
-
-
Save darrenferguson/0566dfba66a4a85ace413dbb553fbf04 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyPublishedContent : PublishedContentBase | |
{ | |
public MyPublishedContent(string xml) | |
{ | |
var doc = new XmlDocument(); | |
doc.LoadXml(xml); | |
_xmlNode = doc.DocumentElement; | |
} | |
private readonly XmlNode _xmlNode; | |
private readonly bool _isPreviewing; | |
private bool _nodeInitialized; | |
private bool _parentInitialized; | |
private bool _childrenInitialized; | |
private IEnumerable<IPublishedContent> _children = Enumerable.Empty<IPublishedContent>(); | |
private IPublishedContent _parent; | |
private PublishedContentType _contentType; | |
private Dictionary<string, IPublishedProperty> _properties; | |
private int _id; | |
private int _parentId; | |
private Guid _key; | |
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 | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
if (_childrenInitialized == false) InitializeChildren(); | |
return _children; | |
} | |
} | |
public override IPublishedProperty GetProperty(string alias) | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
IPublishedProperty property; | |
return _properties.TryGetValue(alias, out property) ? property : null; | |
} | |
// 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); | |
return base.GetProperty(alias, true); | |
//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 (_nodeInitialized == false) InitializeNode(); | |
if (_parentInitialized == false) InitializeParent(); | |
return _parent; | |
} | |
} | |
public override int Id | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _id; | |
} | |
} | |
public int ParentId | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _parentId; | |
} | |
} | |
public override int TemplateId | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _template; | |
} | |
} | |
public override int SortOrder | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _sortOrder; | |
} | |
} | |
public override string Name | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _name; | |
} | |
} | |
public override string DocumentTypeAlias | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _docTypeAlias; | |
} | |
} | |
public override int DocumentTypeId | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _docTypeId; | |
} | |
} | |
public override string WriterName | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _writerName; | |
} | |
} | |
public override string CreatorName | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _creatorName; | |
} | |
} | |
public override int WriterId | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _writerId; | |
} | |
} | |
public override int CreatorId | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _creatorId; | |
} | |
} | |
public override string Path | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _path; | |
} | |
} | |
public override DateTime CreateDate | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _createDate; | |
} | |
} | |
public override DateTime UpdateDate | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _updateDate; | |
} | |
} | |
public override Guid Version | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _version; | |
} | |
} | |
public override string UrlName | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _urlName; | |
} | |
} | |
public override int Level | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _level; | |
} | |
} | |
public override bool IsDraft | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _isDraft; | |
} | |
} | |
public override ICollection<IPublishedProperty> Properties | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _properties.Values; | |
} | |
} | |
public override PublishedContentType ContentType | |
{ | |
get | |
{ | |
if (_nodeInitialized == false) InitializeNode(); | |
return _contentType; | |
} | |
} | |
private void InitializeParent() | |
{ | |
var contentCache = UmbracoContext.Current.ContentCache; | |
_parent = contentCache.GetById(_parentId); | |
// warn: this is not thread-safe... | |
_parentInitialized = true; | |
} | |
private void InitializeNode() | |
{ | |
if (_xmlNode == null) return; | |
if (_xmlNode.Attributes != null) | |
{ | |
_id = int.Parse(_xmlNode.Attributes.GetNamedItem("id").Value); | |
_parentId = int.Parse(_xmlNode.Attributes.GetNamedItem("parentID").Value); | |
if (_xmlNode.Attributes.GetNamedItem("key") != null) // because, migration | |
_key = Guid.Parse(_xmlNode.Attributes.GetNamedItem("key").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 (UmbracoConfig.For.UmbracoSettings().Content.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 = UmbracoConfig.For.UmbracoSettings().Content.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 attrs = n.Attributes; | |
if (attrs == null) continue; | |
var alias = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema | |
? attrs.GetNamedItem("alias").Value | |
: n.Name; | |
propertyNodes[alias.ToLowerInvariant()] = n; | |
} | |
_properties = new Dictionary<string, IPublishedProperty>(); | |
_properties = _contentType.PropertyTypes.Select(p => | |
{ | |
XmlNode n; | |
return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n) | |
? new MyPublicProperty(p, _isPreviewing, n) | |
: new MyPublicProperty(p, _isPreviewing); | |
}).Cast<IPublishedProperty>().ToDictionary( | |
x => x.PropertyTypeAlias, | |
x => x, | |
StringComparer.OrdinalIgnoreCase); | |
// warn: this is not thread-safe... sorrymarc | |
_nodeInitialized = true; | |
} | |
private void InitializeChildren() | |
{ | |
return; | |
//if (_xmlNode == null) return; | |
//// load children | |
//var childXPath = UmbracoConfig.For.UmbracoSettings().Content.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); | |
//_children = iterator.Cast<XPathNavigator>() | |
// .Select(n => Get(((IHasXmlNode)n).GetNode(), _isPreviewing)) | |
// .OrderBy(x => x.SortOrder) | |
// .ToList(); | |
//// warn: this is not thread-safe | |
//_childrenInitialized = true; | |
} | |
} | |
public abstract class MyPublishedPropertyBase : IPublishedProperty | |
{ | |
public readonly PublishedPropertyType PropertyType; | |
protected MyPublishedPropertyBase(PublishedPropertyType propertyType) | |
{ | |
if (propertyType == null) | |
throw new ArgumentNullException("propertyType"); | |
PropertyType = propertyType; | |
} | |
public string PropertyTypeAlias | |
{ | |
get { return PropertyType.PropertyTypeAlias; } | |
} | |
// these have to be provided by the actual implementation | |
public abstract bool HasValue { get; } | |
public abstract object DataValue { get; } | |
public abstract object Value { get; } | |
public abstract object XPathValue { get; } | |
} | |
public class MyPublicProperty : MyPublishedPropertyBase | |
{ | |
private readonly string _xmlValue; // the raw, xml node value | |
// in v7 we're not using XPath value so don't allocate that Lazy. | |
// as for the rest... we're single threaded here, keep it simple | |
//private readonly Lazy<object> _sourceValue; | |
//private readonly Lazy<object> _objectValue; | |
//private readonly Lazy<object> _xpathValue; | |
private object _objectValue; | |
private bool _objectValueComputed; | |
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 Value | |
{ | |
get | |
{ | |
// NOT caching the source (intermediate) value since we'll never need it | |
// everything in Xml cache in v7 is per-request anyways | |
// also, properties should not be shared between requests and therefore | |
// are single threaded, so the following code should be safe & fast | |
if (_objectValueComputed) return _objectValue; | |
var sourceValue = PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing); | |
_objectValue = PropertyType.ConvertSourceToObject(sourceValue, _isPreviewing); | |
_objectValueComputed = true; | |
return _objectValue; | |
} | |
} | |
public override object XPathValue { get { throw new NotImplementedException(); } } | |
public MyPublicProperty(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 MyPublicProperty(PublishedPropertyType propertyType, bool isPreviewing, string propertyData) | |
: this(propertyType, isPreviewing) | |
{ | |
if (propertyData == null) | |
throw new ArgumentNullException("propertyData"); | |
_xmlValue = propertyData; | |
} | |
public MyPublicProperty(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)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment