using System; using System.Collections.Generic; using System.Data; using Examine; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Extensions { public static class FriendlyPublishedContentExtensions { private static IVariationContextAccessor VariationContextAccessor { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IPublishedModelFactory PublishedModelFactory { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IPublishedUrlProvider PublishedUrlProvider { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IUserService UserService { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IUmbracoContextAccessor UmbracoContextAccessor { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static ISiteDomainHelper SiteDomainHelper { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IExamineManager ExamineManager { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IFileService FileService { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IOptions WebRoutingSettings { get; } = StaticServiceProvider.Instance.GetRequiredService>(); private static IContentTypeService ContentTypeService { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IPublishedValueFallback PublishedValueFallback { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IPublishedSnapshot PublishedSnapshot { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IMediaTypeService MediaTypeService { get; } = StaticServiceProvider.Instance.GetRequiredService(); private static IMemberTypeService MemberTypeService { get; } = StaticServiceProvider.Instance.GetRequiredService(); /// /// Creates a strongly typed published content model for an internal published content. /// /// The internal published content. /// The strongly typed published content model. public static IPublishedContent CreateModel( this IPublishedContent content) => content.CreateModel(PublishedModelFactory); /// /// Gets the name of the content item. /// /// The content item. /// The specific culture to get the name for. If null is used the current culture is used (Default is null). public static string Name( this IPublishedContent content, string culture = null) => content.Name(VariationContextAccessor, culture); /// /// Gets the URL segment of the content item. /// /// The content item. /// The specific culture to get the URL segment for. If null is used the current culture is used (Default is null). public static string UrlSegment( this IPublishedContent content, string culture = null) => content.UrlSegment(VariationContextAccessor, culture); /// /// Gets the culture date of the content item. /// /// The content item. /// The specific culture to get the name for. If null is used the current culture is used (Default is null). public static DateTime CultureDate( this IPublishedContent content, string culture = null) => content.CultureDate(VariationContextAccessor, culture); /// /// Returns the current template Alias /// /// Empty string if none is set. public static string GetTemplateAlias(this IPublishedContent content) => content.GetTemplateAlias(FileService); public static bool IsAllowedTemplate(this IPublishedContent content, int templateId) => content.IsAllowedTemplate(ContentTypeService, WebRoutingSettings.Value, templateId); public static bool IsAllowedTemplate( this IPublishedContent content, bool disableAlternativeTemplates, bool validateAlternativeTemplates, int templateId) => content.IsAllowedTemplate( ContentTypeService, disableAlternativeTemplates, validateAlternativeTemplates, templateId); public static bool IsAllowedTemplate( this IPublishedContent content, bool disableAlternativeTemplates, bool validateAlternativeTemplates, string templateAlias) => content.IsAllowedTemplate( FileService, ContentTypeService, disableAlternativeTemplates, validateAlternativeTemplates, templateAlias); /// /// Gets a value indicating whether the content has a value for a property identified by its alias. /// /// The content. /// The property alias. /// The variation language. /// The variation segment. /// Optional fallback strategy. /// A value indicating whether the content has a value for the property identified by the alias. /// Returns true if HasValue is true, or a fallback strategy can provide a value. public static bool HasValue( this IPublishedContent content, string alias, string culture = null, string segment = null, Fallback fallback = default) => content.HasValue(PublishedValueFallback, alias, culture, segment, fallback); /// /// Gets the value of a content's property identified by its alias, if it exists, otherwise a default value. /// /// The content. /// 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. public static object Value(this IPublishedContent content, string alias, string culture = null, string segment = null, Fallback fallback = default, object defaultValue = default) => content.Value(PublishedValueFallback, alias, culture, segment, fallback, defaultValue); /// /// Gets the value of a content's property identified by its alias, converted to a specified type. /// /// The target property type. /// The content. /// 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. public static T Value(this IPublishedContent content, string alias, string culture = null, string segment = null, Fallback fallback = default, T defaultValue = default) => content.Value(PublishedValueFallback, alias, culture, segment, fallback, defaultValue); /// /// Returns all DescendantsOrSelf of all content referenced /// /// /// /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// /// /// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot /// public static IEnumerable DescendantsOrSelfOfType( this IEnumerable parentNodes, string docTypeAlias, string culture = null) => parentNodes.DescendantsOrSelfOfType(VariationContextAccessor, docTypeAlias, culture); /// /// Returns all DescendantsOrSelf of all content referenced /// /// /// Variation context accessor. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// /// /// This can be useful in order to return all nodes in an entire site by a type when combined with TypedContentAtRoot /// public static IEnumerable DescendantsOrSelf( this IEnumerable parentNodes, string culture = null) where T : class, IPublishedContent => parentNodes.DescendantsOrSelf(VariationContextAccessor, culture); public static IEnumerable Descendants(this IPublishedContent content, string culture = null) => content.Descendants(VariationContextAccessor, culture); public static IEnumerable Descendants(this IPublishedContent content, int level, string culture = null) => content.Descendants(VariationContextAccessor, level, culture); public static IEnumerable DescendantsOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.DescendantsOfType(VariationContextAccessor, contentTypeAlias, culture); public static IEnumerable Descendants(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.Descendants(VariationContextAccessor, culture); public static IEnumerable Descendants(this IPublishedContent content, int level, string culture = null) where T : class, IPublishedContent => content.Descendants(VariationContextAccessor, level, culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string culture = null) => content.DescendantsOrSelf(VariationContextAccessor, culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level, string culture = null) => content.DescendantsOrSelf(VariationContextAccessor, level, culture); public static IEnumerable DescendantsOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.DescendantsOrSelfOfType(VariationContextAccessor, contentTypeAlias, culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.DescendantsOrSelf(VariationContextAccessor, culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level, string culture = null) where T : class, IPublishedContent => content.DescendantsOrSelf(VariationContextAccessor, level, culture); public static IPublishedContent Descendant(this IPublishedContent content, string culture = null) => content.Descendant(VariationContextAccessor, culture); public static IPublishedContent Descendant(this IPublishedContent content, int level, string culture = null) => content.Descendant(VariationContextAccessor, level, culture); public static IPublishedContent DescendantOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.DescendantOfType(VariationContextAccessor, contentTypeAlias, culture); public static T Descendant(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.Descendant(VariationContextAccessor, culture); public static T Descendant(this IPublishedContent content, int level, string culture = null) where T : class, IPublishedContent => content.Descendant(VariationContextAccessor, level, culture); public static IPublishedContent DescendantOrSelf(this IPublishedContent content, string culture = null) => content.DescendantOrSelf(VariationContextAccessor, culture); public static IPublishedContent DescendantOrSelf(this IPublishedContent content, int level, string culture = null) => content.DescendantOrSelf(VariationContextAccessor, level, culture); public static IPublishedContent DescendantOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.DescendantOrSelfOfType(VariationContextAccessor, contentTypeAlias, culture); public static T DescendantOrSelf(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.DescendantOrSelf(VariationContextAccessor, culture); public static T DescendantOrSelf(this IPublishedContent content, int level, string culture = null) where T : class, IPublishedContent => content.DescendantOrSelf(VariationContextAccessor, level, culture); /// /// Gets the children of the content item. /// /// The content item. /// /// The specific culture to get the URL children for. Default is null which will use the current culture in /// /// /// Gets children that are available for the specified culture. /// Children are sorted by their sortOrder. /// /// For culture, /// if null is used the current culture is used. /// If an empty string is used only invariant children are returned. /// If "*" is used all children are returned. /// /// /// If a variant culture is specified or there is a current culture in the then the Children returned /// will include both the variant children matching the culture AND the invariant children because the invariant children flow with the current culture. /// However, if an empty string is specified only invariant children are returned. /// /// public static IEnumerable Children(this IPublishedContent content, string culture = null) => content.Children(VariationContextAccessor, culture); /// /// Gets the children of the content, filtered by a predicate. /// /// The content. /// The predicate. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The children of the content, filtered by the predicate. /// /// Children are sorted by their sortOrder. /// public static IEnumerable Children(this IPublishedContent content, Func predicate, string culture = null) => content.Children(VariationContextAccessor, predicate, culture); /// /// Gets the children of the content, of any of the specified types. /// /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The content type alias. /// The children of the content, of any of the specified types. public static IEnumerable ChildrenOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.ChildrenOfType(VariationContextAccessor, contentTypeAlias, culture); /// /// Gets the children of the content, of a given content type. /// /// The content type. /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The children of content, of the given content type. /// /// Children are sorted by their sortOrder. /// public static IEnumerable Children(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.Children(VariationContextAccessor, culture); public static IPublishedContent FirstChild(this IPublishedContent content, string culture = null) => content.FirstChild(VariationContextAccessor, culture); /// /// Gets the first child of the content, of a given content type. /// public static IPublishedContent FirstChildOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.FirstChildOfType(VariationContextAccessor, contentTypeAlias, culture); public static IPublishedContent FirstChild(this IPublishedContent content, Func predicate, string culture = null) => content.FirstChild(VariationContextAccessor, predicate, culture); public static IPublishedContent FirstChild(this IPublishedContent content, Guid uniqueId, string culture = null) => content.FirstChild(VariationContextAccessor, uniqueId, culture); public static T FirstChild(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.FirstChild(VariationContextAccessor, culture); public static T FirstChild(this IPublishedContent content, Func predicate, string culture = null) where T : class, IPublishedContent => content.FirstChild(VariationContextAccessor, predicate, culture); /// /// Gets the siblings of the content. /// /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The siblings of the content. /// /// Note that in V7 this method also return the content node self. /// public static IEnumerable Siblings(this IPublishedContent content, string culture = null) => content.Siblings(PublishedSnapshot, VariationContextAccessor, culture); /// /// Gets the siblings of the content, of a given content type. /// /// The content. /// The content type alias. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The siblings of the content, of the given content type. /// /// Note that in V7 this method also return the content node self. /// public static IEnumerable SiblingsOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.SiblingsOfType(PublishedSnapshot, VariationContextAccessor, contentTypeAlias, culture); /// /// Gets the siblings of the content, of a given content type. /// /// The content type. /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The siblings of the content, of the given content type. /// /// Note that in V7 this method also return the content node self. /// public static IEnumerable Siblings(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.Siblings(PublishedSnapshot, VariationContextAccessor, culture); /// /// Gets the siblings of the content including the node itself to indicate the position. /// /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The siblings of the content including the node itself. public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) => content.SiblingsAndSelf(PublishedSnapshot, VariationContextAccessor, culture); /// /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. /// /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The content type alias. /// The siblings of the content including the node itself, of the given content type. public static IEnumerable SiblingsAndSelfOfType(this IPublishedContent content, string contentTypeAlias, string culture = null) => content.SiblingsAndSelfOfType(PublishedSnapshot, VariationContextAccessor, contentTypeAlias, culture); /// /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. /// /// The content type. /// The content. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The siblings of the content including the node itself, of the given content type. public static IEnumerable SiblingsAndSelf(this IPublishedContent content, string culture = null) where T : class, IPublishedContent => content.SiblingsAndSelf(PublishedSnapshot, VariationContextAccessor, culture); /// /// Gets the url of the content item. /// /// /// If the content item is a document, then this method returns the url of the /// document. If it is a media, then this methods return the media url for the /// 'umbracoFile' property. Use the MediaUrl() method to get the media url for other /// properties. /// 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 Url(this IPublishedContent content, string culture = null, UrlMode mode = UrlMode.Default) => content.Url(PublishedUrlProvider, culture, mode); /// /// Gets the children of the content in a DataTable. /// /// The content. /// Variation context accessor. /// The content type service. /// The media type service. /// The member type service. /// The published url provider. /// An optional content type alias. /// The specific culture to filter for. If null is used the current culture is used. (Default is null) /// The children of the content. public static DataTable ChildrenAsTable(this IPublishedContent content, string contentTypeAliasFilter = "", string culture = null) => content.ChildrenAsTable(VariationContextAccessor, ContentTypeService, MediaTypeService, MemberTypeService, PublishedUrlProvider, contentTypeAliasFilter, culture); /// /// Gets the url for a media. /// /// The content item. /// 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, string culture = null, UrlMode mode = UrlMode.Default, string propertyAlias = Constants.Conventions.Media.File) => content.MediaUrl(PublishedUrlProvider, culture, mode, propertyAlias); /// /// Gets the name of the content item creator. /// /// The content item. public static string CreatorName(this IPublishedContent content) => content.CreatorName(UserService); /// /// Gets the name of the content item writer. /// /// The content item. public static string WriterName(this IPublishedContent content) => content.WriterName(UserService); /// /// Gets the culture assigned to a document by domains, in the context of a current Uri. /// /// The document. /// An optional current Uri. /// The culture assigned to the document by domains. /// /// In 1:1 multilingual setup, a document contains several cultures (there is not /// one document per culture), and domains, withing the context of a current Uri, assign /// a culture to that document. /// public static string GetCultureFromDomains( this IPublishedContent content, Uri current = null) => content.GetCultureFromDomains(UmbracoContextAccessor, SiteDomainHelper, current); public static IEnumerable SearchDescendants( this IPublishedContent content, string term, string indexName = null) => content.SearchDescendants(ExamineManager, UmbracoContextAccessor, term, indexName); public static IEnumerable SearchChildren( this IPublishedContent content, string term, string indexName = null) => content.SearchChildren(ExamineManager, UmbracoContextAccessor, term, indexName); } }