Renormalize
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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_";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user