2013-09-17 18:15:19 +02:00
// fixme - should #define - but when will it be OK?
2013-09-05 17:47:13 +02:00
// axes navigation is broken in many ways... but fixes would not be 100%
// backward compatible... so keep them for v7 or whenever appropriate.
#undef FIX_AXES
2012-09-20 12:42:43 +07:00
using System ;
using System.Collections.Generic ;
using System.Data ;
using System.Linq ;
2012-10-04 01:31:08 +05:00
using System.Web ;
2012-10-18 08:00:07 +05:00
using Examine.LuceneEngine.SearchCriteria ;
2012-09-20 12:42:43 +07:00
using Umbraco.Core.Models ;
2013-09-05 17:47:13 +02:00
using Umbraco.Core.Models.PublishedContent ;
2012-10-04 01:31:08 +05:00
using Umbraco.Web.Models ;
2012-09-20 12:42:43 +07:00
using Umbraco.Core ;
2012-12-07 20:08:06 +05:00
using ContentType = umbraco . cms . businesslogic . ContentType ;
2012-09-20 12:42:43 +07:00
namespace Umbraco.Web
{
2013-09-05 17:47:13 +02:00
/// <summary>
/// Provides extension methods for <c>IPublishedContent</c>.
/// </summary>
2012-10-02 22:51:53 +05:00
public static class PublishedContentExtensions
2013-09-05 17:47:13 +02:00
{
#region Urls
2012-12-04 03:26:04 +05:00
2013-09-05 17:47:13 +02:00
/// <summary>
/// Gets the url for the content.
2012-12-04 03:26:04 +05:00
/// </summary>
2013-09-05 17:47:13 +02:00
/// <param name="content">The content.</param>
/// <returns>The url for the content.</returns>
2012-12-09 03:53:16 +05:00
[Obsolete("NiceUrl() is obsolete, use the Url() method instead")]
2013-09-05 17:47:13 +02:00
public static string NiceUrl ( this IPublishedContent content )
2012-12-09 03:53:16 +05:00
{
2013-09-05 17:47:13 +02:00
return content . Url ();
2012-12-09 03:53:16 +05:00
}
/// <summary>
2013-09-05 17:47:13 +02:00
/// Gets the url for the content.
2012-12-09 03:53:16 +05:00
/// </summary>
2013-09-05 17:47:13 +02:00
/// <param name="content">The content.</param>
/// <returns>The url for the content.</returns>
/// <remarks>Better use the <c>Url</c> property but that method is here to complement <c>UrlAbsolute()</c>.</remarks>
public static string Url ( this IPublishedContent content )
2012-10-24 09:59:23 +05:00
{
2013-09-05 17:47:13 +02:00
return content . Url ;
2012-10-24 09:59:23 +05:00
}
/// <summary>
2013-09-05 17:47:13 +02:00
/// Gets the absolute url for the content.
2012-10-24 09:59:23 +05:00
/// </summary>
2013-09-05 17:47:13 +02:00
/// <param name="content">The content.</param>
/// <returns>The absolute url for the content.</returns>
[Obsolete("NiceUrlWithDomain() is obsolete, use the UrlAbsolute() method instead.")]
public static string NiceUrlWithDomain ( this IPublishedContent content )
2012-12-09 03:53:16 +05:00
{
2013-09-05 17:47:13 +02:00
return content . UrlAbsolute ();
2012-12-09 03:53:16 +05:00
}
/// <summary>
2013-09-05 17:47:13 +02:00
/// Gets the absolute url for the content.
2012-12-09 03:53:16 +05:00
/// </summary>
2013-09-05 17:47:13 +02:00
/// <param name="content">The content.</param>
/// <returns>The absolute url for the content.</returns>
//[Obsolete("UrlWithDomain() is obsolete, use the UrlAbsolute() method instead.")]
public static string UrlWithDomain ( this IPublishedContent content )
2012-10-24 09:59:23 +05:00
{
2013-09-05 17:47:13 +02:00
return content . UrlAbsolute ();
2012-10-24 09:59:23 +05:00
}
2013-09-05 17:47:13 +02:00
/// <summary>
/// Gets the absolute url for the content.
/// </summary>
/// <param name="content">The content.</param>
/// <returns>The absolute url for the content.</returns>
public static string UrlAbsolute ( this IPublishedContent content )
{
// adapted from PublishedContentBase.Url
switch ( content . ItemType )
{
case PublishedItemType . Content :
if ( UmbracoContext . Current == null )
throw new InvalidOperationException ( "Cannot resolve a Url for a content item when UmbracoContext.Current is null." );
if ( UmbracoContext . Current . UrlProvider == null )
throw new InvalidOperationException ( "Cannot resolve a Url for a content item when UmbracoContext.Current.UrlProvider is null." );
2014-02-17 08:40:30 +01:00
return UmbracoContext . Current . UrlProvider . GetUrl ( content . Id , true );
2013-09-05 17:47:13 +02:00
case PublishedItemType . Media :
throw new NotSupportedException ( "AbsoluteUrl is not supported for media types." );
default :
throw new ArgumentOutOfRangeException ();
}
}
#endregion
#region Template
/// <summary>
2012-10-18 08:00:07 +05:00
/// Returns the current template Alias
/// </summary>
2013-09-05 17:47:13 +02:00
/// <param name="content"></param>
2012-10-18 08:00:07 +05:00
/// <returns></returns>
2013-09-05 17:47:13 +02:00
public static string GetTemplateAlias ( this IPublishedContent content )
{
var template = ApplicationContext . Current . Services . FileService . GetTemplate ( content . TemplateId );
return template == null ? string . Empty : template . Alias ;
2012-10-18 08:00:07 +05:00
}
2013-09-05 17:47:13 +02:00
#endregion
2012-12-09 03:22:11 +05:00
2013-09-05 17:47:13 +02:00
#region HasProperty
/// <summary>
/// Gets a value indicating whether the content has a property identified by its alias.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <returns>A value indicating whether the content has the property identified by the alias.</returns>
/// <remarks>The content may have a property, and that property may not have a value.</remarks>
public static bool HasProperty ( this IPublishedContent content , string alias )
{
2013-09-17 10:18:26 +02:00
return content . ContentType . GetPropertyType ( alias ) != null ;
2013-09-05 17:47:13 +02:00
}
#endregion
#region HasValue
/// <summary>
/// Gets a value indicating whether the content has a value for a property identified by its alias.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <returns>A value indicating whether the content has a value for the property identified by the alias.</returns>
/// <remarks>Returns true if <c>GetProperty(alias)</c> is not <c>null</c> and <c>GetProperty(alias).HasValue</c> is <c>true</c>.</remarks>
public static bool HasValue ( this IPublishedContent content , string alias )
{
return content . HasValue ( alias , false );
}
/// <summary>
/// Gets a value indicating whether the content has a value for a property identified by its alias.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to navigate the tree upwards until a property with a value is found.</param>
/// <returns>A value indicating whether the content has a value for the property identified by the alias.</returns>
/// <remarks>Returns true if <c>GetProperty(alias, recurse)</c> is not <c>null</c> and <c>GetProperty(alias, recurse).HasValue</c> is <c>true</c>.</remarks>
public static bool HasValue ( this IPublishedContent content , string alias , bool recurse )
{
var prop = content . GetProperty ( alias , recurse );
return prop != null && prop . HasValue ;
}
/// <summary>
/// Returns one of two strings depending on whether the content has a value for a property identified by its alias.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="valueIfTrue">The value to return if the content has a value for the property.</param>
/// <param name="valueIfFalse">The value to return if the content has no value for the property.</param>
/// <returns>Either <paramref name="valueIfTrue"/> or <paramref name="valueIfFalse"/> depending on whether the content
/// has a value for the property identified by the alias.</returns>
public static IHtmlString HasValue ( this IPublishedContent content , string alias ,
string valueIfTrue , string valueIfFalse = null )
{
return content . HasValue ( alias , false )
? new HtmlString ( valueIfTrue )
: new HtmlString ( valueIfFalse ?? string . Empty );
}
/// <summary>
/// Returns one of two strings depending on whether the content has a value for a property identified by its alias.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to navigate the tree upwards until a property with a value is found.</param>
/// <param name="valueIfTrue">The value to return if the content has a value for the property.</param>
/// <param name="valueIfFalse">The value to return if the content has no value for the property.</param>
/// <returns>Either <paramref name="valueIfTrue"/> or <paramref name="valueIfFalse"/> depending on whether the content
/// has a value for the property identified by the alias.</returns>
public static IHtmlString HasValue ( this IPublishedContent content , string alias , bool recurse ,
string valueIfTrue , string valueIfFalse = null )
{
return content . HasValue ( alias , recurse )
? new HtmlString ( valueIfTrue )
: new HtmlString ( valueIfFalse ?? string . Empty );
}
#endregion
#region GetPropertyValue
/// <summary>
/// Gets the value of a content's property identified by its alias.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <returns>The value of the content's property identified by the alias.</returns>
/// <remarks>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, returns <c>null</c>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static object GetPropertyValue ( this IPublishedContent content , string alias )
{
var property = content . GetProperty ( alias );
2013-09-25 13:16:37 +02:00
return property == null ? null : property . Value ;
2013-09-05 17:47:13 +02:00
}
/// <summary>
/// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the content's property identified by the alias, if it exists, otherwise a default value.</returns>
/// <remarks>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, returns <paramref name="defaultValue"/>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static object GetPropertyValue ( this IPublishedContent content , string alias , string defaultValue )
{
var property = content . GetProperty ( alias );
2013-09-25 13:16:37 +02:00
return property == null || property . HasValue == false ? defaultValue : property . Value ;
2013-09-05 17:47:13 +02:00
}
/// <summary>
/// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the content's property identified by the alias, if it exists, otherwise a default value.</returns>
/// <remarks>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, returns <paramref name="defaultValue"/>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static object GetPropertyValue ( this IPublishedContent content , string alias , object defaultValue )
{
var property = content . GetProperty ( alias );
2013-09-25 13:16:37 +02:00
return property == null || property . HasValue == false ? defaultValue : property . Value ;
2013-09-05 17:47:13 +02:00
}
/// <summary>
/// Recursively gets the value of a content's property identified by its alias.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to recurse.</param>
/// <returns>The recursive value of the content's property identified by the alias.</returns>
/// <remarks>
/// <para>Recursively means: walking up the tree from <paramref name="content"/>, get the first value that can be found.</para>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, returns <c>null</c>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static object GetPropertyValue ( this IPublishedContent content , string alias , bool recurse )
{
var property = content . GetProperty ( alias , recurse );
2013-09-25 13:16:37 +02:00
return property == null ? null : property . Value ;
2013-09-05 17:47:13 +02:00
}
/// <summary>
/// Recursively the value of a content's property identified by its alias, if it exists, otherwise a default value.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to recurse.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the content's property identified by the alias, if it exists, otherwise a default value.</returns>
/// <remarks>
/// <para>Recursively means: walking up the tree from <paramref name="content"/>, get the first value that can be found.</para>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, returns <paramref name="defaultValue"/>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static object GetPropertyValue ( this IPublishedContent content , string alias , bool recurse , object defaultValue )
{
var property = content . GetProperty ( alias , recurse );
2013-09-25 13:16:37 +02:00
return property == null || property . HasValue == false ? defaultValue : property . Value ;
2013-09-05 17:47:13 +02:00
}
#endregion
#region GetPropertyValue < T >
/// <summary>
/// Gets the value of a content's property identified by its alias, converted to a specified type.
2012-12-09 03:22:11 +05:00
/// </summary>
2013-09-05 17:47:13 +02:00
/// <typeparam name="T">The target property type.</typeparam>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <returns>The value of the content's property identified by the alias, converted to the specified type.</returns>
/// <remarks>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns <c>default(T)</c>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static T GetPropertyValue < T >( this IPublishedContent content , string alias )
2012-12-09 03:22:11 +05:00
{
2013-09-05 17:47:13 +02:00
return content . GetPropertyValue ( alias , false , false , default ( T ));
2012-12-09 03:22:11 +05:00
}
2013-09-05 17:47:13 +02:00
/// <summary>
/// Gets the value of a content's property identified by its alias, converted to a specified type, if it exists, otherwise a default value.
/// </summary>
/// <typeparam name="T">The target property type.</typeparam>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value.</returns>
/// <remarks>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns <paramref name="defaultValue"/>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static T GetPropertyValue < T >( this IPublishedContent content , string alias , T defaultValue )
{
return content . GetPropertyValue ( alias , false , true , defaultValue );
}
2012-12-09 03:22:11 +05:00
2013-09-05 17:47:13 +02:00
/// <summary>
/// Recursively gets the value of a content's property identified by its alias, converted to a specified type.
/// </summary>
/// <typeparam name="T">The target property type.</typeparam>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to recurse.</param>
/// <returns>The value of the content's property identified by the alias, converted to the specified type.</returns>
/// <remarks>
/// <para>Recursively means: walking up the tree from <paramref name="content"/>, get the first value that can be found.</para>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns <c>default(T)</c>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static T GetPropertyValue < T >( this IPublishedContent content , string alias , bool recurse )
{
return content . GetPropertyValue ( alias , recurse , false , default ( T ));
}
2013-08-29 11:59:07 +10:00
2013-09-05 17:47:13 +02:00
/// <summary>
/// Recursively gets the value of a content's property identified by its alias, converted to a specified type, if it exists, otherwise a default value.
/// </summary>
/// <typeparam name="T">The target property type.</typeparam>
/// <param name="content">The content.</param>
/// <param name="alias">The property alias.</param>
/// <param name="recurse">A value indicating whether to recurse.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the content's property identified by the alias, converted to the specified type, if it exists, otherwise a default value.</returns>
/// <remarks>
/// <para>Recursively means: walking up the tree from <paramref name="content"/>, get the first value that can be found.</para>
/// <para>The value comes from <c>IPublishedProperty</c> field <c>Value</c> ie it is suitable for use when rendering content.</para>
/// <para>If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns <paramref name="defaultValue"/>.</para>
/// <para>If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.</para>
/// <para>The alias is case-insensitive.</para>
/// </remarks>
public static T GetPropertyValue < T >( this IPublishedContent content , string alias , bool recurse , T defaultValue )
{
return content . GetPropertyValue ( alias , recurse , true , defaultValue );
}
2012-12-09 03:22:11 +05:00
2013-09-05 17:47:13 +02:00
internal static T GetPropertyValue < T >( this IPublishedContent content , string alias , bool recurse , bool withDefaultValue , T defaultValue )
{
var property = content . GetProperty ( alias , recurse );
if ( property == null ) return defaultValue ;
2012-12-09 03:22:11 +05:00
2013-09-05 17:47:13 +02:00
return property . GetValue ( withDefaultValue , defaultValue );
2012-12-09 03:22:11 +05:00
}
#endregion
2013-09-05 17:47:13 +02:00
// copied over from Core.PublishedContentExtensions - should be obsoleted
[Obsolete("GetRecursiveValue() is obsolete, use GetPropertyValue().")]
public static string GetRecursiveValue ( this IPublishedContent content , string alias )
{
var value = content . GetPropertyValue ( alias , true );
return value == null ? string . Empty : value . ToString ();
}
2012-10-18 08:00:07 +05:00
#region Search
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > Search ( this IPublishedContent content , string term , bool useWildCards = true , string searchProvider = null )
2012-10-18 08:00:07 +05:00
{
var searcher = Examine . ExamineManager . Instance . DefaultSearchProvider ;
2013-09-05 17:47:13 +02:00
if ( string . IsNullOrEmpty ( searchProvider ) == false )
2012-10-18 08:00:07 +05:00
searcher = Examine . ExamineManager . Instance . SearchProviderCollection [ searchProvider ];
var t = term . Escape (). Value ;
if ( useWildCards )
t = term . MultipleCharacterWildcard (). Value ;
2013-09-05 17:47:13 +02:00
var luceneQuery = "+__Path:(" + content . Path . Replace ( "-" , "\\-" ) + "*) +" + t ;
2012-10-18 08:00:07 +05:00
var crit = searcher . CreateSearchCriteria (). RawQuery ( luceneQuery );
2013-09-05 17:47:13 +02:00
return content . Search ( crit , searcher );
2012-10-18 08:00:07 +05:00
}
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > SearchDescendants ( this IPublishedContent content , string term , bool useWildCards = true , string searchProvider = null )
2012-10-18 08:00:07 +05:00
{
2013-09-05 17:47:13 +02:00
return content . Search ( term , useWildCards , searchProvider );
2012-10-18 08:00:07 +05:00
}
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > SearchChildren ( this IPublishedContent content , string term , bool useWildCards = true , string searchProvider = null )
2012-10-18 08:00:07 +05:00
{
var searcher = Examine . ExamineManager . Instance . DefaultSearchProvider ;
2013-09-05 17:47:13 +02:00
if ( string . IsNullOrEmpty ( searchProvider ) == false )
2012-10-18 08:00:07 +05:00
searcher = Examine . ExamineManager . Instance . SearchProviderCollection [ searchProvider ];
var t = term . Escape (). Value ;
if ( useWildCards )
t = term . MultipleCharacterWildcard (). Value ;
2013-09-05 17:47:13 +02:00
var luceneQuery = "+parentID:" + content . Id + " +" + t ;
2012-10-18 08:00:07 +05:00
var crit = searcher . CreateSearchCriteria (). RawQuery ( luceneQuery );
2013-09-05 17:47:13 +02:00
return content . Search ( crit , searcher );
2012-10-18 08:00:07 +05:00
}
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > Search ( this IPublishedContent content , Examine . SearchCriteria . ISearchCriteria criteria , Examine . Providers . BaseSearchProvider searchProvider = null )
2012-10-18 08:00:07 +05:00
{
var s = Examine . ExamineManager . Instance . DefaultSearchProvider ;
if ( searchProvider != null )
s = searchProvider ;
var results = s . Search ( criteria );
2013-03-19 17:51:55 -01:00
return results . ConvertSearchResultToPublishedContent ( UmbracoContext . Current . ContentCache );
2012-12-09 03:51:02 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-18 08:00:07 +05:00
#endregion
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
#region ToContentSet
2012-10-04 03:26:56 +05:00
2013-09-05 17:47:13 +02:00
/// <summary>
/// Returns the content enumerable as a content set.
/// </summary>
/// <param name="source">The content enumerable.</param>
/// <returns>A content set wrapping the content enumerable.</returns>
public static PublishedContentSet < T > ToContentSet < T >( this IEnumerable < T > source )
where T : class , IPublishedContent
2013-03-02 01:02:38 +06:00
{
2013-09-05 17:47:13 +02:00
return new PublishedContentSet < T >( source );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
/// <summary>
/// Returns the ordered content enumerable as an ordered content set.
/// </summary>
/// <param name="source">The ordered content enumerable.</param>
/// <returns>A ordered content set wrapping the ordered content enumerable.</returns>
public static PublishedContentOrderedSet < T > ToContentSet < T >( this IOrderedEnumerable < T > source )
where T : class , IPublishedContent
2013-03-02 01:02:38 +06:00
{
2013-09-05 17:47:13 +02:00
return new PublishedContentOrderedSet < T >( source );
2013-03-02 01:02:38 +06:00
}
#endregion
#region Dynamic Linq Extensions
2013-09-17 18:15:19 +02:00
// todo - we should keep this file clean and remove dynamic linq stuff from it
2013-09-05 17:47:13 +02:00
public static IQueryable < IPublishedContent > OrderBy ( this IEnumerable < IPublishedContent > source , string predicate )
2012-10-04 03:26:56 +05:00
{
2013-09-05 17:47:13 +02:00
var dList = new DynamicPublishedContentList ( source );
2012-10-04 03:26:56 +05:00
return dList . OrderBy < DynamicPublishedContent >( predicate );
}
public static IQueryable < IPublishedContent > Where ( this IEnumerable < IPublishedContent > list , string predicate )
{
2013-09-17 18:15:19 +02:00
// wrap in DynamicPublishedContentList so that the ContentSet is correct
// though that code is somewhat ugly.
var dlist = new DynamicPublishedContentList ( new DynamicPublishedContentList ( list )
. Where < DynamicPublishedContent >( predicate ));
return dlist . AsQueryable < IPublishedContent >();
2012-10-04 03:26:56 +05:00
}
public static IEnumerable < IGrouping < object , IPublishedContent >> GroupBy ( this IEnumerable < IPublishedContent > list , string predicate )
{
var dList = new DynamicPublishedContentList ( list );
return dList . GroupBy ( predicate );
}
public static IQueryable Select ( this IEnumerable < IPublishedContent > list , string predicate , params object [] values )
{
var dList = new DynamicPublishedContentList ( list );
return dList . Select ( predicate );
}
2013-09-05 17:47:13 +02:00
public static HtmlString Where ( this IPublishedContent content , string predicate , string valueIfTrue )
{
if ( content == null ) throw new ArgumentNullException ( "content" );
return content . Where ( predicate , valueIfTrue , string . Empty );
}
2012-10-04 03:26:56 +05:00
2013-09-05 17:47:13 +02:00
public static HtmlString Where ( this IPublishedContent content , string predicate , string valueIfTrue , string valueIfFalse )
{
if ( content == null ) throw new ArgumentNullException ( "content" );
return new HtmlString ( content . Where ( predicate ) ? valueIfTrue : valueIfFalse );
}
2012-10-04 03:26:56 +05:00
2013-09-05 17:47:13 +02:00
public static bool Where ( this IPublishedContent content , string predicate )
{
if ( content == null ) throw new ArgumentNullException ( "content" );
var dynamicDocumentList = new DynamicPublishedContentList { content . AsDynamicOrNull () };
var filtered = dynamicDocumentList . Where < DynamicPublishedContent >( predicate );
return filtered . Count () == 1 ;
}
#endregion
#region AsDynamic
// it is ok to have dynamic here
// content should NOT be null
public static dynamic AsDynamic ( this IPublishedContent content )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
if ( content == null ) throw new ArgumentNullException ( "content" );
2012-10-04 01:31:08 +05:00
return new DynamicPublishedContent ( content );
}
2013-09-05 17:47:13 +02:00
// content CAN be null
internal static DynamicPublishedContent AsDynamicOrNull ( this IPublishedContent content )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return content == null ? null : new DynamicPublishedContent ( content );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
#endregion
2012-10-04 03:26:56 +05:00
2013-09-05 17:47:13 +02:00
#region ContentSet
2012-10-04 01:31:08 +05:00
public static int Position ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . GetIndex ();
2012-12-09 03:51:02 +05:00
}
2013-03-02 01:02:38 +06:00
2013-09-05 17:47:13 +02:00
public static int Index ( this IPublishedContent content )
2013-03-02 01:02:38 +06:00
{
2013-09-05 17:47:13 +02:00
return content . GetIndex ();
}
2013-03-02 01:02:38 +06:00
2013-09-05 17:47:13 +02:00
private static int GetIndex ( this IPublishedContent content , IEnumerable < IPublishedContent > set )
{
var index = set . FindIndex ( n => n . Id == content . Id );
if ( index < 0 )
throw new IndexOutOfRangeException ( "Could not find content in the content set." );
return index ;
}
2012-10-04 01:31:08 +05:00
#endregion
2013-09-05 17:47:13 +02:00
#region IsSomething : misc .
/// <summary>
/// Gets a value indicating whether the content is visible.
/// </summary>
/// <param name="content">The content.</param>
/// <returns>A value indicating whether the content is visible.</returns>
/// <remarks>A content is not visible if it has an umbracoNaviHide property with a value of "1". Otherwise,
/// the content is visible.</remarks>
public static bool IsVisible ( this IPublishedContent content )
{
// note: would be better to ensure we have an IPropertyEditorValueConverter for booleans
// and then treat the umbracoNaviHide property as a boolean - vs. the hard-coded "1".
2013-09-17 10:18:26 +02:00
// rely on the property converter - will return default bool value, ie false, if property
// is not defined, or has no value, else will return its value.
return content . GetPropertyValue < bool >( Constants . Conventions . Content . NaviHide ) == false ;
2013-09-05 17:47:13 +02:00
}
2012-10-04 01:31:08 +05:00
2013-10-16 11:38:58 +02:00
/// <summary>
/// Determines whether the specified content is a specified content type.
/// </summary>
/// <param name="content">The content to determine content type of.</param>
/// <param name="docTypeAlias">The alias of the content type to test against.</param>
/// <returns>True if the content is of the specified content type; otherwise false.</returns>
public static bool IsDocumentType ( this IPublishedContent content , string docTypeAlias )
{
return content . DocumentTypeAlias . InvariantEquals ( docTypeAlias );
}
/// <summary>
/// Determines whether the specified content is a specified content type or it's derived types.
/// </summary>
/// <param name="content">The content to determine content type of.</param>
/// <param name="docTypeAlias">The alias of the content type to test against.</param>
/// <param name="recursive">When true, recurses up the content type tree to check inheritance; when false just calls IsDocumentType(this IPublishedContent content, string docTypeAlias).</param>
/// <returns>True if the content is of the specified content type or a derived content type; otherwise false.</returns>
public static bool IsDocumentType ( this IPublishedContent content , string docTypeAlias , bool recursive )
2012-10-06 23:41:42 +05:00
{
2013-10-16 11:38:58 +02:00
if ( content . IsDocumentType ( docTypeAlias ))
2013-09-27 13:12:16 +02:00
return true ;
if ( recursive )
return IsDocumentTypeRecursive ( content , docTypeAlias );
return false ;
}
private static bool IsDocumentTypeRecursive ( IPublishedContent content , string docTypeAlias )
{
var contentTypeService = UmbracoContext . Current . Application . Services . ContentTypeService ;
var type = contentTypeService . GetContentType ( content . DocumentTypeAlias );
while ( type . ParentId > 0 )
{
type = contentTypeService . GetContentType ( type . ParentId );
2013-10-16 11:38:58 +02:00
if ( type . Alias . InvariantEquals ( docTypeAlias ))
2013-09-27 13:12:16 +02:00
return true ;
}
return false ;
2012-10-06 23:41:42 +05:00
}
2013-09-05 17:47:13 +02:00
public static bool IsNull ( this IPublishedContent content , string alias , bool recurse )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return content . HasValue ( alias , recurse ) == false ;
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static bool IsNull ( this IPublishedContent content , string alias )
{
2013-09-05 17:47:13 +02:00
return content . HasValue ( alias ) == false ;
}
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
#endregion
#region IsSomething : position in set
2013-03-02 01:02:38 +06:00
public static bool IsFirst ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . GetIndex () == 0 ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsFirst ( this IPublishedContent content , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsFirst ( valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsFirst ( this IPublishedContent content , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsFirst () ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsNotFirst ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . IsFirst () == false ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotFirst ( this IPublishedContent content , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsNotFirst ( valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotFirst ( this IPublishedContent content , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsNotFirst () ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsPosition ( this IPublishedContent content , int index )
{
2013-09-05 17:47:13 +02:00
return content . GetIndex () == index ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsPosition ( this IPublishedContent content , int index , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsPosition ( index , valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsPosition ( this IPublishedContent content , int index , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsPosition ( index ) ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsModZero ( this IPublishedContent content , int modulus )
{
2013-09-05 17:47:13 +02:00
return content . GetIndex () % modulus == 0 ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsModZero ( this IPublishedContent content , int modulus , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsModZero ( modulus , valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsModZero ( this IPublishedContent content , int modulus , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsModZero ( modulus ) ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsNotModZero ( this IPublishedContent content , int modulus )
{
2013-09-05 17:47:13 +02:00
return content . IsModZero ( modulus ) == false ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotModZero ( this IPublishedContent content , int modulus , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsNotModZero ( modulus , valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotModZero ( this IPublishedContent content , int modulus , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsNotModZero ( modulus ) ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsNotPosition ( this IPublishedContent content , int index )
{
2013-09-05 17:47:13 +02:00
return content . IsPosition ( index ) == false ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotPosition ( this IPublishedContent content , int index , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsNotPosition ( index , valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotPosition ( this IPublishedContent content , int index , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsNotPosition ( index ) ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsLast ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . GetIndex () == content . ContentSet . Count () - 1 ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsLast ( this IPublishedContent content , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsLast ( valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsLast ( this IPublishedContent content , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsLast () ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsNotLast ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . IsLast () == false ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotLast ( this IPublishedContent content , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsNotLast ( valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsNotLast ( this IPublishedContent content , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsNotLast () ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsEven ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . GetIndex () % 2 == 0 ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsEven ( this IPublishedContent content , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsEven ( valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsEven ( this IPublishedContent content , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsEven () ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static bool IsOdd ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . GetIndex () % 2 == 1 ;
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsOdd ( this IPublishedContent content , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsOdd ( valueIfTrue , string . Empty );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
public static HtmlString IsOdd ( this IPublishedContent content , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsOdd () ? valueIfTrue : valueIfFalse );
2013-03-02 01:02:38 +06:00
}
2013-09-05 17:47:13 +02:00
2013-03-02 01:02:38 +06:00
#endregion
2013-09-05 17:47:13 +02:00
#region IsSomething : equality
2013-03-02 01:02:38 +06:00
public static bool IsEqual ( this IPublishedContent content , IPublishedContent other )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return content . Id == other . Id ;
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
public static HtmlString IsEqual ( this IPublishedContent content , IPublishedContent other , string valueIfTrue )
{
return content . IsEqual ( other , valueIfTrue , string . Empty );
}
2012-10-04 01:31:08 +05:00
public static HtmlString IsEqual ( this IPublishedContent content , IPublishedContent other , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsEqual ( other ) ? valueIfTrue : valueIfFalse );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static bool IsNotEqual ( this IPublishedContent content , IPublishedContent other )
{
2013-09-05 17:47:13 +02:00
return content . IsEqual ( other ) == false ;
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
public static HtmlString IsNotEqual ( this IPublishedContent content , IPublishedContent other , string valueIfTrue )
{
return content . IsNotEqual ( other , valueIfTrue , string . Empty );
}
public static HtmlString IsNotEqual ( this IPublishedContent content , IPublishedContent other , string valueIfTrue , string valueIfFalse )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsNotEqual ( other ) ? valueIfTrue : valueIfFalse );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
#endregion
#region IsSomething : ancestors and descendants
public static bool IsDescendant ( this IPublishedContent content , IPublishedContent other )
{
return content . Ancestors (). Any ( x => x . Id == other . Id );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static HtmlString IsDescendant ( this IPublishedContent content , IPublishedContent other , string valueIfTrue )
{
2013-09-05 17:47:13 +02:00
return content . IsDescendant ( other , valueIfTrue , string . Empty );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static HtmlString IsDescendant ( this IPublishedContent content , IPublishedContent other , string valueIfTrue , string valueIfFalse )
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsDescendant ( other ) ? valueIfTrue : valueIfFalse );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static bool IsDescendantOrSelf ( this IPublishedContent content , IPublishedContent other )
{
2013-09-05 17:47:13 +02:00
return content . AncestorsOrSelf (). Any ( x => x . Id == other . Id );
}
2013-01-13 22:36:21 +03:00
public static HtmlString IsDescendantOrSelf ( this IPublishedContent content , IPublishedContent other , string valueIfTrue )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return content . IsDescendantOrSelf ( other , valueIfTrue , string . Empty );
}
2013-01-13 22:36:21 +03:00
public static HtmlString IsDescendantOrSelf ( this IPublishedContent content , IPublishedContent other , string valueIfTrue , string valueIfFalse )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsDescendantOrSelf ( other ) ? valueIfTrue : valueIfFalse );
}
2013-01-13 22:36:21 +03:00
public static bool IsAncestor ( this IPublishedContent content , IPublishedContent other )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
// avoid using Descendants(), that's expensive
return other . Ancestors (). Any ( x => x . Id == content . Id );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2013-01-13 22:36:21 +03:00
public static HtmlString IsAncestor ( this IPublishedContent content , IPublishedContent other , string valueIfTrue )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return content . IsAncestor ( other , valueIfTrue , string . Empty );
}
2013-01-13 22:36:21 +03:00
public static HtmlString IsAncestor ( this IPublishedContent content , IPublishedContent other , string valueIfTrue , string valueIfFalse )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsAncestor ( other ) ? valueIfTrue : valueIfFalse );
}
2013-01-13 22:36:21 +03:00
public static bool IsAncestorOrSelf ( this IPublishedContent content , IPublishedContent other )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
// avoid using DescendantsOrSelf(), that's expensive
return other . AncestorsOrSelf (). Any ( x => x . Id == content . Id );
}
2013-01-13 22:36:21 +03:00
public static HtmlString IsAncestorOrSelf ( this IPublishedContent content , IPublishedContent other , string valueIfTrue )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return content . IsAncestorOrSelf ( other , valueIfTrue , string . Empty );
}
2013-01-13 22:36:21 +03:00
public static HtmlString IsAncestorOrSelf ( this IPublishedContent content , IPublishedContent other , string valueIfTrue , string valueIfFalse )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return new HtmlString ( content . IsAncestorOrSelf ( other ) ? valueIfTrue : valueIfFalse );
}
2013-03-02 01:50:53 +06:00
2013-09-05 17:47:13 +02:00
#endregion
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
#region Axes : ancestors , ancestors - or - self
// as per XPath 1.0 specs §2.2,
// - the ancestor axis contains the ancestors of the context node; the ancestors of the context node consist
// of the parent of context node and the parent's parent and so on; thus, the ancestor axis will always
// include the root node, unless the context node is the root node.
// - the ancestor-or-self axis contains the context node and the ancestors of the context node; thus,
// the ancestor axis will always include the root node.
//
// as per XPath 2.0 specs §3.2.1.1,
// - the ancestor axis is defined as the transitive closure of the parent axis; it contains the ancestors
// of the context node (the parent, the parent of the parent, and so on) - The ancestor axis includes the
// root node of the tree in which the context node is found, unless the context node is the root node.
// - the ancestor-or-self axis contains the context node and the ancestors of the context node; thus,
// the ancestor-or-self axis will always include the root node.
//
// the ancestor and ancestor-or-self axis are reverse axes ie they contain the context node or nodes that
// are before the context node in document order.
//
// document order is defined by §2.4.1 as:
// - the root node is the first node.
// - every node occurs before all of its children and descendants.
// - the relative order of siblings is the order in which they occur in the children property of their parent node.
// - children and descendants occur before following siblings.
// SO, here we want to walk up the tree. which is what AncestorOrSelf does but NOT what AncestorsOrSelf does since
// it reverses the list, so basically ancestors are NOT XPath-compliant in Umbraco at the moment -- but fixing that
// would be a breaking change. Defining FIX_AXES would fix the situation.
2012-10-04 01:31:08 +05:00
2013-04-16 12:29:58 -02:00
public static IEnumerable < IPublishedContent > Ancestors ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . AncestorsOrSelf ( false , null );
2013-04-16 12:29:58 -02:00
}
2012-10-04 01:31:08 +05:00
2013-04-16 12:29:58 -02:00
public static IEnumerable < IPublishedContent > Ancestors ( this IPublishedContent content , int level )
{
return content . AncestorsOrSelf ( false , n => n . Level <= level );
}
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > Ancestors ( this IPublishedContent content , string contentTypeAlias )
2013-04-16 12:29:58 -02:00
{
2013-09-05 17:47:13 +02:00
return content . AncestorsOrSelf ( false , n => n . DocumentTypeAlias == contentTypeAlias );
2013-04-16 12:29:58 -02:00
}
2013-09-13 21:58:37 +02:00
public static IEnumerable < T > Ancestors < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . Ancestors (). OfType < T >();
}
public static IEnumerable < T > Ancestors < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . Ancestors ( level ). OfType < T >();
}
2013-04-16 12:29:58 -02:00
public static IEnumerable < IPublishedContent > AncestorsOrSelf ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . AncestorsOrSelf ( true , null );
2013-04-16 12:29:58 -02:00
}
public static IEnumerable < IPublishedContent > AncestorsOrSelf ( this IPublishedContent content , int level )
{
return content . AncestorsOrSelf ( true , n => n . Level <= level );
}
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > AncestorsOrSelf ( this IPublishedContent content , string contentTypeAlias )
2013-04-16 12:29:58 -02:00
{
2013-09-05 17:47:13 +02:00
return content . AncestorsOrSelf ( true , n => n . DocumentTypeAlias == contentTypeAlias );
2013-04-16 12:29:58 -02:00
}
2013-09-13 21:58:37 +02:00
public static IEnumerable < T > AncestorsOrSelf < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . AncestorsOrSelf (). OfType < T >();
}
public static IEnumerable < T > AncestorsOrSelf < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . AncestorsOrSelf ( level ). OfType < T >();
}
2013-09-05 17:47:13 +02:00
public static IPublishedContent Ancestor ( this IPublishedContent content )
2013-04-16 12:29:58 -02:00
{
2013-09-05 17:47:13 +02:00
return content . Parent ;
}
public static IPublishedContent Ancestor ( this IPublishedContent content , int level )
{
return content . EnumerateAncestors ( false ). FirstOrDefault ( x => x . Level <= level );
}
public static IPublishedContent Ancestor ( this IPublishedContent content , string contentTypeAlias )
{
return content . EnumerateAncestors ( false ). FirstOrDefault ( x => x . DocumentTypeAlias == contentTypeAlias );
}
2013-09-13 21:58:37 +02:00
public static T Ancestor < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . Ancestor () as T ;
}
public static T Ancestor < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . Ancestor ( level ) as T ;
}
2013-09-05 17:47:13 +02:00
// note: that one makes no sense and should return self -- but fixing that
// would be a breaking change. Defining FIX_AXES would fix the situation.
public static IPublishedContent AncestorOrSelf ( this IPublishedContent content )
{
#if FIX_AXES
return content ;
#else
return content . EnumerateAncestors ( true ). FirstOrDefault ( x => x . Level == 1 );
#endif
}
public static IPublishedContent AncestorOrSelf ( this IPublishedContent content , int level )
{
return content . EnumerateAncestors ( true ). FirstOrDefault ( x => x . Level <= level );
}
public static IPublishedContent AncestorOrSelf ( this IPublishedContent content , string contentTypeAlias )
{
return content . EnumerateAncestors ( true ). FirstOrDefault ( x => x . DocumentTypeAlias == contentTypeAlias );
}
2013-09-13 21:58:37 +02:00
public static T AncestorOrSelf < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . AncestorOrSelf () as T ;
}
public static T AncestorOrSelf < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . AncestorOrSelf ( level ) as T ;
}
2013-09-05 17:47:13 +02:00
// broken until we defined FIX_AXES
internal static IEnumerable < IPublishedContent > AncestorsOrSelf ( this IPublishedContent content , bool orSelf , Func < IPublishedContent , bool > func )
{
#if FIX_AXES
return content . EnumerateAncestors ( orSelf ). Where ( x => func == null || func ( x ));
#else
2013-04-16 12:29:58 -02:00
var ancestors = new List < IPublishedContent >();
2013-09-05 17:47:13 +02:00
if ( orSelf && ( func == null || func ( content )))
2013-04-16 12:29:58 -02:00
ancestors . Add ( content );
while ( content . Level > 1 ) // while we have a parent, consider the parent
{
content = content . Parent ;
2013-09-05 17:47:13 +02:00
if (( func == null || func ( content )))
2013-04-16 12:29:58 -02:00
ancestors . Add ( content );
}
ancestors . Reverse ();
return ancestors ;
2013-09-05 17:47:13 +02:00
#endif
}
internal static IEnumerable < IPublishedContent > EnumerateAncestors ( this IPublishedContent content , bool orSelf )
{
if ( orSelf ) yield return content ;
while (( content = content . Parent ) != null )
yield return content ;
2013-04-16 12:29:58 -02:00
}
#endregion
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
#region Axes : descendants , descendants - or - self
2013-03-02 01:02:38 +06:00
2013-10-28 12:35:50 +11:00
/// <summary>
/// Returns all DescendantsOrSelf of all content referenced
/// </summary>
/// <param name="parentNodes"></param>
/// <param name="docTypeAlias"></param>
/// <returns></returns>
/// <remarks>
/// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot
/// </remarks>
public static IEnumerable < IPublishedContent > DescendantsOrSelf ( this IEnumerable < IPublishedContent > parentNodes , string docTypeAlias )
{
return parentNodes . SelectMany ( x => x . DescendantsOrSelf ( docTypeAlias ));
}
/// <summary>
/// Returns all DescendantsOrSelf of all content referenced
/// </summary>
/// <param name="parentNodes"></param>
/// <returns></returns>
/// <remarks>
/// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot
/// </remarks>
public static IEnumerable < T > DescendantsOrSelf < T >( this IEnumerable < IPublishedContent > parentNodes )
where T : class , IPublishedContent
{
return parentNodes . SelectMany ( x => x . DescendantsOrSelf < T >());
}
2013-09-05 17:47:13 +02:00
// as per XPath 1.0 specs §2.2,
// - the descendant axis contains the descendants of the context node; a descendant is a child or a child of a child and so on; thus
// the descendant axis never contains attribute or namespace nodes.
// - the descendant-or-self axis contains the context node and the descendants of the context node.
//
// as per XPath 2.0 specs §3.2.1.1,
// - the descendant axis is defined as the transitive closure of the child axis; it contains the descendants of the context node (the
// children, the children of the children, and so on).
// - the descendant-or-self axis contains the context node and the descendants of the context node.
//
// the descendant and descendant-or-self axis are forward axes ie they contain the context node or nodes that are after the context
// node in document order.
//
// document order is defined by §2.4.1 as:
// - the root node is the first node.
// - every node occurs before all of its children and descendants.
// - the relative order of siblings is the order in which they occur in the children property of their parent node.
// - children and descendants occur before following siblings.
// SO, here we want to implement a depth-first enumeration of children. Which is what EnumerateDescendants does, but NOT what
// DescendantsOrSelf does, so basically descendants are NOT XPath-compliant in Umbraco at the moment -- but fixing that
// would be a breaking change. Defining FIX_AXES would fix the situation.
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > Descendants ( this IPublishedContent content )
{
return content . DescendantsOrSelf ( false , null );
}
public static IEnumerable < IPublishedContent > Descendants ( this IPublishedContent content , int level )
{
return content . DescendantsOrSelf ( false , p => p . Level >= level );
}
public static IEnumerable < IPublishedContent > Descendants ( this IPublishedContent content , string contentTypeAlias )
{
return content . DescendantsOrSelf ( false , p => p . DocumentTypeAlias == contentTypeAlias );
}
2013-09-13 21:58:37 +02:00
public static IEnumerable < T > Descendants < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . Descendants (). OfType < T >();
}
public static IEnumerable < T > Descendants < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . Descendants ( level ). OfType < T >();
}
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > DescendantsOrSelf ( this IPublishedContent content )
{
return content . DescendantsOrSelf ( true , null );
}
public static IEnumerable < IPublishedContent > DescendantsOrSelf ( this IPublishedContent content , int level )
{
return content . DescendantsOrSelf ( true , p => p . Level >= level );
}
public static IEnumerable < IPublishedContent > DescendantsOrSelf ( this IPublishedContent content , string contentTypeAlias )
{
return content . DescendantsOrSelf ( true , p => p . DocumentTypeAlias == contentTypeAlias );
}
2013-09-13 21:58:37 +02:00
public static IEnumerable < T > DescendantsOrSelf < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . DescendantsOrSelf (). OfType < T >();
}
public static IEnumerable < T > DescendantsOrSelf < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . DescendantsOrSelf ( level ). OfType < T >();
}
2013-09-05 17:47:13 +02:00
public static IPublishedContent Descendant ( this IPublishedContent content )
{
return content . Children . FirstOrDefault ();
}
public static IPublishedContent Descendant ( this IPublishedContent content , int level )
{
return content . EnumerateDescendants ( false ). FirstOrDefault ( x => x . Level == level );
}
public static IPublishedContent Descendant ( this IPublishedContent content , string contentTypeAlias )
{
return content . EnumerateDescendants ( false ). FirstOrDefault ( x => x . DocumentTypeAlias == contentTypeAlias );
}
2013-09-13 21:58:37 +02:00
public static T Descendant < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . EnumerateDescendants ( false ). FirstOrDefault ( x => x is T ) as T ;
}
public static T Descendant < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . Descendant ( level ) as T ;
}
2013-09-05 17:47:13 +02:00
public static IPublishedContent DescendantOrSelf ( this IPublishedContent content )
{
return content ;
}
public static IPublishedContent DescendantOrSelf ( this IPublishedContent content , int level )
{
return content . EnumerateDescendants ( true ). FirstOrDefault ( x => x . Level == level );
}
public static IPublishedContent DescendantOrSelf ( this IPublishedContent content , string contentTypeAlias )
{
return content . EnumerateDescendants ( true ). FirstOrDefault ( x => x . DocumentTypeAlias == contentTypeAlias );
}
2013-09-13 21:58:37 +02:00
public static T DescendantOrSelf < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . EnumerateDescendants ( true ). FirstOrDefault ( x => x is T ) as T ;
}
public static T DescendantOrSelf < T >( this IPublishedContent content , int level )
where T : class , IPublishedContent
{
return content . DescendantOrSelf ( level ) as T ;
}
2013-09-05 17:47:13 +02:00
// broken until we defined FIX_AXES
internal static IEnumerable < IPublishedContent > DescendantsOrSelf ( this IPublishedContent content , bool orSelf , Func < IPublishedContent , bool > func )
{
#if FIX_AXES
return content . EnumerateDescendants ( orSelf ). Where ( x => func == null || func ( x ));
#else
var init = ( orSelf && ( func == null || func ( content ))) ? new [] { content } : new IPublishedContent [] { };
var descendants = init
. Union ( content . Children
. FlattenList ( x => x . Children )
. Where ( x => func == null || func ( x ))
)
. OrderBy ( x => x . Level )
. ThenBy ( x => x . SortOrder );
return descendants ;
#endif
}
internal static IEnumerable < IPublishedContent > EnumerateDescendants ( this IPublishedContent content , bool orSelf )
{
if ( orSelf ) yield return content ;
foreach ( var child in content . Children )
foreach ( var child2 in child . EnumerateDescendants ())
yield return child2 ;
}
internal static IEnumerable < IPublishedContent > EnumerateDescendants ( this IPublishedContent content )
{
yield return content ;
foreach ( var child in content . Children )
foreach ( var child2 in child . EnumerateDescendants ())
yield return child2 ;
}
#endregion
#region Axes : following - sibling , preceding - sibling , following , preceding + pseudo - axes up , down , next , previous
// up pseudo-axe ~ ancestors
2013-09-13 23:32:15 +02:00
// bogus, kept for backward compatibility but we should get rid of it
// better use ancestors
2012-10-04 01:31:08 +05:00
public static IPublishedContent Up ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . Parent ;
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static IPublishedContent Up ( this IPublishedContent content , int number )
{
2013-09-05 17:47:13 +02:00
if ( number < 0 )
throw new ArgumentOutOfRangeException ( "number" , "Must be greater than, or equal to, zero." );
#if (! FIX_AXES )
number += 1 ; // legacy is zero-based ie zero == parent
#endif
return number == 0 ? content : content . EnumerateAncestors ( false ). Skip ( number ). FirstOrDefault ();
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
public static IPublishedContent Up ( this IPublishedContent content , string contentTypeAlias )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
return string . IsNullOrEmpty ( contentTypeAlias )
? content . Parent
: content . Ancestor ( contentTypeAlias );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
// down pseudo-axe ~ children (not descendants)
2013-09-13 23:32:15 +02:00
// bogus, kept for backward compatibility but we should get rid of it
// better use descendants
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static IPublishedContent Down ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
return content . Children . FirstOrDefault ();
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static IPublishedContent Down ( this IPublishedContent content , int number )
{
2013-09-05 17:47:13 +02:00
if ( number < 0 )
throw new ArgumentOutOfRangeException ( "number" , "Must be greater than, or equal to, zero." );
#if (! FIX_AXES )
number += 1 ; // legacy is zero-based ie zero == first child
#endif
if ( number == 0 ) return content ;
content = content . Children . FirstOrDefault ();
while ( content != null && -- number > 0 )
content = content . Children . FirstOrDefault ();
return content ;
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
public static IPublishedContent Down ( this IPublishedContent content , string contentTypeAlias )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
if ( string . IsNullOrEmpty ( contentTypeAlias ))
return content . Children . FirstOrDefault ();
// note: this is what legacy did, but with a broken Descendant
// so fixing Descendant will change how it works...
return content . Descendant ( contentTypeAlias );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
// next pseudo-axe ~ following within the content set
2013-09-13 23:32:15 +02:00
// bogus, kept for backward compatibility but we should get rid of it
2013-09-05 17:47:13 +02:00
2012-10-04 01:31:08 +05:00
public static IPublishedContent Next ( this IPublishedContent content )
{
2013-09-13 23:32:15 +02:00
return content . ContentSet . ElementAtOrDefault ( content . GetIndex () + 1 );
2013-09-05 17:47:13 +02:00
}
2012-10-04 01:31:08 +05:00
2013-12-12 15:32:52 +01:00
public static IPublishedContent Next ( this IPublishedContent current , Func < IPublishedContent , bool > func ) {
IPublishedContent next = current . Next ();
while ( next != null ) {
if ( func ( next )) return next ;
next = next . Next ();
}
return null ;
}
2013-09-05 17:47:13 +02:00
public static IPublishedContent Next ( this IPublishedContent content , int number )
2012-10-04 01:31:08 +05:00
{
2013-09-05 17:47:13 +02:00
if ( number < 0 )
throw new ArgumentOutOfRangeException ( "number" , "Must be greater than, or equal to, zero." );
#if (! FIX_AXES )
number += 1 ; // legacy is zero-based ie zero == next, whereas zero should be current
#endif
2013-09-13 23:32:15 +02:00
return number == 0 ? content : content . ContentSet . ElementAtOrDefault ( content . GetIndex () + number );
2013-09-05 17:47:13 +02:00
}
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
public static IPublishedContent Next ( this IPublishedContent content , string contentTypeAlias )
{
2013-09-13 23:32:15 +02:00
return content . Next ( contentTypeAlias , false );
2013-09-05 17:47:13 +02:00
}
public static IPublishedContent Next ( this IPublishedContent content , string contentTypeAlias , bool wrap )
{
2013-09-13 23:32:15 +02:00
return content . Next ( content . ContentSet , x => x . DocumentTypeAlias . InvariantEquals ( contentTypeAlias ), wrap );
}
public static T Next < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . Next < T >( false );
}
public static T Next < T >( this IPublishedContent content , bool wrap )
where T : class , IPublishedContent
{
return content . Next ( content . ContentSet , x => x is T , wrap ) as T ;
}
static IPublishedContent Next ( this IPublishedContent content , IEnumerable < IPublishedContent > axis , Func < IPublishedContent , bool > predicate , bool wrap )
{
var b4 = true ;
IPublishedContent wrapped = null ;
foreach ( var c in axis )
{
if ( b4 )
{
if ( c == content )
b4 = false ;
else if ( wrap && wrapped == null && predicate ( c ))
wrapped = c ;
continue ;
}
if ( predicate ( c ))
return c ;
}
return wrapped ;
2013-09-05 17:47:13 +02:00
}
// previous pseudo-axe ~ preceding within the content set
2013-09-13 23:32:15 +02:00
// bogus, kept for backward compatibility but we should get rid of it
2013-09-05 17:47:13 +02:00
public static IPublishedContent Previous ( this IPublishedContent content )
2012-10-04 01:31:08 +05:00
{
2013-09-13 23:32:15 +02:00
return content . ContentSet . ElementAtOrDefault ( content . GetIndex () - 1 );
2013-09-05 17:47:13 +02:00
}
2013-12-12 15:32:52 +01:00
public static IPublishedContent Previous ( this IPublishedContent current , Func < IPublishedContent , bool > func ) {
IPublishedContent prev = current . Previous ();
while ( prev != null ) {
if ( func ( prev )) return prev ;
prev = prev . Previous ();
}
return null ;
}
2012-10-04 01:31:08 +05:00
public static IPublishedContent Previous ( this IPublishedContent content , int number )
{
2013-09-05 17:47:13 +02:00
if ( number < 0 )
throw new ArgumentOutOfRangeException ( "number" , "Must be greater than, or equal to, zero." );
#if (! FIX_AXES )
number = - number ; // legacy wants negative numbers, should be positive
number += 1 ; // legacy is zero-based ie zero == previous, whereas zero should be current
#endif
2013-09-13 23:32:15 +02:00
return number == 0 ? content : content . ContentSet . ElementAtOrDefault ( content . GetIndex () - number );
2013-09-05 17:47:13 +02:00
}
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
public static IPublishedContent Previous ( this IPublishedContent content , string contentTypeAlias )
2012-10-04 01:31:08 +05:00
{
2013-09-13 23:32:15 +02:00
return content . Previous ( contentTypeAlias , false );
}
2012-10-04 01:31:08 +05:00
2013-09-05 17:47:13 +02:00
public static IPublishedContent Previous ( this IPublishedContent content , string contentTypeAlias , bool wrap )
{
2013-09-13 23:32:15 +02:00
return content . Next ( content . ContentSet . Reverse (), x => x . DocumentTypeAlias . InvariantEquals ( contentTypeAlias ), wrap );
2013-09-05 17:47:13 +02:00
}
2013-09-13 23:32:15 +02:00
public static T Previous < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . Previous < T >( false );
}
public static T Previous < T >( this IPublishedContent content , bool wrap )
where T : class , IPublishedContent
{
return content . Next ( content . ContentSet . Reverse (), x => x is T , wrap ) as T ;
}
//
2013-09-05 17:47:13 +02:00
[Obsolete("Obsolete, use FollowingSibling or PrecedingSibling instead.")]
2012-10-04 01:31:08 +05:00
public static IPublishedContent Sibling ( this IPublishedContent content , int number )
2013-09-05 17:47:13 +02:00
{
if ( number < 0 )
throw new ArgumentOutOfRangeException ( "number" , "Must be greater than, or equal to, zero." );
number += 1 ; // legacy is zero-based
2013-09-13 23:32:15 +02:00
return content . FollowingSibling ( number );
2012-10-04 01:31:08 +05:00
}
2013-09-05 17:47:13 +02:00
// contentTypeAlias is case-insensitive
[Obsolete("Obsolete, use FollowingSibling or PrecedingSibling instead.")]
public static IPublishedContent Sibling ( this IPublishedContent content , string contentTypeAlias )
{
// note: the original implementation seems to loop on all siblings
// ie if it reaches the end of the set, it starts again at the beginning.
2013-09-13 23:32:15 +02:00
// so here we wrap, although it's not consistent... but anyway those
// methods should be obsoleted.
2013-09-05 17:47:13 +02:00
2013-09-13 23:32:15 +02:00
return content . FollowingSibling ( contentTypeAlias , true );
2013-09-05 17:47:13 +02:00
}
// following-sibling, preceding-sibling axes
public static IPublishedContent FollowingSibling ( this IPublishedContent content )
{
2013-09-13 23:32:15 +02:00
return content . Siblings (). ElementAtOrDefault ( content . GetIndex ( content . Siblings ()) + 1 );
2013-09-05 17:47:13 +02:00
}
public static IPublishedContent FollowingSibling ( this IPublishedContent content , int number )
{
if ( number < 0 )
throw new ArgumentOutOfRangeException ( "number" , "Must be greater than, or equal to, zero." );
2013-09-13 23:32:15 +02:00
return number == 0 ? content : content . Siblings (). ElementAtOrDefault ( content . GetIndex ( content . Siblings ()) + number );
2013-09-05 17:47:13 +02:00
}
// contentTypeAlias is case-insensitive
public static IPublishedContent FollowingSibling ( this IPublishedContent content , string contentTypeAlias )
{
2013-09-13 23:32:15 +02:00
return content . FollowingSibling ( contentTypeAlias , false );
2013-09-05 17:47:13 +02:00
}
// contentTypeAlias is case-insensitive
// note: not sure that one makes a lot of sense but it is here for backward compatibility
public static IPublishedContent FollowingSibling ( this IPublishedContent content , string contentTypeAlias , bool wrap )
{
2013-09-13 23:32:15 +02:00
return content . Next ( content . Siblings (), x => x . DocumentTypeAlias . InvariantEquals ( contentTypeAlias ), wrap );
}
public static T FollowingSibling < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . FollowingSibling < T >( false );
}
public static T FollowingSibling < T >( this IPublishedContent content , bool wrap )
where T : class , IPublishedContent
{
return content . Next ( content . Siblings (), x => x is T , wrap ) as T ;
2013-09-05 17:47:13 +02:00
}
public static IPublishedContent PrecedingSibling ( this IPublishedContent content )
{
2013-09-13 23:32:15 +02:00
return content . Siblings (). ElementAtOrDefault ( content . GetIndex ( content . Siblings ()) - 1 );
2013-09-05 17:47:13 +02:00
}
public static IPublishedContent PrecedingSibling ( this IPublishedContent content , int number )
{
if ( number < 0 )
throw new ArgumentOutOfRangeException ( "number" , "Must be greater than, or equal to, zero." );
2013-09-13 23:32:15 +02:00
return number == 0 ? content : content . Siblings (). ElementAtOrDefault ( content . GetIndex ( content . Siblings ()) - number );
2013-09-05 17:47:13 +02:00
}
// contentTypeAlias is case-insensitive
public static IPublishedContent PrecedingSibling ( this IPublishedContent content , string contentTypeAlias )
{
2013-09-13 23:32:15 +02:00
return content . PrecedingSibling ( contentTypeAlias , false );
2013-09-05 17:47:13 +02:00
}
// contentTypeAlias is case-insensitive
// note: not sure that one makes a lot of sense but it is here for backward compatibility
public static IPublishedContent PrecedingSibling ( this IPublishedContent content , string contentTypeAlias , bool wrap )
{
2013-09-13 23:32:15 +02:00
return content . Next ( content . Siblings (). Reverse (), x => x . DocumentTypeAlias . InvariantEquals ( contentTypeAlias ), wrap );
}
public static T PrecedingSibling < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . PrecedingSibling < T >( false );
}
public static T PrecedingSibling < T >( this IPublishedContent content , bool wrap )
where T : class , IPublishedContent
{
return content . Next ( content . Siblings (). Reverse (), x => x is T , wrap ) as T ;
2013-09-05 17:47:13 +02:00
}
// following, preceding axes - NOT IMPLEMENTED
// utilities
2013-03-02 01:02:38 +06:00
public static IEnumerable < IPublishedContent > Siblings ( this IPublishedContent content )
{
2013-09-05 17:47:13 +02:00
// content.Parent, content.Children and cache.GetAtRoot() should be fast enough,
// or cached by the content cache, so that we don't have to implement cache here.
// returns the true tree siblings, even if the content is in a set
// get the root docs if parent is null
// note: I don't like having to refer to the "current" content cache here, but
// what else? would need root content to have a special, non-null but hidden,
// parent...
var siblings = content . Parent == null
2013-03-19 17:51:55 -01:00
? UmbracoContext . Current . ContentCache . GetAtRoot ()
2013-03-02 01:02:38 +06:00
: content . Parent . Children ;
2013-09-05 17:47:13 +02:00
// make sure we run it once
return siblings . ToArray ();
}
2013-03-02 01:02:38 +06:00
2013-09-13 21:58:37 +02:00
public static IEnumerable < T > Siblings < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . Siblings (). OfType < T >();
}
2013-03-02 01:02:38 +06:00
#endregion
2012-09-20 12:42:43 +07:00
2013-09-05 17:47:13 +02:00
#region Axes : parent
// Parent is native
2013-09-13 21:58:37 +02:00
/// <summary>
/// Gets the parent of the content, of a given content type.
/// </summary>
/// <typeparam name="T">The content type.</typeparam>
/// <param name="content">The content.</param>
/// <returns>The parent of content, of the given content type, else null.</returns>
public static T Parent < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content . Parent as T ;
}
2013-09-05 17:47:13 +02:00
#endregion
2013-09-13 21:58:37 +02:00
#region Axes : children
2013-09-05 17:47:13 +02:00
/// <summary>
/// Gets the children of the content.
2012-10-08 21:55:03 +05:00
/// </summary>
2013-09-05 17:47:13 +02:00
/// <param name="content">The content.</param>
/// <returns>The children of the content.</returns>
2012-10-08 21:55:03 +05:00
/// <remarks>
2013-09-05 17:47:13 +02:00
/// <para>Children are sorted by their sortOrder.</para>
/// <para>This method exists for consistency, it is the same as calling content.Children as a property.</para>
2012-10-08 21:55:03 +05:00
/// </remarks>
2013-09-05 17:47:13 +02:00
public static IEnumerable < IPublishedContent > Children ( this IPublishedContent content )
2012-10-08 21:55:03 +05:00
{
2013-09-05 17:47:13 +02:00
return content . Children ;
}
/// <summary>
/// Gets the children of the content, filtered by a predicate.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="predicate">The predicate.</param>
/// <returns>The children of the content, filtered by the predicate.</returns>
/// <remarks>
/// <para>Children are sorted by their sortOrder.</para>
/// </remarks>
public static IEnumerable < IPublishedContent > Children ( this IPublishedContent content , Func < IPublishedContent , bool > predicate )
{
return content . Children (). Where ( predicate );
}
/// <summary>
/// Gets the children of the content, of a given content type.
/// </summary>
/// <typeparam name="T">The content type.</typeparam>
/// <param name="content">The content.</param>
/// <returns>The children of content, of the given content type.</returns>
/// <remarks>
/// <para>Children are sorted by their sortOrder.</para>
/// </remarks>
2013-09-13 21:58:37 +02:00
public static IEnumerable < T > Children < T >( this IPublishedContent content )
where T : class , IPublishedContent
2013-09-05 17:47:13 +02:00
{
return content . Children (). OfType < T >();
}
/// <summary>
/// Gets the children of the content in a DataTable.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="contentTypeAliasFilter">An optional content type alias.</param>
/// <returns>The children of the content.</returns>
public static DataTable ChildrenAsTable ( this IPublishedContent content , string contentTypeAliasFilter = "" )
{
return GenerateDataTable ( content , contentTypeAliasFilter );
2012-12-09 03:51:02 +05:00
}
2012-10-08 21:55:03 +05:00
2012-09-20 12:42:43 +07:00
/// <summary>
2013-09-05 17:47:13 +02:00
/// Gets the children of the content in a DataTable.
/// </summary>
/// <param name="content">The content.</param>
/// <param name="contentTypeAliasFilter">An optional content type alias.</param>
/// <returns>The children of the content.</returns>
private static DataTable GenerateDataTable ( IPublishedContent content , string contentTypeAliasFilter = "" )
2012-09-20 12:42:43 +07:00
{
2013-09-05 17:47:13 +02:00
var firstNode = contentTypeAliasFilter . IsNullOrWhiteSpace ()
? content . Children . Any ()
? content . Children . ElementAt ( 0 )
2012-12-09 03:51:02 +05:00
: null
2013-09-05 17:47:13 +02:00
: content . Children . FirstOrDefault ( x => x . DocumentTypeAlias == contentTypeAliasFilter );
2012-09-20 12:42:43 +07:00
if ( firstNode == null )
return new DataTable (); //no children found
//use new utility class to create table so that we don't have to maintain code in many places, just one
2013-09-05 17:47:13 +02:00
var dt = Core . DataTableExtensions . GenerateDataTable (
2012-09-20 12:42:43 +07:00
//pass in the alias of the first child node since this is the node type we're rendering headers for
firstNode . DocumentTypeAlias ,
//pass in the callback to extract the Dictionary<string, string> of all defined aliases to their names
alias => GetPropertyAliasesAndNames ( alias ),
//pass in a callback to populate the datatable, yup its a bit ugly but it's already legacy and we just want to maintain code in one place.
() =>
2012-12-09 03:51:02 +05:00
{
//create all row data
2013-09-05 17:47:13 +02:00
var tableData = Core . DataTableExtensions . CreateTableData ();
2012-12-09 03:51:02 +05:00
//loop through each child and create row data for it
2013-09-05 17:47:13 +02:00
foreach ( var n in content . Children . OrderBy ( x => x . SortOrder ))
2012-09-20 12:42:43 +07:00
{
2013-09-05 17:47:13 +02:00
if ( contentTypeAliasFilter . IsNullOrWhiteSpace () == false )
2012-09-20 12:42:43 +07:00
{
2013-09-05 17:47:13 +02:00
if ( n . DocumentTypeAlias != contentTypeAliasFilter )
2012-12-09 03:51:02 +05:00
continue ; //skip this one, it doesn't match the filter
}
2012-09-20 12:42:43 +07:00
2013-09-05 17:47:13 +02:00
var standardVals = new Dictionary < string , object >
{
{ "Id" , n . Id },
{ "NodeName" , n . Name },
{ "NodeTypeAlias" , n . DocumentTypeAlias },
{ "CreateDate" , n . CreateDate },
{ "UpdateDate" , n . UpdateDate },
{ "CreatorName" , n . CreatorName },
{ "WriterName" , n . WriterName },
{ "Url" , n . Url }
2012-09-20 12:42:43 +07:00
};
2013-09-05 17:47:13 +02:00
2012-12-09 03:51:02 +05:00
var userVals = new Dictionary < string , object >();
2013-09-19 13:09:27 +02:00
foreach ( var p in from IPublishedProperty p in n . Properties where p . DataValue != null select p )
2013-09-23 13:08:45 +02:00
{
// probably want the "object value" of the property here...
2013-09-25 13:16:37 +02:00
userVals [ p . PropertyTypeAlias ] = p . Value ;
2012-09-20 12:42:43 +07:00
}
2012-12-09 03:51:02 +05:00
//add the row data
2013-09-05 17:47:13 +02:00
Core . DataTableExtensions . AddRowData ( tableData , standardVals , userVals );
2012-09-20 12:42:43 +07:00
}
2012-12-09 03:51:02 +05:00
return tableData ;
}
2012-09-20 12:42:43 +07:00
);
return dt ;
}
2013-09-05 17:47:13 +02:00
#endregion
2013-09-13 21:58:37 +02:00
#region OfTypes
// the .OfType<T>() filter is nice when there's only one type
// this is to support filtering with multiple types
public static IEnumerable < IPublishedContent > OfTypes ( this IEnumerable < IPublishedContent > contents , params Type [] types )
{
return contents . Where ( x => types . Contains ( x . GetType ()));
}
public static IEnumerable < IPublishedContent > OfTypes ( this IEnumerable < IPublishedContent > contents , params string [] types )
{
types = types . Select ( x => x . ToLowerInvariant ()). ToArray ();
return contents . Where ( x => types . Contains ( x . DocumentTypeAlias . ToLowerInvariant ()));
}
2013-09-27 15:21:23 +02:00
public static T OfType < T >( this IPublishedContent content )
where T : class , IPublishedContent
{
return content as T ;
}
2013-09-13 21:58:37 +02:00
#endregion
2013-09-05 17:47:13 +02:00
#region PropertyAliasesAndNames
private static Func < string , Dictionary < string , string >> _getPropertyAliasesAndNames ;
2012-12-09 03:51:02 +05:00
2012-09-20 12:42:43 +07:00
/// <summary>
/// This is used only for unit tests to set the delegate to look up aliases/names dictionary of a content type
/// </summary>
internal static Func < string , Dictionary < string , string >> GetPropertyAliasesAndNames
{
get
{
return _getPropertyAliasesAndNames ?? ( _getPropertyAliasesAndNames = alias =>
{
var userFields = ContentType . GetAliasesAndNames ( alias );
//ensure the standard fields are there
var allFields = new Dictionary < string , string >()
{
{ "Id" , "Id" },
{ "NodeName" , "NodeName" },
{ "NodeTypeAlias" , "NodeTypeAlias" },
{ "CreateDate" , "CreateDate" },
{ "UpdateDate" , "UpdateDate" },
{ "CreatorName" , "CreatorName" },
{ "WriterName" , "WriterName" },
{ "Url" , "Url" }
};
2013-09-05 17:47:13 +02:00
foreach ( var f in userFields . Where ( f => allFields . ContainsKey ( f . Key ) == false ))
2012-09-20 12:42:43 +07:00
{
allFields . Add ( f . Key , f . Value );
}
return allFields ;
});
}
set { _getPropertyAliasesAndNames = value ; }
2013-09-05 17:47:13 +02:00
}
#endregion
}
2013-12-12 15:32:52 +01:00
}