Renormalize

This commit is contained in:
Stephan
2018-06-29 19:52:40 +02:00
parent c1f3de7e5c
commit 7a615133ff
1616 changed files with 273322 additions and 273322 deletions

View File

@@ -1,115 +1,115 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
// Note: RoutesCache closely follows the caching strategy dating from v4, which
// is obviously broken in many ways (eg it's a global cache but relying to some
// extend to the content cache, which itself is local to each request...).
// Not going to fix it anyway.
class RoutesCache
{
private ConcurrentDictionary<int, string> _routes;
private ConcurrentDictionary<string, int> _nodeIds;
// NOTE
// RoutesCache is cleared by
// - ContentTypeCacheRefresher, whenever anything happens to any content type
// - DomainCacheRefresher, whenever anything happens to any domain
// - XmlStore, whenever anything happens to the XML cache
/// <summary>
/// Initializes a new instance of the <see cref="RoutesCache"/> class.
/// </summary>
public RoutesCache()
{
Clear();
}
/// <summary>
/// Used ONLY for unit tests
/// </summary>
/// <returns></returns>
internal IDictionary<int, string> GetCachedRoutes()
{
return _routes;
}
/// <summary>
/// Used ONLY for unit tests
/// </summary>
/// <returns></returns>
internal IDictionary<string, int> GetCachedIds()
{
return _nodeIds;
}
#region Public
/// <summary>
/// Stores a route for a node.
/// </summary>
/// <param name="nodeId">The node identified.</param>
/// <param name="route">The route.</param>
/// <param name="trust">A value indicating whether the value can be trusted for inbound routing.</param>
public void Store(int nodeId, string route, bool trust)
{
_routes.AddOrUpdate(nodeId, i => route, (i, s) => route);
if (trust)
_nodeIds.AddOrUpdate(route, i => nodeId, (i, s) => nodeId);
}
/// <summary>
/// Gets a route for a node.
/// </summary>
/// <param name="nodeId">The node identifier.</param>
/// <returns>The route for the node, else null.</returns>
public string GetRoute(int nodeId)
{
string val;
_routes.TryGetValue(nodeId, out val);
return val;
}
/// <summary>
/// Gets a node for a route.
/// </summary>
/// <param name="route">The route.</param>
/// <returns>The node identified for the route, else zero.</returns>
public int GetNodeId(string route)
{
int val;
_nodeIds.TryGetValue(route, out val);
return val;
}
/// <summary>
/// Clears the route for a node.
/// </summary>
/// <param name="nodeId">The node identifier.</param>
public void ClearNode(int nodeId)
{
string route;
if (_routes.TryRemove(nodeId, out route))
{
int id;
_nodeIds.TryRemove(route, out id);
}
}
/// <summary>
/// Clears all routes.
/// </summary>
public void Clear()
{
_routes = new ConcurrentDictionary<int, string>();
_nodeIds = new ConcurrentDictionary<string, int>();
}
#endregion
}
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
// Note: RoutesCache closely follows the caching strategy dating from v4, which
// is obviously broken in many ways (eg it's a global cache but relying to some
// extend to the content cache, which itself is local to each request...).
// Not going to fix it anyway.
class RoutesCache
{
private ConcurrentDictionary<int, string> _routes;
private ConcurrentDictionary<string, int> _nodeIds;
// NOTE
// RoutesCache is cleared by
// - ContentTypeCacheRefresher, whenever anything happens to any content type
// - DomainCacheRefresher, whenever anything happens to any domain
// - XmlStore, whenever anything happens to the XML cache
/// <summary>
/// Initializes a new instance of the <see cref="RoutesCache"/> class.
/// </summary>
public RoutesCache()
{
Clear();
}
/// <summary>
/// Used ONLY for unit tests
/// </summary>
/// <returns></returns>
internal IDictionary<int, string> GetCachedRoutes()
{
return _routes;
}
/// <summary>
/// Used ONLY for unit tests
/// </summary>
/// <returns></returns>
internal IDictionary<string, int> GetCachedIds()
{
return _nodeIds;
}
#region Public
/// <summary>
/// Stores a route for a node.
/// </summary>
/// <param name="nodeId">The node identified.</param>
/// <param name="route">The route.</param>
/// <param name="trust">A value indicating whether the value can be trusted for inbound routing.</param>
public void Store(int nodeId, string route, bool trust)
{
_routes.AddOrUpdate(nodeId, i => route, (i, s) => route);
if (trust)
_nodeIds.AddOrUpdate(route, i => nodeId, (i, s) => nodeId);
}
/// <summary>
/// Gets a route for a node.
/// </summary>
/// <param name="nodeId">The node identifier.</param>
/// <returns>The route for the node, else null.</returns>
public string GetRoute(int nodeId)
{
string val;
_routes.TryGetValue(nodeId, out val);
return val;
}
/// <summary>
/// Gets a node for a route.
/// </summary>
/// <param name="route">The route.</param>
/// <returns>The node identified for the route, else zero.</returns>
public int GetNodeId(string route)
{
int val;
_nodeIds.TryGetValue(route, out val);
return val;
}
/// <summary>
/// Clears the route for a node.
/// </summary>
/// <param name="nodeId">The node identifier.</param>
public void ClearNode(int nodeId)
{
string route;
if (_routes.TryRemove(nodeId, out route))
{
int id;
_nodeIds.TryRemove(route, out id);
}
}
/// <summary>
/// Clears all routes.
/// </summary>
public void Clear()
{
_routes = new ConcurrentDictionary<int, string>();
_nodeIds = new ConcurrentDictionary<string, int>();
}
#endregion
}
}

View File

@@ -1,28 +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);
}
}
}
}
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

@@ -1,434 +1,434 @@
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;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Composing;
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
{
private XmlPublishedContent(XmlNode xmlNode, bool isPreviewing, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache)
{
_xmlNode = xmlNode;
_isPreviewing = isPreviewing;
_cacheProvider = cacheProvider;
_contentTypeCache = contentTypeCache;
}
private readonly XmlNode _xmlNode;
private readonly bool _isPreviewing;
private readonly ICacheProvider _cacheProvider; // at snapshot/request level (see PublishedContentCache)
private readonly PublishedContentTypeCache _contentTypeCache;
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 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 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;
}
public override PublishedItemType ItemType => 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 override Guid Key
{
get
{
if (_nodeInitialized == false) InitializeNode();
return _key;
}
}
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;
}
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;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Composing;
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
{
private XmlPublishedContent(XmlNode xmlNode, bool isPreviewing, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache)
{
_xmlNode = xmlNode;
_isPreviewing = isPreviewing;
_cacheProvider = cacheProvider;
_contentTypeCache = contentTypeCache;
}
public override PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException();
public override IReadOnlyDictionary<string, PublishedCultureInfo> Cultures => throw new NotSupportedException();
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 string UrlSegment
{
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 IEnumerable<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 parent = _xmlNode?.ParentNode;
if (parent == null) return;
if (parent.Attributes?.GetNamedItem("isDoc") != null)
_parent = Get(parent, _isPreviewing, _cacheProvider, _contentTypeCache);
// warn: this is not thread-safe...
_parentInitialized = true;
}
private void InitializeNode()
{
InitializeNode(this, _xmlNode, _isPreviewing,
out _id, out _key, out _template, out _sortOrder, out _name, out _writerName,
out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path,
out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties,
_contentTypeCache.Get);
// warn: this is not thread-safe...
_nodeInitialized = true;
}
internal static void InitializeNode(XmlPublishedContent node, XmlNode xmlNode, bool isPreviewing,
out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName,
out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path,
out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft,
out PublishedContentType contentType, out Dictionary<string, IPublishedProperty> properties,
Func<PublishedItemType, string, PublishedContentType> getPublishedContentType)
{
//initialize the out params with defaults:
writerName = null;
docTypeAlias = null;
id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int);
key = default(Guid);
name = writerName = urlName = creatorName = docTypeAlias = path = null;
createDate = updateDate = default(DateTime);
isDraft = false;
contentType = null;
properties = null;
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);
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;
if (xmlNode.Attributes.GetNamedItem("creatorName") != null)
creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value;
//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);
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("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;
}
//dictionary to store the property node data
var propertyNodes = new Dictionary<string, XmlNode>();
foreach (XmlNode n in xmlNode.ChildNodes)
{
var e = n as XmlElement;
if (e == null) continue;
if (e.HasAttribute("isDoc") == false)
{
PopulatePropertyNodes(propertyNodes, e, false);
}
else break; //we are not longer on property elements
}
//lookup the content type and create the properties collection
try
{
contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias);
}
catch (InvalidOperationException e)
{
// fixme - enable!
//content.Instance.RefreshContentFromDatabase();
throw new InvalidOperationException($"{e.Message}. This usually indicates that the content cache is corrupt; the content cache has been rebuilt in an attempt to self-fix the issue.");
}
//fill in the property collection
properties = new Dictionary<string, IPublishedProperty>(StringComparer.OrdinalIgnoreCase);
foreach (var propertyType in contentType.PropertyTypes)
{
var val = propertyNodes.TryGetValue(propertyType.Alias.ToLowerInvariant(), out XmlNode n)
? new XmlPublishedProperty(propertyType, node, isPreviewing, n)
: new XmlPublishedProperty(propertyType, node, isPreviewing);
properties[propertyType.Alias] = val;
}
}
private static void PopulatePropertyNodes(IDictionary<string, XmlNode> propertyNodes, XmlNode n, bool legacy)
{
var attrs = n.Attributes;
if (attrs == null) return;
var alias = legacy
? attrs.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
private void InitializeChildren()
{
if (_xmlNode == null) return;
// load children
const string childXPath = "* [@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, _cacheProvider, _contentTypeCache))
.OrderBy(x => x.SortOrder)
.ToList();
// warn: this is not thread-safe
_childrenInitialized = true;
}
/// <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>
/// <param name="cacheProvider">A cache provider.</param>
/// <param name="contentTypeCache">A content type cache.</param>
/// <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>
public static IPublishedContent Get(XmlNode node, bool isPreviewing, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache)
{
// only 1 per request
var attrs = node.Attributes;
var id = attrs?.GetNamedItem("id").Value;
if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute.");
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());
}
public static void ClearRequest()
{
Current.ApplicationCache.RequestCache.ClearCacheByKeySearch(CacheKeyPrefix);
}
private const string CacheKeyPrefix = "CONTENTCACHE_XMLPUBLISHEDCONTENT_";
}
}
private readonly XmlNode _xmlNode;
private readonly bool _isPreviewing;
private readonly ICacheProvider _cacheProvider; // at snapshot/request level (see PublishedContentCache)
private readonly PublishedContentTypeCache _contentTypeCache;
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 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 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;
}
public override PublishedItemType ItemType => 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 override Guid Key
{
get
{
if (_nodeInitialized == false) InitializeNode();
return _key;
}
}
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 PublishedCultureInfo GetCulture(string culture = null) => throw new NotSupportedException();
public override IReadOnlyDictionary<string, PublishedCultureInfo> Cultures => throw new NotSupportedException();
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 string UrlSegment
{
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 IEnumerable<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 parent = _xmlNode?.ParentNode;
if (parent == null) return;
if (parent.Attributes?.GetNamedItem("isDoc") != null)
_parent = Get(parent, _isPreviewing, _cacheProvider, _contentTypeCache);
// warn: this is not thread-safe...
_parentInitialized = true;
}
private void InitializeNode()
{
InitializeNode(this, _xmlNode, _isPreviewing,
out _id, out _key, out _template, out _sortOrder, out _name, out _writerName,
out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path,
out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties,
_contentTypeCache.Get);
// warn: this is not thread-safe...
_nodeInitialized = true;
}
internal static void InitializeNode(XmlPublishedContent node, XmlNode xmlNode, bool isPreviewing,
out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName,
out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path,
out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft,
out PublishedContentType contentType, out Dictionary<string, IPublishedProperty> properties,
Func<PublishedItemType, string, PublishedContentType> getPublishedContentType)
{
//initialize the out params with defaults:
writerName = null;
docTypeAlias = null;
id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int);
key = default(Guid);
name = writerName = urlName = creatorName = docTypeAlias = path = null;
createDate = updateDate = default(DateTime);
isDraft = false;
contentType = null;
properties = null;
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);
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;
if (xmlNode.Attributes.GetNamedItem("creatorName") != null)
creatorName = xmlNode.Attributes.GetNamedItem("creatorName").Value;
//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);
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("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;
}
//dictionary to store the property node data
var propertyNodes = new Dictionary<string, XmlNode>();
foreach (XmlNode n in xmlNode.ChildNodes)
{
var e = n as XmlElement;
if (e == null) continue;
if (e.HasAttribute("isDoc") == false)
{
PopulatePropertyNodes(propertyNodes, e, false);
}
else break; //we are not longer on property elements
}
//lookup the content type and create the properties collection
try
{
contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias);
}
catch (InvalidOperationException e)
{
// fixme - enable!
//content.Instance.RefreshContentFromDatabase();
throw new InvalidOperationException($"{e.Message}. This usually indicates that the content cache is corrupt; the content cache has been rebuilt in an attempt to self-fix the issue.");
}
//fill in the property collection
properties = new Dictionary<string, IPublishedProperty>(StringComparer.OrdinalIgnoreCase);
foreach (var propertyType in contentType.PropertyTypes)
{
var val = propertyNodes.TryGetValue(propertyType.Alias.ToLowerInvariant(), out XmlNode n)
? new XmlPublishedProperty(propertyType, node, isPreviewing, n)
: new XmlPublishedProperty(propertyType, node, isPreviewing);
properties[propertyType.Alias] = val;
}
}
private static void PopulatePropertyNodes(IDictionary<string, XmlNode> propertyNodes, XmlNode n, bool legacy)
{
var attrs = n.Attributes;
if (attrs == null) return;
var alias = legacy
? attrs.GetNamedItem("alias").Value
: n.Name;
propertyNodes[alias.ToLowerInvariant()] = n;
}
private void InitializeChildren()
{
if (_xmlNode == null) return;
// load children
const string childXPath = "* [@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, _cacheProvider, _contentTypeCache))
.OrderBy(x => x.SortOrder)
.ToList();
// warn: this is not thread-safe
_childrenInitialized = true;
}
/// <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>
/// <param name="cacheProvider">A cache provider.</param>
/// <param name="contentTypeCache">A content type cache.</param>
/// <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>
public static IPublishedContent Get(XmlNode node, bool isPreviewing, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache)
{
// only 1 per request
var attrs = node.Attributes;
var id = attrs?.GetNamedItem("id").Value;
if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute.");
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());
}
public static void ClearRequest()
{
Current.ApplicationCache.RequestCache.ClearCacheByKeySearch(CacheKeyPrefix);
}
private const string CacheKeyPrefix = "CONTENTCACHE_XMLPUBLISHEDCONTENT_";
}
}

View File

@@ -1,77 +1,77 @@
using System;
using System.Xml;
using System.Xml.Serialization;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Xml;
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 _sourceValue; // the raw, xml node value
// Xml cache not using XPath value... and as for the rest...
// we're single threaded here, keep it simple
private object _objectValue;
private bool _objectValueComputed;
private readonly bool _isPreviewing;
private readonly IPublishedContent _content;
/// <summary>
/// Gets the raw value of the property.
/// </summary>
public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
// 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(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0;
public override object GetValue(string culture = null, string segment = null)
{
// NOT caching the source (intermediate) value since we'll never need it
// everything in Xml cache 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 inter = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing);
// initial reference cache level always is .Content
_objectValue = PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Element, inter, _isPreviewing);
_objectValueComputed = true;
return _objectValue;
}
public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); }
public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, XmlNode propertyXmlData)
: this(propertyType, content, isPreviewing)
{
if (propertyXmlData == null)
throw new ArgumentNullException(nameof(propertyXmlData), "Property xml source is null");
_sourceValue = XmlHelper.GetNodeValue(propertyXmlData);
}
public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, string propertyData)
: this(propertyType, content, isPreviewing)
{
if (propertyData == null)
throw new ArgumentNullException(nameof(propertyData));
_sourceValue = propertyData;
}
public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing)
: base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored
{
_sourceValue = string.Empty;
_content = content;
_isPreviewing = isPreviewing;
}
}
}
using System;
using System.Xml;
using System.Xml.Serialization;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Xml;
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 _sourceValue; // the raw, xml node value
// Xml cache not using XPath value... and as for the rest...
// we're single threaded here, keep it simple
private object _objectValue;
private bool _objectValueComputed;
private readonly bool _isPreviewing;
private readonly IPublishedContent _content;
/// <summary>
/// Gets the raw value of the property.
/// </summary>
public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue;
// 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(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0;
public override object GetValue(string culture = null, string segment = null)
{
// NOT caching the source (intermediate) value since we'll never need it
// everything in Xml cache 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 inter = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing);
// initial reference cache level always is .Content
_objectValue = PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Element, inter, _isPreviewing);
_objectValueComputed = true;
return _objectValue;
}
public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); }
public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, XmlNode propertyXmlData)
: this(propertyType, content, isPreviewing)
{
if (propertyXmlData == null)
throw new ArgumentNullException(nameof(propertyXmlData), "Property xml source is null");
_sourceValue = XmlHelper.GetNodeValue(propertyXmlData);
}
public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, string propertyData)
: this(propertyType, content, isPreviewing)
{
if (propertyData == null)
throw new ArgumentNullException(nameof(propertyData));
_sourceValue = propertyData;
}
public XmlPublishedProperty(PublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing)
: base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored
{
_sourceValue = string.Empty;
_content = content;
_isPreviewing = isPreviewing;
}
}
}