2012-09-08 13:22:45 +07:00
using System ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
2015-06-23 13:57:00 +02:00
using System.Configuration ;
2012-09-08 13:22:45 +07:00
using System.IO ;
using System.Linq ;
2016-08-16 15:41:52 +02:00
using System.Threading ;
2012-09-08 13:22:45 +07:00
using System.Xml.XPath ;
using Examine ;
2016-04-27 12:58:43 +02:00
using Examine.LuceneEngine.Providers ;
2013-02-20 00:13:35 +06:00
using Examine.LuceneEngine.SearchCriteria ;
2012-11-15 21:46:54 +05:00
using Examine.Providers ;
2012-09-08 13:22:45 +07:00
using Umbraco.Core ;
2013-02-22 04:12:24 +06:00
using Umbraco.Core.Logging ;
2012-09-08 13:22:45 +07:00
using Umbraco.Core.Models ;
2013-09-05 17:47:13 +02:00
using Umbraco.Core.Models.PublishedContent ;
2013-02-05 06:31:13 -01:00
using Umbraco.Core.Xml ;
2012-12-09 03:22:11 +05:00
using Umbraco.Web.Models ;
2013-02-19 22:46:44 +06:00
using UmbracoExamine ;
2012-09-20 07:13:45 +07:00
using umbraco ;
2015-06-18 15:36:04 +02:00
using Umbraco.Core.Cache ;
2016-05-26 17:12:04 +02:00
using Umbraco.Core.Services ;
2012-09-08 13:22:45 +07:00
2013-03-22 15:02:26 -01:00
namespace Umbraco.Web.PublishedCache.XmlPublishedCache
2012-09-08 13:22:45 +07:00
{
2016-08-16 15:41:52 +02:00
/// <summary>
/// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database
/// </summary>
/// <remarks>
/// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly.
/// </remarks>
2016-05-26 17:12:04 +02:00
internal class PublishedMediaCache : PublishedCacheBase , IPublishedMediaCache
2016-08-16 15:41:52 +02:00
{
2016-05-26 17:12:04 +02:00
private readonly IMediaService _mediaService ;
2016-07-20 12:38:57 +02:00
private readonly IUserService _userService ;
2016-05-26 17:12:04 +02:00
// by default these are null unless specified by the ctor dedicated to tests
// when they are null the cache derives them from the ExamineManager, see
// method GetExamineManagerSafe().
//
private readonly ILuceneSearcher _searchProvider ;
private readonly BaseIndexProvider _indexProvider ;
private readonly XmlStore _xmlStore ;
private readonly PublishedContentTypeCache _contentTypeCache ;
// must be specified by the ctor
private readonly ICacheProvider _cacheProvider ;
2016-07-20 12:38:57 +02:00
public PublishedMediaCache ( XmlStore xmlStore , IMediaService mediaService , IUserService userService , ICacheProvider cacheProvider , PublishedContentTypeCache contentTypeCache )
2016-05-26 17:12:04 +02:00
: base ( false )
{
if ( mediaService = = null ) throw new ArgumentNullException ( nameof ( mediaService ) ) ;
2016-07-20 12:38:57 +02:00
if ( userService = = null ) throw new ArgumentNullException ( nameof ( userService ) ) ;
_mediaService = mediaService ;
_userService = userService ;
_cacheProvider = cacheProvider ;
2016-05-26 17:12:04 +02:00
_xmlStore = xmlStore ;
_contentTypeCache = contentTypeCache ;
}
2012-11-15 21:46:54 +05:00
2016-08-16 15:41:52 +02:00
/// <summary>
/// Generally used for unit testing to use an explicit examine searcher
/// </summary>
2016-05-26 17:12:04 +02:00
/// <param name="mediaService"></param>
2016-06-29 14:46:53 +02:00
/// <param name="userService"></param>
2016-08-16 15:41:52 +02:00
/// <param name="searchProvider"></param>
/// <param name="indexProvider"></param>
2016-05-26 17:12:04 +02:00
/// <param name="cacheProvider"></param>
/// <param name="contentTypeCache"></param>
2016-07-20 12:38:57 +02:00
internal PublishedMediaCache ( IMediaService mediaService , IUserService userService , ILuceneSearcher searchProvider , BaseIndexProvider indexProvider , ICacheProvider cacheProvider , PublishedContentTypeCache contentTypeCache )
2016-05-26 17:12:04 +02:00
: base ( false )
2016-08-16 15:41:52 +02:00
{
2016-05-26 17:12:04 +02:00
if ( mediaService = = null ) throw new ArgumentNullException ( nameof ( mediaService ) ) ;
2016-07-20 12:38:57 +02:00
if ( userService = = null ) throw new ArgumentNullException ( nameof ( userService ) ) ;
if ( searchProvider = = null ) throw new ArgumentNullException ( nameof ( searchProvider ) ) ;
2016-05-26 17:12:04 +02:00
if ( indexProvider = = null ) throw new ArgumentNullException ( nameof ( indexProvider ) ) ;
2014-11-19 11:35:37 +11:00
2016-05-26 17:12:04 +02:00
_mediaService = mediaService ;
2016-07-20 12:38:57 +02:00
_userService = userService ;
_searchProvider = searchProvider ;
2016-08-16 15:41:52 +02:00
_indexProvider = indexProvider ;
2016-05-26 17:12:04 +02:00
_cacheProvider = cacheProvider ;
_contentTypeCache = contentTypeCache ;
}
2012-11-15 21:46:54 +05:00
2016-08-16 15:41:52 +02:00
static PublishedMediaCache ( )
{
InitializeCacheConfig ( ) ;
}
2015-06-23 13:57:00 +02:00
2016-05-26 17:12:04 +02:00
public override IPublishedContent GetById ( bool preview , int nodeId )
2016-08-16 15:41:52 +02:00
{
return GetUmbracoMedia ( nodeId ) ;
}
2012-09-08 13:22:45 +07:00
2016-11-03 10:31:44 +01:00
public override IPublishedContent GetById ( bool preview , Guid nodeId )
{
throw new NotImplementedException ( ) ;
}
public override bool HasById ( bool preview , int contentId )
2016-05-26 17:12:04 +02:00
{
return GetUmbracoMedia ( contentId ) ! = null ;
}
public override IEnumerable < IPublishedContent > GetAtRoot ( bool preview )
2016-08-16 15:41:52 +02:00
{
2014-11-19 11:35:37 +11:00
//TODO: We should be able to look these ids first in Examine!
2016-05-26 17:12:04 +02:00
var rootMedia = _mediaService . GetRootMedia ( ) ;
2016-08-16 15:41:52 +02:00
return rootMedia . Select ( m = > GetUmbracoMedia ( m . Id ) ) ;
}
2012-10-04 01:31:08 +05:00
2016-05-26 17:12:04 +02:00
public override IPublishedContent GetSingleByXPath ( bool preview , string xpath , XPathVariable [ ] vars )
2013-02-05 06:31:13 -01:00
{
2013-04-03 11:19:10 -02:00
throw new NotImplementedException ( "PublishedMediaCache does not support XPath." ) ;
2016-05-26 17:12:04 +02:00
//var navigator = CreateNavigator(preview);
//var iterator = navigator.Select(xpath, vars);
//return GetSingleByXPath(iterator);
2013-02-05 06:31:13 -01:00
}
2016-05-26 17:12:04 +02:00
public override IPublishedContent GetSingleByXPath ( bool preview , XPathExpression xpath , XPathVariable [ ] vars )
2013-04-10 12:49:45 -02:00
{
throw new NotImplementedException ( "PublishedMediaCache does not support XPath." ) ;
2016-05-26 17:12:04 +02:00
//var navigator = CreateNavigator(preview);
//var iterator = navigator.Select(xpath, vars);
//return GetSingleByXPath(iterator);
2013-04-10 12:49:45 -02:00
}
2016-02-26 11:35:24 +01:00
2016-05-26 17:12:04 +02:00
private IPublishedContent GetSingleByXPath ( XPathNodeIterator iterator )
2013-04-10 12:49:45 -02:00
{
throw new NotImplementedException ( "PublishedMediaCache does not support XPath." ) ;
2016-05-26 17:12:04 +02:00
//if (iterator.MoveNext() == false) return null;
//var idAttr = iterator.Current.GetAttribute("id", "");
//int id;
//return int.TryParse(idAttr, out id) ? GetUmbracoMedia(id) : null;
2013-04-10 12:49:45 -02:00
}
2016-05-26 17:12:04 +02:00
public override IEnumerable < IPublishedContent > GetByXPath ( bool preview , string xpath , XPathVariable [ ] vars )
2013-02-05 06:31:13 -01:00
{
2013-04-03 11:19:10 -02:00
throw new NotImplementedException ( "PublishedMediaCache does not support XPath." ) ;
2016-05-26 17:12:04 +02:00
//var navigator = CreateNavigator(preview);
//var iterator = navigator.Select(xpath, vars);
//return GetByXPath(iterator);
2013-02-05 06:31:13 -01:00
}
2013-04-03 11:19:10 -02:00
2016-05-26 17:12:04 +02:00
public override IEnumerable < IPublishedContent > GetByXPath ( bool preview , XPathExpression xpath , XPathVariable [ ] vars )
2013-04-03 11:19:10 -02:00
{
throw new NotImplementedException ( "PublishedMediaCache does not support XPath." ) ;
2016-05-26 17:12:04 +02:00
//var navigator = CreateNavigator(preview);
//var iterator = navigator.Select(xpath, vars);
//return GetByXPath(iterator);
2013-04-03 11:19:10 -02:00
}
2016-05-26 17:12:04 +02:00
private IEnumerable < IPublishedContent > GetByXPath ( XPathNodeIterator iterator )
{
while ( iterator . MoveNext ( ) )
{
var idAttr = iterator . Current . GetAttribute ( "id" , "" ) ;
int id ;
if ( int . TryParse ( idAttr , out id ) )
yield return GetUmbracoMedia ( id ) ;
}
}
public override XPathNavigator CreateNavigator ( bool preview )
{
throw new NotImplementedException ( "PublishedMediaCache does not support XPath." ) ;
//var doc = _xmlStore.GetMediaXml();
//return doc.CreateNavigator();
}
2013-06-11 09:52:41 +02:00
2016-05-26 17:12:04 +02:00
public override XPathNavigator CreateNodeNavigator ( int id , bool preview )
{
// preview is ignored for media cache
// this code is mostly used when replacing old media.ToXml() code, and that code
// stored the XML attached to the media itself - so for some time in memory - so
// unless we implement some sort of cache here, we're probably degrading perfs.
XPathNavigator navigator = null ;
var node = _xmlStore . GetMediaXmlNode ( id ) ;
if ( node ! = null )
{
navigator = node . CreateNavigator ( ) ;
}
return navigator ;
}
2013-03-20 16:01:49 -01:00
2016-05-26 17:12:04 +02:00
public override bool HasContent ( bool preview ) { throw new NotImplementedException ( ) ; }
private static ExamineManager GetExamineManagerSafe ( )
2016-08-16 15:41:52 +02:00
{
try
{
return ExamineManager . Instance ;
}
catch ( TypeInitializationException )
{
return null ;
}
}
2012-11-15 04:18:23 +05:00
2016-08-16 15:41:52 +02:00
private BaseIndexProvider GetIndexProviderSafe ( )
{
2013-02-22 04:12:24 +06:00
if ( _indexProvider ! = null )
return _indexProvider ;
var eMgr = GetExamineManagerSafe ( ) ;
2016-05-26 17:12:04 +02:00
if ( eMgr = = null ) return null ;
try
{
//by default use the InternalSearcher
2016-11-03 10:31:44 +01:00
var indexer = eMgr . IndexProviderCollection [ Constants . Examine . InternalIndexer ] ;
2016-05-26 17:12:04 +02:00
if ( indexer . IndexerData . IncludeNodeTypes . Any ( ) | | indexer . IndexerData . ExcludeNodeTypes . Any ( ) )
{
2016-09-11 19:57:33 +02:00
Current . Logger . Warn < PublishedMediaCache > ( "The InternalIndexer for examine is configured incorrectly, it should not list any include/exclude node types or field names, it should simply be configured as: " + "<IndexSet SetName=\"InternalIndexSet\" IndexPath=\"~/App_Data/TEMP/ExamineIndexes/Internal/\" />" ) ;
2016-05-26 17:12:04 +02:00
}
return indexer ;
}
catch ( Exception ex )
{
2016-09-11 19:57:33 +02:00
Current . Logger . Error < PublishedMediaCache > ( "Could not retrieve the InternalIndexer" , ex ) ;
2016-05-26 17:12:04 +02:00
//something didn't work, continue returning null.
}
return null ;
2016-08-16 15:41:52 +02:00
}
2013-02-22 04:12:24 +06:00
2016-04-27 12:58:43 +02:00
private ILuceneSearcher GetSearchProviderSafe ( )
2016-08-16 15:41:52 +02:00
{
if ( _searchProvider ! = null )
return _searchProvider ;
2012-11-15 21:46:54 +05:00
2016-08-16 15:41:52 +02:00
var eMgr = GetExamineManagerSafe ( ) ;
2016-05-26 17:12:04 +02:00
if ( eMgr = = null ) return null ;
2016-11-03 10:31:44 +01:00
try
2016-08-16 15:41:52 +02:00
{
2016-11-03 10:31:44 +01:00
//by default use the InternalSearcher
return eMgr . GetSearcher ( Constants . Examine . InternalIndexer ) ;
}
catch ( FileNotFoundException )
{
//Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
//See this thread: http://examine.cdodeplex.com/discussions/264341
//Catch the exception here for the time being, and just fallback to GetMedia
//TODO: Need to fix examine in LB scenarios!
}
catch ( NullReferenceException )
{
//This will occur when the search provider cannot be initialized. In newer examine versions the initialization is lazy and therefore
// the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null
// reference error will occur because the examine settings are null.
2016-08-16 15:41:52 +02:00
}
return null ;
}
2012-11-15 21:46:54 +05:00
2016-08-16 15:41:52 +02:00
private IPublishedContent GetUmbracoMedia ( int id )
{
2015-06-18 15:36:04 +02:00
// this recreates an IPublishedContent and model each time
// it is called, but at least it should NOT hit the database
// nor Lucene each time, relying on the memory cache instead
2016-08-16 15:41:52 +02:00
if ( id < = 0 ) return null ; // fail fast
2016-02-26 11:35:24 +01:00
2016-08-16 15:41:52 +02:00
var cacheValues = GetCacheValues ( id , GetUmbracoMediaCacheValues ) ;
2015-06-18 15:36:04 +02:00
2016-08-16 15:41:52 +02:00
return cacheValues = = null ? null : CreateFromCacheValues ( cacheValues ) ;
}
2015-06-18 15:36:04 +02:00
private CacheValues GetUmbracoMediaCacheValues ( int id )
{
2016-08-16 15:41:52 +02:00
var searchProvider = GetSearchProviderSafe ( ) ;
2016-02-26 11:35:24 +01:00
2016-08-16 15:41:52 +02:00
if ( searchProvider ! = null )
{
try
{
// first check in Examine as this is WAY faster
//
// the filter will create a query like this:
// +(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media
//
// note that since the use of the wildcard, it automatically escapes it in Lucene.
var criteria = searchProvider . CreateSearchCriteria ( "media" ) ;
2016-05-26 17:12:04 +02:00
var filter = criteria . Id ( id ) . Not ( ) . Field ( BaseUmbracoIndexer . IndexPathFieldName , "-1,-21," . MultipleCharacterWildcard ( ) ) ;
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
var result = searchProvider . Search ( filter . Compile ( ) ) . FirstOrDefault ( ) ;
if ( result ! = null ) return ConvertFromSearchResult ( result ) ;
}
catch ( FileNotFoundException ex )
{
//Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
//See this thread: http://examine.cdodeplex.com/discussions/264341
//Catch the exception here for the time being, and just fallback to GetMedia
//TODO: Need to fix examine in LB scenarios!
2016-11-03 10:31:44 +01:00
Current . Logger . Error < PublishedMediaCache > ( "Could not load data from Examine index for media" , ex ) ;
2016-08-16 15:41:52 +02:00
}
}
2014-11-18 12:23:32 +11:00
2016-08-16 15:41:52 +02:00
// don't log a warning here, as it can flood the log in case of eg a media picker referencing a media
// that has been deleted, hence is not in the Examine index anymore (for a good reason). try to get
// the media from the service, first
2016-06-29 14:46:53 +02:00
var media = _mediaService . GetById ( id ) ;
2016-11-02 13:35:32 +01:00
if ( media = = null | | media . Trashed ) return null ; // not found, ok
2016-08-16 15:41:52 +02:00
// so, the media was not found in Examine's index *yet* it exists, which probably indicates that
// the index is corrupted. Or not up-to-date. Log a warning, but only once, and only if seeing the
// error more that a number of times.
var miss = Interlocked . CompareExchange ( ref _examineIndexMiss , 0 , 0 ) ; // volatile read
if ( miss < ExamineIndexMissMax & & Interlocked . Increment ( ref _examineIndexMiss ) = = ExamineIndexMissMax )
2016-11-03 10:31:44 +01:00
Current . Logger . Warn < PublishedMediaCache > ( "Failed ({0} times) to retrieve medias from Examine index and had to load"
2016-08-16 15:41:52 +02:00
+ " them from DB. This may indicate that the Examine index is corrupted." ,
( ) = > ExamineIndexMissMax ) ;
return ConvertFromIMedia ( media ) ;
2016-06-29 14:46:53 +02:00
}
2014-11-19 11:11:14 +11:00
2016-08-16 15:41:52 +02:00
private const int ExamineIndexMissMax = 10 ;
private int _examineIndexMiss ;
2015-06-18 15:36:04 +02:00
internal CacheValues ConvertFromXPathNodeIterator ( XPathNodeIterator media , int id )
2016-08-16 15:41:52 +02:00
{
2016-05-26 17:12:04 +02:00
if ( media ? . Current ! = null )
2014-11-19 11:11:14 +11:00
{
2016-02-26 11:35:24 +01:00
return media . Current . Name . InvariantEquals ( "error" )
? null
2014-11-19 11:11:14 +11:00
: ConvertFromXPathNavigator ( media . Current ) ;
}
2012-09-08 13:22:45 +07:00
2016-09-11 19:57:33 +02:00
Current . Logger . Warn < PublishedMediaCache > (
2014-11-18 12:23:32 +11:00
"Could not retrieve media {0} from Examine index or from legacy library.GetMedia method" ,
( ) = > id ) ;
2014-11-19 11:11:14 +11:00
return null ;
2016-08-16 15:41:52 +02:00
}
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
internal CacheValues ConvertFromSearchResult ( SearchResult searchResult )
{
2016-05-26 17:12:04 +02:00
// note: fixing fields in 7.x, removed by Shan for 8.0
2012-09-20 07:13:45 +07:00
var values = new Dictionary < string , string > ( searchResult . Fields ) ;
2016-05-26 17:12:04 +02:00
2016-08-16 15:41:52 +02:00
return new CacheValues
{
Values = values ,
2015-06-18 15:36:04 +02:00
FromExamine = true
2016-08-16 15:41:52 +02:00
} ;
}
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
internal CacheValues ConvertFromXPathNavigator ( XPathNavigator xpath , bool forceNav = false )
{
2016-05-26 17:12:04 +02:00
if ( xpath = = null ) throw new ArgumentNullException ( nameof ( xpath ) ) ;
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
var values = new Dictionary < string , string > { { "nodeName" , xpath . GetAttribute ( "nodeName" , "" ) } } ;
2016-11-03 10:31:44 +01:00
values [ "nodeTypeAlias" ] = xpath . Name ;
2016-08-16 15:41:52 +02:00
var result = xpath . SelectChildren ( XPathNodeType . Element ) ;
//add the attributes e.g. id, parentId etc
if ( result . Current ! = null & & result . Current . HasAttributes )
{
if ( result . Current . MoveToFirstAttribute ( ) )
{
//checking for duplicate keys because of the 'nodeTypeAlias' might already be added above.
2016-11-03 10:31:44 +01:00
if ( values . ContainsKey ( result . Current . Name ) = = false )
2016-08-16 15:41:52 +02:00
{
values [ result . Current . Name ] = result . Current . Value ;
}
while ( result . Current . MoveToNextAttribute ( ) )
{
2016-11-03 10:31:44 +01:00
if ( values . ContainsKey ( result . Current . Name ) = = false )
2016-08-16 15:41:52 +02:00
{
values [ result . Current . Name ] = result . Current . Value ;
}
}
result . Current . MoveToParent ( ) ;
}
}
2015-09-01 14:49:50 +02:00
// because, migration
2016-08-16 15:41:52 +02:00
if ( values . ContainsKey ( "key" ) = = false )
values [ "key" ] = Guid . Empty . ToString ( ) ;
//add the user props
while ( result . MoveNext ( ) )
{
2016-11-03 10:31:44 +01:00
if ( result . Current ! = null & & result . Current . HasAttributes = = false )
2016-08-16 15:41:52 +02:00
{
2016-11-03 10:31:44 +01:00
var value = result . Current . Value ;
2016-08-16 15:41:52 +02:00
if ( string . IsNullOrEmpty ( value ) )
{
if ( result . Current . HasAttributes | | result . Current . SelectChildren ( XPathNodeType . Element ) . Count > 0 )
{
value = result . Current . OuterXml ;
}
}
values [ result . Current . Name ] = value ;
}
}
return new CacheValues
{
2015-06-18 15:36:04 +02:00
Values = values ,
XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator!
} ;
2012-09-20 14:17:40 +07:00
}
2016-06-29 14:46:53 +02:00
internal CacheValues ConvertFromIMedia ( IMedia media )
{
var values = new Dictionary < string , string > ( ) ;
2016-06-23 13:33:54 +02:00
2016-06-29 14:46:53 +02:00
var creator = _userService . GetProfileById ( media . CreatorId ) ;
2016-06-23 17:58:01 +02:00
var creatorName = creator = = null ? "" : creator . Name ;
2016-06-29 14:46:53 +02:00
values [ "id" ] = media . Id . ToString ( ) ;
values [ "key" ] = media . Key . ToString ( ) ;
values [ "parentID" ] = media . ParentId . ToString ( ) ;
values [ "level" ] = media . Level . ToString ( ) ;
values [ "creatorID" ] = media . CreatorId . ToString ( ) ;
values [ "creatorName" ] = creatorName ;
2016-06-23 17:58:01 +02:00
values [ "writerID" ] = media . CreatorId . ToString ( ) ;
2016-06-29 14:46:53 +02:00
values [ "writerName" ] = creatorName ;
2016-06-23 13:33:54 +02:00
values [ "template" ] = "0" ;
values [ "urlName" ] = "" ;
values [ "sortOrder" ] = media . SortOrder . ToString ( ) ;
2016-06-29 14:46:53 +02:00
values [ "createDate" ] = media . CreateDate . ToString ( "yyyy-MM-dd HH:mm:ss" ) ;
values [ "updateDate" ] = media . UpdateDate . ToString ( "yyyy-MM-dd HH:mm:ss" ) ;
values [ "nodeName" ] = media . Name ;
values [ "path" ] = media . Path ;
values [ "nodeType" ] = media . ContentType . Id . ToString ( ) ;
values [ "nodeTypeAlias" ] = media . ContentType . Alias ;
2016-06-23 13:33:54 +02:00
// add the user props
2016-06-29 14:46:53 +02:00
foreach ( var prop in media . Properties )
values [ prop . Alias ] = prop . Value ? . ToString ( ) ;
2016-06-23 13:33:54 +02:00
return new CacheValues
{
Values = values
} ;
}
/// <summary>
/// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists
/// in the results, if it does not, then we'll have to revert to looking up in the db.
/// </summary>
/// <param name="dd"> </param>
/// <param name="alias"></param>
/// <returns></returns>
private IPublishedProperty GetProperty ( DictionaryPublishedContent dd , string alias )
2016-08-16 15:41:52 +02:00
{
2014-05-01 11:36:17 +10:00
//lets check if the alias does not exist on the document.
//NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations
// would mean that the property is missing from the collection whether we are getting the value from Examine or from the library media cache.
2014-05-09 10:21:31 +02:00
if ( dd . Properties . All ( x = > x . PropertyTypeAlias . InvariantEquals ( alias ) = = false ) )
2014-05-01 11:36:17 +10:00
{
return null ;
}
2012-09-20 14:17:40 +07:00
2016-08-16 15:41:52 +02:00
if ( dd . LoadedFromExamine )
{
//We are going to check for a special field however, that is because in some cases we store a 'Raw'
//value in the index such as for xml/html.
2016-11-03 10:31:44 +01:00
var rawValue = dd . Properties . FirstOrDefault ( x = > x . PropertyTypeAlias . InvariantEquals ( BaseUmbracoIndexer . RawFieldPrefix + alias ) ) ;
2016-08-16 15:41:52 +02:00
return rawValue
? ? dd . Properties . FirstOrDefault ( x = > x . PropertyTypeAlias . InvariantEquals ( alias ) ) ;
}
2014-05-01 11:36:17 +10:00
//if its not loaded from examine, then just return the property
2016-08-16 15:41:52 +02:00
return dd . Properties . FirstOrDefault ( x = > x . PropertyTypeAlias . InvariantEquals ( alias ) ) ;
}
2012-09-20 07:13:45 +07:00
2016-08-16 15:41:52 +02:00
/// <summary>
/// A Helper methods to return the children for media whther it is based on examine or xml
/// </summary>
/// <param name="parentId"></param>
/// <param name="xpath"></param>
/// <returns></returns>
private IEnumerable < IPublishedContent > GetChildrenMedia ( int parentId , XPathNavigator xpath = null )
{
2012-09-20 07:13:45 +07:00
2016-08-16 15:41:52 +02:00
//if there is no navigator, try examine first, then re-look it up
if ( xpath = = null )
{
var searchProvider = GetSearchProviderSafe ( ) ;
2012-11-15 04:18:23 +05:00
2016-08-16 15:41:52 +02:00
if ( searchProvider ! = null )
{
try
{
//first check in Examine as this is WAY faster
2016-11-03 10:31:44 +01:00
var criteria = searchProvider . CreateCriteria ( "media" ) ;
2016-02-26 11:35:24 +01:00
2016-04-28 18:39:52 +02:00
var filter = criteria . ParentId ( parentId ) . Not ( ) . Field ( BaseUmbracoIndexer . IndexPathFieldName , "-1,-21," . MultipleCharacterWildcard ( ) ) ;
2014-04-23 13:34:57 +10:00
//the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene.
//+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media
2016-11-03 10:31:44 +01:00
// sort with the Sort field (updated for 8.0)
2016-04-28 18:39:52 +02:00
var results = searchProvider . Find (
filter . And ( ) . OrderBy ( new SortableField ( "sortOrder" , SortType . Int ) ) . Compile ( ) ) ;
if ( results . Any ( ) )
2016-08-16 15:41:52 +02:00
{
2015-06-18 15:36:04 +02:00
// var medias = results.Select(ConvertFromSearchResult);
2016-08-16 15:41:52 +02:00
var medias = results . Select ( x = >
{
int nid ;
if ( int . TryParse ( x [ "__NodeId" ] , out nid ) = = false & & int . TryParse ( x [ "NodeId" ] , out nid ) = = false )
throw new Exception ( "Failed to extract NodeId from search result." ) ;
var cacheValues = GetCacheValues ( nid , id = > ConvertFromSearchResult ( x ) ) ;
return CreateFromCacheValues ( cacheValues ) ;
} ) ;
2016-11-03 10:31:44 +01:00
return medias ;
2016-08-16 15:41:52 +02:00
}
2016-05-26 17:12:04 +02:00
//if there's no result then return null. Previously we defaulted back to library.GetMedia below
//but this will always get called for when we are getting descendents since many items won't have
//children and then we are hitting the database again!
//So instead we're going to rely on Examine to have the correct results like it should.
return Enumerable . Empty < IPublishedContent > ( ) ;
2016-08-16 15:41:52 +02:00
}
catch ( FileNotFoundException )
{
//Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
//See this thread: http://examine.cdodeplex.com/discussions/264341
//Catch the exception here for the time being, and just fallback to GetMedia
}
}
2013-05-07 19:02:36 -10:00
//falling back to get media
2012-09-20 07:13:45 +07:00
2016-08-16 15:41:52 +02:00
var media = library . GetMedia ( parentId , true ) ;
2016-11-03 10:31:44 +01:00
if ( media ? . Current ! = null )
2016-08-16 15:41:52 +02:00
{
xpath = media . Current ;
}
else
{
2013-05-07 19:02:36 -10:00
return Enumerable . Empty < IPublishedContent > ( ) ;
2016-08-16 15:41:52 +02:00
}
}
2012-09-20 07:13:45 +07:00
2015-06-18 15:36:04 +02:00
var mediaList = new List < IPublishedContent > ( ) ;
// this is so bad, really
var item = xpath . Select ( "//*[@id='" + parentId + "']" ) ;
if ( item . Current = = null )
2013-05-07 19:02:36 -10:00
return Enumerable . Empty < IPublishedContent > ( ) ;
2015-06-18 15:36:04 +02:00
var items = item . Current . SelectChildren ( XPathNodeType . Element ) ;
// and this does not work, because... meh
2016-08-16 15:41:52 +02:00
//var q = "//* [@id='" + parentId + "']/* [@id]";
2015-06-18 15:36:04 +02:00
//var items = xpath.Select(q);
2016-08-16 15:41:52 +02:00
foreach ( XPathNavigator itemm in items )
{
int id ;
if ( int . TryParse ( itemm . GetAttribute ( "id" , "" ) , out id ) = = false )
continue ; // wtf?
var captured = itemm ;
var cacheValues = GetCacheValues ( id , idd = > ConvertFromXPathNavigator ( captured ) ) ;
mediaList . Add ( CreateFromCacheValues ( cacheValues ) ) ;
}
2015-06-18 15:36:04 +02:00
////The xpath might be the whole xpath including the current ones ancestors so we need to select the current node
//var item = xpath.Select("//*[@id='" + parentId + "']");
//if (item.Current == null)
//{
// return Enumerable.Empty<IPublishedContent>();
//}
//var children = item.Current.SelectChildren(XPathNodeType.Element);
//foreach(XPathNavigator x in children)
//{
// //NOTE: I'm not sure why this is here, it is from legacy code of ExamineBackedMedia, but
// // will leave it here as it must have done something!
// if (x.Name != "contents")
// {
2016-02-26 11:35:24 +01:00
// //make sure it's actually a node, not a property
2015-06-18 15:36:04 +02:00
// if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) &&
// !string.IsNullOrEmpty(x.GetAttribute("id", "")))
// {
// mediaList.Add(ConvertFromXPathNavigator(x));
// }
2016-02-26 11:35:24 +01:00
// }
2015-06-18 15:36:04 +02:00
//}
2012-09-20 07:13:45 +07:00
2016-08-16 15:41:52 +02:00
return mediaList ;
}
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
/// <summary>
/// An IPublishedContent that is represented all by a dictionary.
/// </summary>
/// <remarks>
/// This is a helper class and definitely not intended for public use, it expects that all of the values required
/// to create an IPublishedContent exist in the dictionary by specific aliases.
/// </remarks>
2016-11-03 10:31:44 +01:00
internal class DictionaryPublishedContent : PublishedContentBase
2016-08-16 15:41:52 +02:00
{
2013-09-05 17:47:13 +02:00
// note: I'm not sure this class fully complies with IPublishedContent rules especially
// I'm not sure that _properties contains all properties including those without a value,
// neither that GetProperty will return a property without a value vs. null... @zpqrtbnk
2012-09-08 13:22:45 +07:00
2014-01-30 08:45:43 +01:00
// List of properties that will appear in the XML and do not match
// anything in the ContentType, so they must be ignored.
2015-09-01 14:49:50 +02:00
private static readonly string [ ] IgnoredKeys = { "version" , "isDoc" } ;
2014-01-30 08:45:43 +01:00
2016-08-16 15:41:52 +02:00
public DictionaryPublishedContent (
IDictionary < string , string > valueDictionary ,
Func < int , IPublishedContent > getParent ,
Func < int , XPathNavigator , IEnumerable < IPublishedContent > > getChildren ,
Func < DictionaryPublishedContent , string , IPublishedProperty > getProperty ,
2016-05-26 17:12:04 +02:00
ICacheProvider cacheProvider ,
PublishedContentTypeCache contentTypeCache ,
2015-06-18 15:36:04 +02:00
XPathNavigator nav ,
2016-08-16 15:41:52 +02:00
bool fromExamine )
{
2016-05-26 17:12:04 +02:00
if ( valueDictionary = = null ) throw new ArgumentNullException ( nameof ( valueDictionary ) ) ;
if ( getParent = = null ) throw new ArgumentNullException ( nameof ( getParent ) ) ;
if ( getProperty = = null ) throw new ArgumentNullException ( nameof ( getProperty ) ) ;
2012-09-20 14:17:40 +07:00
2016-08-16 15:41:52 +02:00
_getParent = new Lazy < IPublishedContent > ( ( ) = > getParent ( ParentId ) ) ;
_getChildren = new Lazy < IEnumerable < IPublishedContent > > ( ( ) = > getChildren ( Id , nav ) ) ;
_getProperty = getProperty ;
2016-05-26 17:12:04 +02:00
_cacheProvider = cacheProvider ;
2012-09-20 14:17:40 +07:00
2016-08-16 15:41:52 +02:00
LoadedFromExamine = fromExamine ;
2012-09-20 03:47:24 +07:00
2016-08-16 15:41:52 +02:00
ValidateAndSetProperty ( valueDictionary , val = > _id = int . Parse ( val ) , "id" , "nodeId" , "__NodeId" ) ; //should validate the int!
2016-05-26 17:12:04 +02:00
ValidateAndSetProperty ( valueDictionary , val = > _key = Guid . Parse ( val ) , "key" ) ;
//ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId");
2016-08-16 15:41:52 +02:00
ValidateAndSetProperty ( valueDictionary , val = > _sortOrder = int . Parse ( val ) , "sortOrder" ) ;
ValidateAndSetProperty ( valueDictionary , val = > _name = val , "nodeName" , "__nodeName" ) ;
ValidateAndSetProperty ( valueDictionary , val = > _urlName = val , "urlName" ) ;
2016-04-28 18:39:52 +02:00
ValidateAndSetProperty ( valueDictionary , val = > _documentTypeAlias = val , "nodeTypeAlias" , LuceneIndexer . NodeTypeAliasFieldName ) ;
2016-08-16 15:41:52 +02:00
ValidateAndSetProperty ( valueDictionary , val = > _documentTypeId = int . Parse ( val ) , "nodeType" ) ;
2016-05-26 17:12:04 +02:00
//ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName");
2016-08-16 15:41:52 +02:00
ValidateAndSetProperty ( valueDictionary , val = > _creatorName = val , "creatorName" , "writerName" ) ; //this is a bit of a hack fix for: U4-1132
2016-05-26 17:12:04 +02:00
//ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID");
2016-08-16 15:41:52 +02:00
ValidateAndSetProperty ( valueDictionary , val = > _creatorId = int . Parse ( val ) , "creatorID" , "writerID" ) ; //this is a bit of a hack fix for: U4-1132
ValidateAndSetProperty ( valueDictionary , val = > _path = val , "path" , "__Path" ) ;
ValidateAndSetProperty ( valueDictionary , val = > _createDate = ParseDateTimeValue ( val ) , "createDate" ) ;
ValidateAndSetProperty ( valueDictionary , val = > _updateDate = ParseDateTimeValue ( val ) , "updateDate" ) ;
ValidateAndSetProperty ( valueDictionary , val = > _level = int . Parse ( val ) , "level" ) ;
ValidateAndSetProperty ( valueDictionary , val = >
{
int pId ;
ParentId = - 1 ;
if ( int . TryParse ( val , out pId ) )
{
ParentId = pId ;
}
} , "parentID" ) ;
2012-09-08 13:22:45 +07:00
2016-05-26 17:12:04 +02:00
_contentType = contentTypeCache . Get ( PublishedItemType . Media , _documentTypeAlias ) ;
2016-08-16 15:41:52 +02:00
_properties = new Collection < IPublishedProperty > ( ) ;
2012-09-08 13:22:45 +07:00
2014-05-26 15:01:06 +02:00
//handle content type properties
//make sure we create them even if there's no value
2016-08-16 15:41:52 +02:00
foreach ( var propertyType in _contentType . PropertyTypes )
{
var alias = propertyType . PropertyTypeAlias ;
2014-05-26 15:01:06 +02:00
_keysAdded . Add ( alias ) ;
string value ;
const bool isPreviewing = false ; // false :: never preview a media
2016-06-23 13:33:54 +02:00
var property = valueDictionary . TryGetValue ( alias , out value ) = = false | | value = = null
2016-02-26 11:35:24 +01:00
? new XmlPublishedProperty ( propertyType , isPreviewing )
2014-05-26 15:01:06 +02:00
: new XmlPublishedProperty ( propertyType , isPreviewing , value ) ;
_properties . Add ( property ) ;
2016-08-16 15:41:52 +02:00
}
2014-05-26 15:01:06 +02:00
2016-08-16 15:41:52 +02:00
//loop through remaining values that haven't been applied
foreach ( var i in valueDictionary . Where ( x = >
2014-05-26 15:01:06 +02:00
_keysAdded . Contains ( x . Key ) = = false // not already processed
& & IgnoredKeys . Contains ( x . Key ) = = false ) ) // not ignorable
2016-08-16 15:41:52 +02:00
{
2013-09-05 17:47:13 +02:00
if ( i . Key . InvariantStartsWith ( "__" ) )
2014-05-26 15:01:06 +02:00
{
2013-09-23 21:57:22 +02:00
// no type for that one, dunno how to convert
2014-05-26 15:01:06 +02:00
IPublishedProperty property = new PropertyResult ( i . Key , i . Value , PropertyResultType . CustomProperty ) ;
_properties . Add ( property ) ;
}
2013-09-05 17:47:13 +02:00
else
{
2014-05-26 15:01:06 +02:00
// this is a property that does not correspond to anything, ignore and log
2016-09-11 19:57:33 +02:00
Current . Logger . Warn < PublishedMediaCache > ( "Dropping property \"" + i . Key + "\" because it does not belong to the content type." ) ;
2013-09-05 17:47:13 +02:00
}
2016-08-16 15:41:52 +02:00
}
}
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
private DateTime ParseDateTimeValue ( string val )
{
2016-05-26 17:12:04 +02:00
if ( LoadedFromExamine = = false )
return DateTime . Parse ( val ) ;
2012-11-15 21:46:54 +05:00
2016-05-26 17:12:04 +02:00
//we need to parse the date time using Lucene converters
var ticks = long . Parse ( val ) ;
return new DateTime ( ticks ) ;
2016-08-16 15:41:52 +02:00
}
2012-11-15 21:46:54 +05:00
2016-08-16 15:41:52 +02:00
/// <summary>
/// Flag to get/set if this was laoded from examine cache
/// </summary>
2016-05-26 17:12:04 +02:00
internal bool LoadedFromExamine { get ; }
2012-09-20 14:17:40 +07:00
2016-08-16 15:41:52 +02:00
//private readonly Func<DictionaryPublishedContent, IPublishedContent> _getParent;
private readonly Lazy < IPublishedContent > _getParent ;
//private readonly Func<DictionaryPublishedContent, IEnumerable<IPublishedContent>> _getChildren;
private readonly Lazy < IEnumerable < IPublishedContent > > _getChildren ;
private readonly Func < DictionaryPublishedContent , string , IPublishedProperty > _getProperty ;
2016-05-26 17:12:04 +02:00
private readonly ICacheProvider _cacheProvider ;
2012-09-20 03:47:24 +07:00
2016-08-16 15:41:52 +02:00
/// <summary>
/// Returns 'Media' as the item type
/// </summary>
2016-05-26 17:12:04 +02:00
public override PublishedItemType ItemType = > PublishedItemType . Media ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override IPublishedContent Parent = > _getParent . Value ;
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
public int ParentId { get ; private set ; }
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override int Id = > _id ;
2015-09-01 14:49:50 +02:00
2016-05-26 17:12:04 +02:00
public override Guid Key = > _key ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override int TemplateId = > 0 ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override int SortOrder = > _sortOrder ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override string Name = > _name ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override string UrlName = > _urlName ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override string DocumentTypeAlias = > _documentTypeAlias ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override int DocumentTypeId = > _documentTypeId ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override string WriterName = > _creatorName ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override string CreatorName = > _creatorName ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override int WriterId = > _creatorId ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override int CreatorId = > _creatorId ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override string Path = > _path ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override DateTime CreateDate = > _createDate ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override DateTime UpdateDate = > _updateDate ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override Guid Version = > Guid . Empty ;
2012-12-09 03:22:11 +05:00
2016-05-26 17:12:04 +02:00
public override int Level = > _level ;
2013-09-05 17:47:13 +02:00
2016-05-26 17:12:04 +02:00
public override bool IsDraft = > false ;
2012-12-09 03:22:11 +05:00
2016-06-10 16:37:28 +02:00
public override IEnumerable < IPublishedProperty > Properties = > _properties ;
2016-05-26 17:12:04 +02:00
public override IEnumerable < IPublishedContent > Children = > _getChildren . Value ;
2012-09-08 13:22:45 +07:00
2016-08-16 15:41:52 +02:00
public override IPublishedProperty GetProperty ( string alias )
{
return _getProperty ( this , alias ) ;
}
2012-09-20 14:17:40 +07:00
2016-05-26 17:12:04 +02:00
public override PublishedContentType ContentType = > _contentType ;
2013-09-05 17:47:13 +02:00
2016-05-26 17:12:04 +02:00
// override to implement cache
2013-09-05 17:47:13 +02:00
// 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 ) ;
2016-05-26 17:12:04 +02:00
var key = $"XmlPublishedCache.PublishedMediaCache:RecursiveProperty-{Id}-{alias.ToLowerInvariant()}" ;
var cacheProvider = _cacheProvider ;
return cacheProvider . GetCacheItem < IPublishedProperty > ( key , ( ) = > base . GetProperty ( alias , true ) ) ;
2013-09-05 17:47:13 +02:00
}
2016-08-16 15:41:52 +02:00
private readonly List < string > _keysAdded = new List < string > ( ) ;
private int _id ;
private Guid _key ;
2016-11-03 10:31:44 +01:00
//private int _templateId;
2016-08-16 15:41:52 +02:00
private int _sortOrder ;
private string _name ;
private string _urlName ;
private string _documentTypeAlias ;
private int _documentTypeId ;
2016-11-03 10:31:44 +01:00
//private string _writerName;
2016-08-16 15:41:52 +02:00
private string _creatorName ;
2016-11-03 10:31:44 +01:00
//private int _writerId;
2016-08-16 15:41:52 +02:00
private int _creatorId ;
private string _path ;
private DateTime _createDate ;
private DateTime _updateDate ;
2016-11-03 10:31:44 +01:00
//private Guid _version;
2016-08-16 15:41:52 +02:00
private int _level ;
private readonly ICollection < IPublishedProperty > _properties ;
private readonly PublishedContentType _contentType ;
private void ValidateAndSetProperty ( IDictionary < string , string > valueDictionary , Action < string > setProperty , params string [ ] potentialKeys )
{
var key = potentialKeys . FirstOrDefault ( x = > valueDictionary . ContainsKey ( x ) & & valueDictionary [ x ] ! = null ) ;
if ( key = = null )
{
throw new FormatException ( "The valueDictionary is not formatted correctly and is missing any of the '" + string . Join ( "," , potentialKeys ) + "' elements" ) ;
}
setProperty ( valueDictionary [ key ] ) ;
_keysAdded . Add ( key ) ;
}
2013-09-05 17:47:13 +02:00
}
2015-06-18 15:36:04 +02:00
2016-05-26 17:12:04 +02:00
internal void Resync ( )
{
// clear recursive properties cached by XmlPublishedContent.GetProperty
// assume that nothing else is going to cache IPublishedProperty items (else would need to do ByKeySearch)
// NOTE all properties cleared when clearing the content cache (see content cache)
//_cacheProvider.ClearCacheObjectTypes<IPublishedProperty>();
//_cacheProvider.ClearCacheByKeySearch("XmlPublishedCache.PublishedMediaCache:RecursiveProperty-");
}
#region Content types
public override PublishedContentType GetContentType ( int id )
{
return _contentTypeCache . Get ( PublishedItemType . Media , id ) ;
}
public override PublishedContentType GetContentType ( string alias )
{
return _contentTypeCache . Get ( PublishedItemType . Media , alias ) ;
}
public override IEnumerable < IPublishedContent > GetByContentType ( PublishedContentType contentType )
{
throw new NotImplementedException ( ) ;
}
#endregion
2015-06-18 15:36:04 +02:00
// REFACTORING
// caching the basic atomic values - and the parent id
// but NOT caching actual parent nor children and NOT even
// the list of children ids - BUT caching the path
2016-08-16 15:41:52 +02:00
internal class CacheValues
{
2015-06-18 15:36:04 +02:00
public IDictionary < string , string > Values { get ; set ; }
public XPathNavigator XPath { get ; set ; }
public bool FromExamine { get ; set ; }
2016-08-16 15:41:52 +02:00
}
2015-06-18 15:36:04 +02:00
public const string PublishedMediaCacheKey = "MediaCacheMeh." ;
2016-08-16 15:41:52 +02:00
private const int PublishedMediaCacheTimespanSeconds = 4 * 60 ; // 4 mins
2015-06-23 13:57:00 +02:00
private static TimeSpan _publishedMediaCacheTimespan ;
2016-08-16 15:41:52 +02:00
private static bool _publishedMediaCacheEnabled ;
2015-06-23 13:57:00 +02:00
2016-08-16 15:41:52 +02:00
private static void InitializeCacheConfig ( )
{
var value = ConfigurationManager . AppSettings [ "Umbraco.PublishedMediaCache.Seconds" ] ;
int seconds ;
if ( int . TryParse ( value , out seconds ) = = false )
seconds = PublishedMediaCacheTimespanSeconds ;
if ( seconds > 0 )
{
_publishedMediaCacheEnabled = true ;
_publishedMediaCacheTimespan = TimeSpan . FromSeconds ( seconds ) ;
}
else
{
_publishedMediaCacheEnabled = false ;
}
}
2015-06-18 15:36:04 +02:00
internal IPublishedContent CreateFromCacheValues ( CacheValues cacheValues )
{
var content = new DictionaryPublishedContent (
cacheValues . Values ,
parentId = > parentId < 0 ? null : GetUmbracoMedia ( parentId ) ,
GetChildrenMedia ,
GetProperty ,
2016-05-26 17:12:04 +02:00
_cacheProvider ,
_contentTypeCache ,
2015-06-18 15:36:04 +02:00
cacheValues . XPath , // though, outside of tests, that should be null
cacheValues . FromExamine
) ;
return content . CreateModel ( ) ;
}
2016-08-16 15:41:52 +02:00
private static CacheValues GetCacheValues ( int id , Func < int , CacheValues > func )
{
if ( _publishedMediaCacheEnabled = = false )
return func ( id ) ;
2015-06-23 13:57:00 +02:00
2016-11-03 10:31:44 +01:00
var cache = Current . ApplicationCache . RuntimeCache ;
2015-06-18 15:36:04 +02:00
var key = PublishedMediaCacheKey + id ;
2016-08-16 15:41:52 +02:00
return ( CacheValues ) cache . GetCacheItem ( key , ( ) = > func ( id ) , _publishedMediaCacheTimespan ) ;
}
2015-06-18 15:36:04 +02:00
2016-08-16 15:41:52 +02:00
internal static void ClearCache ( int id )
{
2016-09-01 19:06:08 +02:00
var cache = Current . ApplicationCache . RuntimeCache ;
2016-08-16 15:41:52 +02:00
var sid = id . ToString ( ) ;
2015-06-18 15:36:04 +02:00
var key = PublishedMediaCacheKey + sid ;
// we do clear a lot of things... but the cache refresher is somewhat
// convoluted and it's hard to tell what to clear exactly ;-(
2016-02-26 11:35:24 +01:00
2015-06-18 15:36:04 +02:00
// clear the parent - NOT (why?)
//var exist = (CacheValues) cache.GetCacheItem(key);
//if (exist != null)
// cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID"));
2016-02-26 11:35:24 +01:00
2015-06-18 15:36:04 +02:00
// clear the item
cache . ClearCacheItem ( key ) ;
2016-02-26 11:35:24 +01:00
2015-06-18 15:36:04 +02:00
// clear all children - in case we moved and their path has changed
2016-08-16 15:41:52 +02:00
var fid = "/" + sid + "/" ;
2015-06-18 15:36:04 +02:00
cache . ClearCacheObjectTypes < CacheValues > ( ( k , v ) = >
GetValuesValue ( v . Values , "path" , "__Path" ) . Contains ( fid ) ) ;
}
2016-08-16 15:41:52 +02:00
private static string GetValuesValue ( IDictionary < string , string > d , params string [ ] keys )
{
string value = null ;
var ignored = keys . Any ( x = > d . TryGetValue ( x , out value ) ) ;
2015-06-18 15:36:04 +02:00
return value ? ? "" ;
2016-08-16 15:41:52 +02:00
}
2015-06-18 15:36:04 +02:00
}
2014-03-25 20:44:47 -07:00
}