2012-09-08 13:22:45 +07:00
using System ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.IO ;
using System.Linq ;
using System.Xml.XPath ;
using Examine ;
using Umbraco.Core ;
using Umbraco.Core.Dynamics ;
using Umbraco.Core.Models ;
namespace Umbraco.Web
{
/// <summary>
/// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database
/// </summary>
2012-09-14 09:09:23 +07:00
/// <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>
2012-09-08 13:22:45 +07:00
internal class DefaultPublishedMediaStore : IPublishedMediaStore
{
public virtual IDocument GetDocumentById ( UmbracoContext umbracoContext , int nodeId )
{
return GetUmbracoMedia ( nodeId ) ;
}
private IDocument GetUmbracoMedia ( int id )
{
try
{
//first check in Examine as this is WAY faster
var criteria = ExamineManager . Instance
. SearchProviderCollection [ "InternalSearcher" ]
. CreateSearchCriteria ( "media" ) ;
var filter = criteria . Id ( id ) ;
var results = ExamineManager
. Instance . SearchProviderCollection [ "InternalSearcher" ]
. Search ( filter . Compile ( ) ) ;
if ( results . Any ( ) )
{
return ConvertFromSearchResult ( results . First ( ) ) ;
}
}
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!
}
var media = global :: umbraco . library . GetMedia ( id , true ) ;
if ( media ! = null & & media . Current ! = null )
{
media . MoveNext ( ) ;
return ConvertFromXPathNavigator ( media . Current ) ;
}
return null ;
}
internal IDocument ConvertFromSearchResult ( SearchResult searchResult )
{
//TODO: Unit test this
2012-09-14 09:09:23 +07:00
//NOTE: we could just use the ExamineExtensions.ConvertFromSearchResult method but it will be faster to just
// use the data store in Examine cache.
2012-09-08 13:22:45 +07:00
throw new NotImplementedException ( ) ;
}
internal IDocument ConvertFromXPathNavigator ( XPathNavigator xpath )
{
//TODO: Unit test this
if ( xpath = = null ) throw new ArgumentNullException ( "xpath" ) ;
var values = new Dictionary < string , string > { { "nodeName" , xpath . GetAttribute ( "nodeName" , "" ) } } ;
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 ( ) )
{
values . Add ( result . Current . Name , result . Current . Value ) ;
while ( result . Current . MoveToNextAttribute ( ) )
{
values . Add ( result . Current . Name , result . Current . Value ) ;
}
result . Current . MoveToParent ( ) ;
}
}
//add the user props
while ( result . MoveNext ( ) )
{
if ( result . Current ! = null & & ! result . Current . HasAttributes )
{
string value = result . Current . Value ;
if ( string . IsNullOrEmpty ( value ) )
{
if ( result . Current . HasAttributes | | result . Current . SelectChildren ( XPathNodeType . Element ) . Count > 0 )
{
value = result . Current . OuterXml ;
}
}
values . Add ( result . Current . Name , value ) ;
}
}
2012-09-20 03:47:24 +07:00
return new DictionaryDocument ( values ,
d = > d . ParentId . HasValue ? GetUmbracoMedia ( d . ParentId . Value ) : null ,
//TODO: Fix this!
d = > Enumerable . Empty < IDocument > ( ) ) ;
2012-09-08 13:22:45 +07:00
}
/// <summary>
/// An IDocument 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 IDocument exist in the dictionary by specific aliases.
/// </remarks>
internal class DictionaryDocument : IDocument
{
//TODO: Unit test this!
2012-09-20 03:47:24 +07:00
public DictionaryDocument (
IDictionary < string , string > valueDictionary ,
Func < DictionaryDocument , IDocument > getParent ,
Func < DictionaryDocument , IEnumerable < IDocument > > getChildren )
2012-09-08 13:22:45 +07:00
{
if ( valueDictionary = = null ) throw new ArgumentNullException ( "valueDictionary" ) ;
if ( getParent = = null ) throw new ArgumentNullException ( "getParent" ) ;
_getParent = getParent ;
2012-09-20 03:47:24 +07:00
_getChildren = getChildren ;
2012-09-08 13:22:45 +07:00
ValidateAndSetProperty ( valueDictionary , val = > Id = int . Parse ( val ) , "id" , "nodeId" , "__NodeId" ) ; //should validate the int!
ValidateAndSetProperty ( valueDictionary , val = > TemplateId = int . Parse ( val ) , "template" , "templateId" ) ;
ValidateAndSetProperty ( valueDictionary , val = > SortOrder = int . Parse ( val ) , "sortOrder" ) ;
ValidateAndSetProperty ( valueDictionary , val = > Name = val , "nodeName" , "__nodeName" ) ;
ValidateAndSetProperty ( valueDictionary , val = > UrlName = val , "urlName" ) ;
ValidateAndSetProperty ( valueDictionary , val = > DocumentTypeAlias = val , "nodeTypeAlias" , "__NodeTypeAlias" ) ;
ValidateAndSetProperty ( valueDictionary , val = > DocumentTypeId = int . Parse ( val ) , "nodeType" ) ;
ValidateAndSetProperty ( valueDictionary , val = > WriterName = val , "writerName" ) ;
ValidateAndSetProperty ( valueDictionary , val = > CreatorName = val , "creatorName" ) ;
ValidateAndSetProperty ( valueDictionary , val = > WriterId = int . Parse ( val ) , "writerID" ) ;
ValidateAndSetProperty ( valueDictionary , val = > CreatorId = int . Parse ( val ) , "creatorID" ) ;
ValidateAndSetProperty ( valueDictionary , val = > Path = val , "path" , "__Path" ) ;
ValidateAndSetProperty ( valueDictionary , val = > CreateDate = DateTime . Parse ( val ) , "createDate" ) ;
ValidateAndSetProperty ( valueDictionary , val = > Level = int . Parse ( val ) , "level" ) ;
ValidateAndSetProperty ( valueDictionary , val = >
{
int pId ;
ParentId = null ;
if ( int . TryParse ( val , out pId ) )
{
ParentId = pId ;
}
} , "parentID" ) ;
Properties = new Collection < IDocumentProperty > ( ) ;
//loop through remaining values that haven't been applied
foreach ( var i in valueDictionary . Where ( x = > ! _keysAdded . Contains ( x . Key ) ) )
{
//this is taken from examine
Properties . Add ( i . Key . InvariantStartsWith ( "__" )
? new PropertyResult ( i . Key , i . Value , Guid . Empty , PropertyResultType . CustomProperty )
: new PropertyResult ( i . Key , i . Value , Guid . Empty , PropertyResultType . UserProperty ) ) ;
}
}
private readonly Func < DictionaryDocument , IDocument > _getParent ;
2012-09-20 03:47:24 +07:00
private readonly Func < DictionaryDocument , IEnumerable < IDocument > > _getChildren ;
2012-09-08 13:22:45 +07:00
public IDocument Parent
{
get { return _getParent ( this ) ; }
}
public int? ParentId { get ; private set ; }
public int Id { get ; private set ; }
public int TemplateId { get ; private set ; }
public int SortOrder { get ; private set ; }
public string Name { get ; private set ; }
public string UrlName { get ; private set ; }
public string DocumentTypeAlias { get ; private set ; }
public int DocumentTypeId { get ; private set ; }
public string WriterName { get ; private set ; }
public string CreatorName { get ; private set ; }
public int WriterId { get ; private set ; }
public int CreatorId { get ; private set ; }
public string Path { get ; private set ; }
public DateTime CreateDate { get ; private set ; }
public DateTime UpdateDate { get ; private set ; }
public Guid Version { get ; private set ; }
public int Level { get ; private set ; }
public Collection < IDocumentProperty > Properties { get ; private set ; }
2012-09-20 03:47:24 +07:00
public IEnumerable < IDocument > Children
{
get { return _getChildren ( this ) ; }
}
2012-09-08 13:22:45 +07:00
private readonly List < string > _keysAdded = new List < string > ( ) ;
private void ValidateAndSetProperty ( IDictionary < string , string > valueDictionary , Action < string > setProperty , params string [ ] potentialKeys )
{
foreach ( var s in potentialKeys )
{
if ( valueDictionary [ s ] = = null )
throw new FormatException ( "The valueDictionary is not formatted correctly and is missing the '" + s + "' element" ) ;
setProperty ( valueDictionary [ s ] ) ;
_keysAdded . Add ( s ) ;
break ;
}
}
}
}
}