// Copyright (c) Umbraco. // See LICENSE for more details. using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Routing; namespace Umbraco.Extensions; /// /// Provides extension methods for IPublishedElement. /// public static class PublishedElementExtensions { #region OfTypes // the .OfType() filter is nice when there's only one type // this is to support filtering with multiple types public static IEnumerable OfTypes(this IEnumerable contents, params string[] types) where T : IPublishedElement { if (types == null || types.Length == 0) { return Enumerable.Empty(); } return contents.Where(x => types.InvariantContains(x.ContentType.Alias)); } #endregion #region IsComposedOf /// /// Gets a value indicating whether the content is of a content type composed of the given alias /// /// The content. /// The content type alias. /// /// A value indicating whether the content is of a content type composed of a content type identified by the /// alias. /// public static bool IsComposedOf(this IPublishedElement content, string alias) => content.ContentType.CompositionAliases.InvariantContains(alias); #endregion #region HasProperty /// /// Gets a value indicating whether the content has a property identified by its alias. /// /// The content. /// The property alias. /// A value indicating whether the content has the property identified by the alias. /// The content may have a property, and that property may not have a value. public static bool HasProperty(this IPublishedElement content, string alias) => content.ContentType.GetPropertyType(alias) != null; #endregion #region HasValue /// /// Gets a value indicating whether the content has a value for a property identified by its alias. /// /// /// Returns true if GetProperty(alias) is not null and GetProperty(alias).HasValue is /// true. /// public static bool HasValue(this IPublishedElement content, string alias, string? culture = null, string? segment = null) { IPublishedProperty? prop = content.GetProperty(alias); return prop != null && prop.HasValue(culture, segment); } #endregion #region Value /// /// Gets the value of a content's property identified by its alias. /// /// The content. /// The published value fallback implementation. /// The property alias. /// The variation language. /// The variation segment. /// Optional fallback strategy. /// The default value. /// The value of the content's property identified by the alias, if it exists, otherwise a default value. /// /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering /// content. /// /// /// If no property with the specified alias exists, or if the property has no value, returns /// . /// /// /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the /// converter. /// /// The alias is case-insensitive. /// public static object? Value( this IPublishedElement content, IPublishedValueFallback publishedValueFallback, string alias, string? culture = null, string? segment = null, Fallback fallback = default, object? defaultValue = default) { IPublishedProperty? property = content.GetProperty(alias); // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) { return property.GetValue(culture, segment); } // else let fallback try to get a value if (publishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out var value)) { return value; } // else... if we have a property, at least let the converter return its own // vision of 'no value' (could be an empty enumerable) - otherwise, default return property?.GetValue(culture, segment); } #endregion #region Value /// /// Gets the value of a content's property identified by its alias, converted to a specified type. /// /// The target property type. /// The content. /// The published value fallback implementation. /// The property alias. /// The variation language. /// The variation segment. /// Optional fallback strategy. /// The default value. /// The value of the content's property identified by the alias, converted to the specified type. /// /// /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering /// content. /// /// /// If no property with the specified alias exists, or if the property has no value, or if it could not be /// converted, returns default(T). /// /// /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the /// converter. /// /// The alias is case-insensitive. /// public static T? Value( this IPublishedElement content, IPublishedValueFallback publishedValueFallback, string alias, string? culture = null, string? segment = null, Fallback fallback = default, T? defaultValue = default) { IPublishedProperty? property = content.GetProperty(alias); // if we have a property, and it has a value, return that value if (property != null && property.HasValue(culture, segment)) { return property.Value(publishedValueFallback, culture, segment); } // else let fallback try to get a value if (publishedValueFallback.TryGetValue(content, alias, culture, segment, fallback, defaultValue, out T? value)) { return value; } // else... if we have a property, at least let the converter return its own // vision of 'no value' (could be an empty enumerable) - otherwise, default return property == null ? default : property.Value(publishedValueFallback, culture, segment); } #endregion #region ToIndexedArray public static IndexedArrayItem[] ToIndexedArray(this IEnumerable source) where TContent : class, IPublishedElement { IndexedArrayItem[] set = source.Select((content, index) => new IndexedArrayItem(content, index)).ToArray(); foreach (IndexedArrayItem setItem in set) { setItem.TotalCount = set.Length; } return set; } #endregion #region IsSomething /// /// Gets a value indicating whether the content is visible. /// /// The content. /// The published value fallback implementation. /// A value indicating whether the content is visible. /// /// A content is not visible if it has an umbracoNaviHide property with a value of "1". Otherwise, /// the content is visible. /// public static bool IsVisible(this IPublishedElement content, IPublishedValueFallback publishedValueFallback) => // 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. content.Value(publishedValueFallback, Constants.Conventions.Content.NaviHide) == false; #endregion #region MediaUrl /// /// Gets the url for a media. /// /// The content item. /// The published url provider. /// The culture (use current culture by default). /// The url mode (use site configuration by default). /// The alias of the property (use 'umbracoFile' by default). /// The url for the media. /// /// /// The value of this property is contextual. It depends on the 'current' request uri, /// if any. In addition, when the content type is multi-lingual, this is the url for the /// specified culture. Otherwise, it is the invariant url. /// /// public static string MediaUrl( this IPublishedContent content, IPublishedUrlProvider publishedUrlProvider, string? culture = null, UrlMode mode = UrlMode.Default, string propertyAlias = Constants.Conventions.Media.File) { if (publishedUrlProvider == null) { throw new ArgumentNullException(nameof(publishedUrlProvider)); } return publishedUrlProvider.GetMediaUrl(content, mode, culture, propertyAlias); } #endregion }