Skip to content

Instantly share code, notes, and snippets.

@darrenferguson
Created February 28, 2017 12:51
Show Gist options
  • Save darrenferguson/0566dfba66a4a85ace413dbb553fbf04 to your computer and use it in GitHub Desktop.
Save darrenferguson/0566dfba66a4a85ace413dbb553fbf04 to your computer and use it in GitHub Desktop.
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