2016-05-27 14:26:28 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Xml.Serialization;
|
|
|
|
|
|
using Umbraco.Core.Cache;
|
|
|
|
|
|
using Umbraco.Core.Models;
|
|
|
|
|
|
using Umbraco.Core.Models.PublishedContent;
|
|
|
|
|
|
using Umbraco.Core.PropertyEditors;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Web.PublishedCache.NuCache
|
|
|
|
|
|
{
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
|
[XmlType(Namespace = "http://umbraco.org/webservices/")]
|
|
|
|
|
|
class Property : PublishedPropertyBase
|
|
|
|
|
|
{
|
2016-05-30 19:54:36 +02:00
|
|
|
|
private readonly IFacadeAccessor _facadeAccessor;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
private readonly object _dataValue;
|
|
|
|
|
|
private readonly Guid _contentUid;
|
|
|
|
|
|
private readonly bool _isPreviewing;
|
|
|
|
|
|
private readonly bool _isMember;
|
|
|
|
|
|
|
|
|
|
|
|
readonly object _locko = new object();
|
|
|
|
|
|
|
|
|
|
|
|
private ValueSet _valueSet;
|
|
|
|
|
|
private string _valueSetCacheKey;
|
|
|
|
|
|
private string _recurseCacheKey;
|
|
|
|
|
|
|
|
|
|
|
|
// initializes a published content property with no value
|
2016-05-30 19:54:36 +02:00
|
|
|
|
public Property(PublishedPropertyType propertyType, PublishedContent content, IFacadeAccessor facadeAccessor)
|
|
|
|
|
|
: this(propertyType, content, null, facadeAccessor)
|
2016-05-27 14:26:28 +02:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
// initializes a published content property with a value
|
2016-05-30 19:54:36 +02:00
|
|
|
|
public Property(PublishedPropertyType propertyType, PublishedContent content, object valueSource, IFacadeAccessor facadeAccessor)
|
2016-05-27 14:26:28 +02:00
|
|
|
|
: base(propertyType)
|
|
|
|
|
|
{
|
|
|
|
|
|
_dataValue = valueSource;
|
|
|
|
|
|
_contentUid = content.Key;
|
|
|
|
|
|
var inner = PublishedContent.UnwrapIPublishedContent(content);
|
|
|
|
|
|
_isPreviewing = inner.IsPreviewing;
|
|
|
|
|
|
_isMember = content.ContentType.ItemType == PublishedItemType.Member;
|
2016-05-30 19:54:36 +02:00
|
|
|
|
_facadeAccessor = facadeAccessor;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// clone for previewing as draft a published content that is published and has no draft
|
|
|
|
|
|
public Property(Property origin)
|
|
|
|
|
|
: base(origin.PropertyType)
|
|
|
|
|
|
{
|
|
|
|
|
|
_dataValue = origin._dataValue;
|
|
|
|
|
|
_contentUid = origin._contentUid;
|
|
|
|
|
|
_isPreviewing = true;
|
|
|
|
|
|
_isMember = origin._isMember;
|
2016-05-30 19:54:36 +02:00
|
|
|
|
_facadeAccessor = origin._facadeAccessor;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// detached
|
|
|
|
|
|
//internal Property(PublishedPropertyType propertyType, Guid contentUid, object valueSource, bool isPreviewing, bool isMember)
|
|
|
|
|
|
// : base(propertyType)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// _dataValue = valueSource;
|
|
|
|
|
|
// _contentUid = contentUid;
|
|
|
|
|
|
// _isPreviewing = isPreviewing;
|
|
|
|
|
|
// _isMember = isMember;
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
2016-05-30 19:54:36 +02:00
|
|
|
|
public override bool HasValue => _dataValue != null
|
|
|
|
|
|
&& ((_dataValue is string) == false || string.IsNullOrWhiteSpace((string)_dataValue) == false);
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
|
|
|
|
|
private class ValueSet
|
|
|
|
|
|
{
|
|
|
|
|
|
public bool SourceInitialized;
|
|
|
|
|
|
public object Source;
|
|
|
|
|
|
public bool ValueInitialized;
|
|
|
|
|
|
public object Value;
|
|
|
|
|
|
public bool XPathInitialized;
|
|
|
|
|
|
public object XPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-05-30 19:54:36 +02:00
|
|
|
|
internal string RecurseCacheKey => _recurseCacheKey
|
|
|
|
|
|
?? (_recurseCacheKey = CacheKeys.PropertyRecurse(_contentUid, PropertyTypeAlias, _isPreviewing));
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
2016-05-30 19:54:36 +02:00
|
|
|
|
internal string ValueSetCacheKey => _valueSetCacheKey
|
|
|
|
|
|
?? (_valueSetCacheKey = CacheKeys.PropertyValueSet(_contentUid, PropertyTypeAlias, _isPreviewing));
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
|
|
|
|
|
private ValueSet GetValueSet(PropertyCacheLevel cacheLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
ValueSet valueSet;
|
|
|
|
|
|
Facade facade;
|
|
|
|
|
|
ICacheProvider cache;
|
|
|
|
|
|
switch (cacheLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
case PropertyCacheLevel.None:
|
|
|
|
|
|
// never cache anything
|
|
|
|
|
|
valueSet = new ValueSet();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case PropertyCacheLevel.Content:
|
|
|
|
|
|
// cache within the property object itself, ie within the content object
|
|
|
|
|
|
valueSet = _valueSet ?? (_valueSet = new ValueSet());
|
|
|
|
|
|
break;
|
|
|
|
|
|
case PropertyCacheLevel.ContentCache:
|
|
|
|
|
|
// cache within the snapshot cache, unless previewing, then use the facade or
|
|
|
|
|
|
// snapshot cache (if we don't want to pollute the snapshot cache with short-lived
|
|
|
|
|
|
// data) depending on settings
|
|
|
|
|
|
// for members, always cache in the facade cache - never pollute snapshot cache
|
2016-05-30 19:54:36 +02:00
|
|
|
|
facade = (Facade) _facadeAccessor.Facade;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
cache = facade == null
|
|
|
|
|
|
? null
|
|
|
|
|
|
: ((_isPreviewing == false || FacadeService.FullCacheWhenPreviewing) && (_isMember == false)
|
|
|
|
|
|
? facade.SnapshotCache
|
|
|
|
|
|
: facade.FacadeCache);
|
|
|
|
|
|
valueSet = GetValueSet(cache);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case PropertyCacheLevel.Request:
|
|
|
|
|
|
// cache within the facade cache
|
2016-05-30 19:54:36 +02:00
|
|
|
|
facade = (Facade)_facadeAccessor.Facade;
|
|
|
|
|
|
cache = facade?.FacadeCache;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
valueSet = GetValueSet(cache);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
throw new InvalidOperationException("Invalid cache level.");
|
|
|
|
|
|
}
|
|
|
|
|
|
return valueSet;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private ValueSet GetValueSet(ICacheProvider cache)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cache == null) // no cache, don't cache
|
|
|
|
|
|
return new ValueSet();
|
|
|
|
|
|
return (ValueSet) cache.GetCacheItem(ValueSetCacheKey, () => new ValueSet());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private object GetSourceValue()
|
|
|
|
|
|
{
|
|
|
|
|
|
var valueSet = GetValueSet(PropertyType.SourceCacheLevel);
|
|
|
|
|
|
if (valueSet.SourceInitialized == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
valueSet.Source = PropertyType.ConvertDataToSource(_dataValue, _isPreviewing);
|
|
|
|
|
|
valueSet.SourceInitialized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return valueSet.Source;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-05-30 19:54:36 +02:00
|
|
|
|
public override object DataValue => _dataValue;
|
2016-05-27 14:26:28 +02:00
|
|
|
|
|
|
|
|
|
|
public override object Value
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_locko)
|
|
|
|
|
|
{
|
|
|
|
|
|
var valueSet = GetValueSet(PropertyType.ObjectCacheLevel);
|
|
|
|
|
|
if (valueSet.ValueInitialized == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
valueSet.Value = PropertyType.ConvertSourceToObject(GetSourceValue(), _isPreviewing);
|
|
|
|
|
|
valueSet.ValueInitialized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return valueSet.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override object XPathValue
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_locko)
|
|
|
|
|
|
{
|
|
|
|
|
|
var valueSet = GetValueSet(PropertyType.XPathCacheLevel);
|
|
|
|
|
|
if (valueSet.XPathInitialized == false)
|
|
|
|
|
|
{
|
|
|
|
|
|
valueSet.XPath = PropertyType.ConvertSourceToXPath(GetSourceValue(), _isPreviewing);
|
|
|
|
|
|
valueSet.XPathInitialized = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return valueSet.XPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|