From 790c451df18b1eaefb1dac04b458661f141783af Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 25 Feb 2025 13:25:33 +0100 Subject: [PATCH] Published status filtering (#18281) * Initial refactor (pending more tests) * Fix structural querying across changing publish states + add tests accordingly * Add tests to validate ancestor and descendant order * Remove axis querying from published status filtering --------- Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> --- .../Services/ApiMediaQueryService.cs | 12 +- .../Query/ExecuteTemplateQueryController.cs | 55 +- .../DeliveryApi/ApiContentRouteBuilder.cs | 29 +- .../DependencyInjection/UmbracoBuilder.cs | 3 + .../ListDescendantsFromCurrentPage.cshtml | 9 +- .../EmbeddedResources/Snippets/SiteMap.cshtml | 7 +- ...rningsWhenPublishingNotificationHandler.cs | 101 +- .../Extensions/PublishedContentExtensions.cs | 3519 +++++++---------- .../PublishedContent/PublishedContentBase.cs | 8 +- .../PublishedValueFallback.cs | 2 +- .../Internal/InternalPublishedContent.cs | 9 +- src/Umbraco.Core/Routing/AliasUrlProvider.cs | 57 +- .../Routing/ContentFinderByUrlAlias.cs | 72 +- .../Routing/DefaultUrlProvider.cs | 61 +- src/Umbraco.Core/Routing/DomainUtilities.cs | 52 +- .../Routing/NewDefaultUrlProvider.cs | 38 +- src/Umbraco.Core/Routing/UrlProvider.cs | 75 +- .../Routing/UrlProviderExtensions.cs | 51 +- ...IPublishedContentStatusFilteringService.cs | 5 + .../IPublishedMediaStatusFilteringService.cs | 5 + .../IPublishedStatusFilteringService.cs | 8 + .../PublishedContentStatusFilteringService.cs | 50 + .../PublishedMediaStatusFilteringService.cs | 19 + .../Routing/RedirectTracker.cs | 64 +- .../PublishedContent.cs | 8 +- .../FriendlyPublishedContentExtensions.cs | 152 +- .../Extensions/PublishedContentExtensions.cs | 31 +- .../Controllers/UmbLoginController.cs | 73 +- .../DocumentNavigationServiceTests.cs | 43 + .../UrlAndDomains/DomainAndUrlsTests.cs | 4 +- .../DeliveryApi/ContentBuilderTests.cs | 8 +- .../DeliveryApi/ContentRouteBuilderTests.cs | 93 +- .../DeliveryApi/DeliveryApiTests.cs | 3 +- .../MultiNodeTreePickerValueConverterTests.cs | 2 +- .../PropertyValueConverterTests.cs | 33 + .../Routing/ContentFinderByUrlAliasTests.cs | 20 +- ...ishedContentStatusFilteringServiceTests.cs | 357 ++ .../Templates/HtmlImageSourceParserTests.cs | 3 +- .../Templates/HtmlLocalLinkParserTests.cs | 11 +- .../Umbraco.Tests.UnitTests.csproj | 4 +- 40 files changed, 2730 insertions(+), 2426 deletions(-) create mode 100644 src/Umbraco.Core/Services/PublishStatus/IPublishedContentStatusFilteringService.cs create mode 100644 src/Umbraco.Core/Services/PublishStatus/IPublishedMediaStatusFilteringService.cs create mode 100644 src/Umbraco.Core/Services/PublishStatus/IPublishedStatusFilteringService.cs create mode 100644 src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs create mode 100644 src/Umbraco.Core/Services/PublishStatus/PublishedMediaStatusFilteringService.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs diff --git a/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs b/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs index 9dcf85b6ef..0eeeb4d6da 100644 --- a/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs +++ b/src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs @@ -16,12 +16,18 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService private readonly IPublishedMediaCache _publishedMediaCache; private readonly ILogger _logger; private readonly IMediaNavigationQueryService _mediaNavigationQueryService; + private readonly IPublishedMediaStatusFilteringService _publishedMediaStatusFilteringService; - public ApiMediaQueryService(IPublishedMediaCache publishedMediaCache, ILogger logger, IMediaNavigationQueryService mediaNavigationQueryService) + public ApiMediaQueryService( + IPublishedMediaCache publishedMediaCache, + ILogger logger, + IMediaNavigationQueryService mediaNavigationQueryService, + IPublishedMediaStatusFilteringService publishedMediaStatusFilteringService) { _publishedMediaCache = publishedMediaCache; _logger = logger; _mediaNavigationQueryService = mediaNavigationQueryService; + _publishedMediaStatusFilteringService = publishedMediaStatusFilteringService; } /// @@ -71,7 +77,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService break; } - currentChildren = resolvedMedia.Children(null, _publishedMediaCache, _mediaNavigationQueryService); + currentChildren = resolvedMedia.Children(_mediaNavigationQueryService, _publishedMediaStatusFilteringService); } return resolvedMedia; @@ -104,7 +110,7 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService ? mediaCache.GetById(parentKey) : TryGetByPath(childrenOf, mediaCache); - return parent?.Children(null, _publishedMediaCache, _mediaNavigationQueryService) ?? Array.Empty(); + return parent?.Children(_mediaNavigationQueryService, _publishedMediaStatusFilteringService) ?? Array.Empty(); } private IEnumerable? ApplyFilters(IEnumerable source, IEnumerable filters) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs index 3f22c3931c..b491b45f59 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Template/Query/ExecuteTemplateQueryController.cs @@ -1,12 +1,13 @@ using System.Diagnostics; using System.Linq.Expressions; -using System.Runtime.Versioning; using System.Text; using Asp.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Api.Management.ViewModels.Template.Query; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Models.TemplateQuery; using Umbraco.Cms.Core.PublishedCache; @@ -20,14 +21,46 @@ namespace Umbraco.Cms.Api.Management.Controllers.Template.Query; public class ExecuteTemplateQueryController : TemplateQueryControllerBase { private readonly IPublishedContentQuery _publishedContentQuery; - private readonly IVariationContextAccessor _variationContextAccessor; private readonly IPublishedValueFallback _publishedValueFallback; private readonly IContentTypeService _contentTypeService; - private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _documentNavigationQueryService; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; private static readonly string _indent = $"{Environment.NewLine} "; + public ExecuteTemplateQueryController( + IPublishedContentQuery publishedContentQuery, + IPublishedValueFallback publishedValueFallback, + IContentTypeService contentTypeService, + IDocumentNavigationQueryService documentNavigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + { + _publishedContentQuery = publishedContentQuery; + _publishedValueFallback = publishedValueFallback; + _contentTypeService = contentTypeService; + _documentNavigationQueryService = documentNavigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; + } + + [Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")] + public ExecuteTemplateQueryController( + IPublishedContentQuery publishedContentQuery, + IVariationContextAccessor variationContextAccessor, + IPublishedValueFallback publishedValueFallback, + IContentTypeService contentTypeService, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService documentNavigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + : this( + publishedContentQuery, + publishedValueFallback, + contentTypeService, + documentNavigationQueryService, + publishedContentStatusFilteringService) + { + } + + [Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")] public ExecuteTemplateQueryController( IPublishedContentQuery publishedContentQuery, IVariationContextAccessor variationContextAccessor, @@ -35,13 +68,13 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase IContentTypeService contentTypeService, IPublishedContentCache contentCache, IDocumentNavigationQueryService documentNavigationQueryService) + : this( + publishedContentQuery, + publishedValueFallback, + contentTypeService, + documentNavigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) { - _publishedContentQuery = publishedContentQuery; - _variationContextAccessor = variationContextAccessor; - _publishedValueFallback = publishedValueFallback; - _contentTypeService = contentTypeService; - _contentCache = contentCache; - _documentNavigationQueryService = documentNavigationQueryService; } [HttpPost("execute")] @@ -118,13 +151,13 @@ public class ExecuteTemplateQueryController : TemplateQueryControllerBase queryExpression.Append(".ChildrenOfType(\"").Append(model.DocumentTypeAlias).Append("\")"); return rootContent == null ? Enumerable.Empty() - : rootContent.ChildrenOfType(_variationContextAccessor, _contentCache, _documentNavigationQueryService, model.DocumentTypeAlias); + : rootContent.ChildrenOfType(_documentNavigationQueryService, _publishedContentStatusFilteringService, model.DocumentTypeAlias); } queryExpression.Append(".Children()"); return rootContent == null ? Enumerable.Empty() - : rootContent.Children(_variationContextAccessor, _contentCache, _documentNavigationQueryService); + : rootContent.Children(_documentNavigationQueryService, _publishedContentStatusFilteringService); } private IEnumerable ApplyFiltering(IEnumerable? filters, IEnumerable contentQuery, StringBuilder queryExpression) diff --git a/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs b/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs index c5d93d979c..1868ee0aea 100644 --- a/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs +++ b/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs @@ -42,7 +42,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder requestSettings.OnChange(settings => _requestSettings = settings); } - [Obsolete("Use constructor that takes an IPublishStatusQueryService instead, scheduled for removal in v17")] + [Obsolete("Use the non-obsolete constructor, scheduled for removal in v17")] public ApiContentRouteBuilder( IApiContentPathProvider apiContentPathProvider, IOptions globalSettings, @@ -80,7 +80,12 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder contentPath = contentPath.EnsureStartsWith("/"); - IPublishedContent root = GetRoot(content, isPreview); + IPublishedContent? root = GetRoot(content, isPreview); + if (root is null) + { + return null; + } + var rootPath = root.UrlSegment(_variationContextAccessor, culture) ?? string.Empty; if (_globalSettings.HideTopLevelNodeFromPath == false) @@ -127,19 +132,21 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder private static bool IsInvalidContentPath(string? path) => path.IsNullOrWhiteSpace() || "#".Equals(path); - private IPublishedContent GetRoot(IPublishedContent content, bool isPreview) + private IPublishedContent? GetRoot(IPublishedContent content, bool isPreview) { - if (isPreview is false) + if (content.Level == 1) { - return content.Root(_variationContextAccessor, _contentCache, _navigationQueryService, _publishStatusQueryService); + return content; } - _navigationQueryService.TryGetRootKeys(out IEnumerable rootKeys); - IEnumerable rootContent = rootKeys.Select(x => _contentCache.GetById(true, x)).WhereNotNull(); + if (_navigationQueryService.TryGetAncestorsKeys(content.Key, out IEnumerable ancestorKeys) is false) + { + return null; + } - // in very edge case scenarios during preview, content.Root() does not map to the root. - // we'll code our way around it for the time being. - return rootContent.FirstOrDefault(root => root.IsAncestorOrSelf(content)) - ?? content.Root(_variationContextAccessor, _contentCache, _navigationQueryService, _publishStatusQueryService); + Guid[] ancestorKeysAsArray = ancestorKeys as Guid[] ?? ancestorKeys.ToArray(); + return ancestorKeysAsArray.Length > 0 + ? _contentCache.GetById(isPreview, ancestorKeysAsArray.Last()) + : content; } } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 95c6da574a..3d36c67d3a 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -387,6 +387,9 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddUnique(x => x.GetRequiredService()); Services.AddUnique(x => x.GetRequiredService()); + Services.AddUnique(); + Services.AddUnique(); + // Register a noop IHtmlSanitizer & IMarkdownSanitizer to be replaced Services.AddUnique(); Services.AddUnique(); diff --git a/src/Umbraco.Core/EmbeddedResources/Snippets/ListDescendantsFromCurrentPage.cshtml b/src/Umbraco.Core/EmbeddedResources/Snippets/ListDescendantsFromCurrentPage.cshtml index f174b38495..8fcde8691b 100644 --- a/src/Umbraco.Core/EmbeddedResources/Snippets/ListDescendantsFromCurrentPage.cshtml +++ b/src/Umbraco.Core/EmbeddedResources/Snippets/ListDescendantsFromCurrentPage.cshtml @@ -7,15 +7,14 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @inject IPublishedValueFallback PublishedValueFallback @inject IPublishedUrlProvider PublishedUrlProvider -@inject IVariationContextAccessor VariationContextAccessor -@inject IPublishedContentCache PublishedContentCache @inject IDocumentNavigationQueryService DocumentNavigationQueryService +@inject IPublishedContentStatusFilteringService PublishedContentStatusFilteringService @* This snippet creates links for every single page (no matter how deep) below the page currently being viewed by the website visitor, displayed as nested unordered HTML lists. *@ -@{ var selection = Model?.Content.Children(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService).Where(x => x.IsVisible(PublishedValueFallback)).ToArray(); } +@{ var selection = Model?.Content.Children(DocumentNavigationQueryService, PublishedContentStatusFilteringService).Where(x => x.IsVisible(PublishedValueFallback)).ToArray(); } @* Ensure that the Current Page has children *@ @if (selection?.Length > 0) @@ -34,7 +33,7 @@ @* if this child page has any children, where the property umbracoNaviHide is not True *@ @{ var children = item - .Children(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService) + .Children(DocumentNavigationQueryService, PublishedContentStatusFilteringService) .Where(x => x.IsVisible(PublishedValueFallback)) .ToArray(); @@ -68,7 +67,7 @@ @* if the page has any children, where the property umbracoNaviHide is not True *@ @{ var children = item - .Children(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService) + .Children(DocumentNavigationQueryService, PublishedContentStatusFilteringService) .Where(x => x.IsVisible(PublishedValueFallback)) .ToArray(); diff --git a/src/Umbraco.Core/EmbeddedResources/Snippets/SiteMap.cshtml b/src/Umbraco.Core/EmbeddedResources/Snippets/SiteMap.cshtml index 9f2c1c4193..b657b70d21 100644 --- a/src/Umbraco.Core/EmbeddedResources/Snippets/SiteMap.cshtml +++ b/src/Umbraco.Core/EmbeddedResources/Snippets/SiteMap.cshtml @@ -7,9 +7,8 @@ @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage @inject IPublishedValueFallback PublishedValueFallback @inject IPublishedUrlProvider PublishedUrlProvider -@inject IVariationContextAccessor VariationContextAccessor -@inject IPublishedContentCache PublishedContentCache @inject IDocumentNavigationQueryService DocumentNavigationQueryService +@inject IPublishedContentStatusFilteringService PublishedContentStatusFilteringService @* This snippet makes a list of links of all visible pages of the site, as nested unordered HTML lists. @@ -17,7 +16,7 @@ - It uses a local method called Traverse() to select and display the markup and links. *@ -@{ var selection = Model?.Content.Root(PublishedContentCache, DocumentNavigationQueryService); } +@{ var selection = Model?.Content.Root(DocumentNavigationQueryService, PublishedContentStatusFilteringService); }
@* Render the sitemap by passing the root node to the traverse method, below *@ @@ -33,7 +32,7 @@ @* Select visible children *@ var selection = node - .Children(VariationContextAccessor, PublishedContentCache, DocumentNavigationQueryService) + .Children(DocumentNavigationQueryService, PublishedContentStatusFilteringService) .Where(x => x.IsVisible(PublishedValueFallback) && x.Level <= maxLevelForSitemap) .ToArray(); diff --git a/src/Umbraco.Core/Events/AddUnroutableContentWarningsWhenPublishingNotificationHandler.cs b/src/Umbraco.Core/Events/AddUnroutableContentWarningsWhenPublishingNotificationHandler.cs index ad457d287c..bb94a68d79 100644 --- a/src/Umbraco.Core/Events/AddUnroutableContentWarningsWhenPublishingNotificationHandler.cs +++ b/src/Umbraco.Core/Events/AddUnroutableContentWarningsWhenPublishingNotificationHandler.cs @@ -1,6 +1,8 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Notifications; @@ -24,11 +26,75 @@ public class AddUnroutableContentWarningsWhenPublishingNotificationHandler : INo private readonly ILoggerFactory _loggerFactory; private readonly UriUtility _uriUtility; private readonly IPublishedUrlProvider _publishedUrlProvider; - private readonly IPublishedContentCache _publishedContentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; private readonly IEventMessagesFactory _eventMessagesFactory; private readonly ContentSettings _contentSettings; + public AddUnroutableContentWarningsWhenPublishingNotificationHandler( + IPublishedRouter publishedRouter, + IUmbracoContextAccessor umbracoContextAccessor, + ILanguageService languageService, + ILocalizedTextService localizedTextService, + IContentService contentService, + IVariationContextAccessor variationContextAccessor, + ILoggerFactory loggerFactory, + UriUtility uriUtility, + IPublishedUrlProvider publishedUrlProvider, + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService, + IEventMessagesFactory eventMessagesFactory, + IOptions contentSettings) + { + _publishedRouter = publishedRouter; + _umbracoContextAccessor = umbracoContextAccessor; + _languageService = languageService; + _localizedTextService = localizedTextService; + _contentService = contentService; + _variationContextAccessor = variationContextAccessor; + _loggerFactory = loggerFactory; + _uriUtility = uriUtility; + _publishedUrlProvider = publishedUrlProvider; + _navigationQueryService = navigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; + _eventMessagesFactory = eventMessagesFactory; + _contentSettings = contentSettings.Value; + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public AddUnroutableContentWarningsWhenPublishingNotificationHandler( + IPublishedRouter publishedRouter, + IUmbracoContextAccessor umbracoContextAccessor, + ILanguageService languageService, + ILocalizedTextService localizedTextService, + IContentService contentService, + IVariationContextAccessor variationContextAccessor, + ILoggerFactory loggerFactory, + UriUtility uriUtility, + IPublishedUrlProvider publishedUrlProvider, + IPublishedContentCache publishedContentCache, + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService, + IEventMessagesFactory eventMessagesFactory, + IOptions contentSettings) + : this( + publishedRouter, + umbracoContextAccessor, + languageService, + localizedTextService, + contentService, + variationContextAccessor, + loggerFactory, + uriUtility, + publishedUrlProvider, + navigationQueryService, + publishedContentStatusFilteringService, + eventMessagesFactory, + contentSettings) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public AddUnroutableContentWarningsWhenPublishingNotificationHandler( IPublishedRouter publishedRouter, IUmbracoContextAccessor umbracoContextAccessor, @@ -43,20 +109,21 @@ public class AddUnroutableContentWarningsWhenPublishingNotificationHandler : INo IDocumentNavigationQueryService navigationQueryService, IEventMessagesFactory eventMessagesFactory, IOptions contentSettings) + : this( + publishedRouter, + umbracoContextAccessor, + languageService, + localizedTextService, + contentService, + variationContextAccessor, + loggerFactory, + uriUtility, + publishedUrlProvider, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService(), + eventMessagesFactory, + contentSettings) { - _publishedRouter = publishedRouter; - _umbracoContextAccessor = umbracoContextAccessor; - _languageService = languageService; - _localizedTextService = localizedTextService; - _contentService = contentService; - _variationContextAccessor = variationContextAccessor; - _loggerFactory = loggerFactory; - _uriUtility = uriUtility; - _publishedUrlProvider = publishedUrlProvider; - _publishedContentCache = publishedContentCache; - _navigationQueryService = navigationQueryService; - _eventMessagesFactory = eventMessagesFactory; - _contentSettings = contentSettings.Value; } public async Task HandleAsync(ContentPublishedNotification notification, CancellationToken cancellationToken) @@ -99,8 +166,8 @@ public class AddUnroutableContentWarningsWhenPublishingNotificationHandler : INo _loggerFactory.CreateLogger(), _uriUtility, _publishedUrlProvider, - _publishedContentCache, - _navigationQueryService)).ToArray(); + _navigationQueryService, + _publishedContentStatusFilteringService)).ToArray(); EventMessages eventMessages = _eventMessagesFactory.Get(); diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index a2204c9fb5..6b9b0359d7 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -123,36 +123,42 @@ public static class PublishedContentExtensions /// /// The content type. /// The content. - /// The content cache. /// The query service for the in-memory navigation structure. + /// /// The parent of content, of the given content type, else null. + public static T? Parent( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + where T : class, IPublishedContent + { + ArgumentNullException.ThrowIfNull(content); + + return content.GetParent(navigationQueryService, publishedStatusFilteringService) as T; + } + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Parent( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) where T : class, IPublishedContent - { - ArgumentNullException.ThrowIfNull(content); - - return content.GetParent(publishedCache, navigationQueryService) as T; - } + => content.Parent(navigationQueryService, GetPublishedStatusFilteringService(content)); private static IPublishedContent? GetParent( this IPublishedContent content, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService) + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) { - IPublishedContent? parent; - if (navigationQueryService.TryGetParentKey(content.Key, out Guid? parentKey)) - { - parent = parentKey.HasValue ? publishedCache.GetById(parentKey.Value) : null; - } - else + if (navigationQueryService.TryGetParentKey(content.Key, out Guid? parentKey) is false) { throw new KeyNotFoundException($"Content with key '{content.Key}' was not found in the in-memory navigation structure."); } - return parent; + // parent key is null if content is at root + return parentKey.HasValue + ? publishedStatusFilteringService.FilterAvailable([parentKey.Value], null).FirstOrDefault() + : null; } #endregion @@ -221,30 +227,6 @@ public static class PublishedContentExtensions public static bool IsInvariantOrHasCulture(this IPublishedContent content, string culture) => !content.ContentType.VariesByCulture() || content.Cultures.ContainsKey(culture ?? string.Empty); - /// - /// Filters a sequence of to return invariant items, and items that are published for - /// the specified culture. - /// - /// The content items. - /// - /// - /// The specific culture to filter for. If null is used the current culture is used. (Default is - /// null). - /// - internal static IEnumerable WhereIsInvariantOrHasCulture(this IEnumerable contents, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent - { - if (contents == null) - { - throw new ArgumentNullException(nameof(contents)); - } - - culture = culture ?? variationContextAccessor.VariationContext?.Culture ?? string.Empty; - - // either does not vary by culture, or has the specified culture - return contents.Where(x => !x.ContentType.VariesByCulture() || HasCulture(x, culture)); - } - /// /// Gets the culture date of the content item. /// @@ -521,106 +503,95 @@ public static class PublishedContentExtensions /// Gets the ancestors of the content. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The ancestors of the content, in down-top order. /// Does not consider the content itself. + public static IEnumerable Ancestors( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + => content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService, false, null); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService) => - content.AncestorsOrSelf( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - false, - null); + IPublishStatusQueryService publishStatusQueryService) + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the ancestors of the content. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The ancestors of the content, in down-top order. - /// Does not consider the content itself. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IPublishedCache publishedCache, - INavigationQueryService navigationQueryService) => Ancestors( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + INavigationQueryService navigationQueryService) + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the ancestors of the content, at a level lesser or equal to a specified level. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The level. /// The ancestors of the content, at a level lesser or equal to the specified level, in down-top order. /// Does not consider the content itself. Only content that are "high enough" in the tree are returned. + public static IEnumerable Ancestors( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + => content.AncestorsOrSelf( + navigationQueryService, + publishedStatusFilteringService, + false, + n => n.Level <= maxLevel); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, - int maxLevel) => - content.AncestorsOrSelf( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - false, - n => n.Level <= maxLevel); + int maxLevel) + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the ancestors of the content, at a level lesser or equal to a specified level. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// The ancestors of the content, at a level lesser or equal to the specified level, in down-top order. - /// Does not consider the content itself. Only content that are "high enough" in the tree are returned. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - int maxLevel) => - Ancestors( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); - + int maxLevel) + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); /// /// Gets the ancestors of the content, of a specified content type. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The content type. /// The ancestors of the content, of the specified content type, in down-top order. /// Does not consider the content itself. Returns all ancestors, of the specified content type. + public static IEnumerable Ancestors( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string contentTypeAlias) + { + ArgumentNullException.ThrowIfNull(content); + + return content.EnumerateAncestorsOrSelfInternal( + navigationQueryService, + publishedStatusFilteringService, + false, + contentTypeAlias); + } + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -628,82 +599,49 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias) - { - ArgumentNullException.ThrowIfNull(content); + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); - return content.EnumerateAncestorsOrSelfInternal( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - false, - contentTypeAlias); - } - - /// - /// Gets the ancestors of the content, of a specified content type. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The content type. - /// The ancestors of the content, of the specified content type, in down-top order. - /// Does not consider the content itself. Returns all ancestors, of the specified content type. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - string contentTypeAlias) => - Ancestors( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias); + string contentTypeAlias) + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); /// /// Gets the ancestors of the content, of a specified content type. /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The ancestors of the content, of the specified content type, in down-top order. /// Does not consider the content itself. Returns all ancestors, of the specified content type. + public static IEnumerable Ancestors( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, publishedStatusFilteringService).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService) - where T : class, IPublishedContent => - content.Ancestors(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService).OfType(); + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the ancestors of the content, of a specified content type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The ancestors of the content, of the specified content type, in down-top order. - /// Does not consider the content itself. Returns all ancestors, of the specified content type. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) - where T : class, IPublishedContent => - Ancestors( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the ancestors of the content, at a level lesser or equal to a specified level, and of a specified content @@ -711,10 +649,7 @@ public static class PublishedContentExtensions /// /// The content type. /// The content. - /// - /// The content cache. - /// The query service for the in-memory navigation structure. - /// + /// /// The level. /// /// The ancestors of the content, at a level lesser or equal to the specified level, and of the specified @@ -724,6 +659,15 @@ public static class PublishedContentExtensions /// Does not consider the content itself. Only content that are "high enough" in the trees, and of the /// specified content type, are returned. /// + public static IEnumerable Ancestors( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, publishedStatusFilteringService, maxLevel).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -731,91 +675,53 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, int maxLevel) - where T : class, IPublishedContent => - content.Ancestors(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, maxLevel).OfType(); + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the ancestors of the content, at a level lesser or equal to a specified level, and of a specified content - /// type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// - /// The ancestors of the content, at a level lesser or equal to the specified level, and of the specified - /// content type, in down-top order. - /// - /// - /// Does not consider the content itself. Only content that are "high enough" in the trees, and of the - /// specified content type, are returned. - /// - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Ancestors( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int maxLevel) - where T : class, IPublishedContent => - Ancestors( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); /// /// Gets the content and its ancestors. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The content and its ancestors, in down-top order. + public static IEnumerable AncestorsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + => content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService, true, null); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService) => - content.AncestorsOrSelf( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - true, - null); + IPublishStatusQueryService publishStatusQueryService) + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the content and its ancestors. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The content and its ancestors, in down-top order. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IPublishedCache publishedCache, - INavigationQueryService navigationQueryService) => - AncestorsOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + INavigationQueryService navigationQueryService) + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the content and its ancestors, at a level lesser or equal to a specified level. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The level. /// /// The content and its ancestors, at a level lesser or equal to the specified level, @@ -825,61 +731,60 @@ public static class PublishedContentExtensions /// Only content that are "high enough" in the tree are returned. So it may or may not begin /// with the content itself, depending on its level. /// + public static IEnumerable AncestorsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + => content.AncestorsOrSelf( + navigationQueryService, + publishedStatusFilteringService, + true, + n => n.Level <= maxLevel); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, - int maxLevel) => - content.AncestorsOrSelf( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - true, - n => n.Level <= maxLevel); + int maxLevel) + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the content and its ancestors, at a level lesser or equal to a specified level. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// - /// The content and its ancestors, at a level lesser or equal to the specified level, - /// in down-top order. - /// - /// - /// Only content that are "high enough" in the tree are returned. So it may or may not begin - /// with the content itself, depending on its level. - /// - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - int maxLevel) => - AncestorsOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); + int maxLevel) + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); /// /// Gets the content and its ancestors, of a specified content type. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The content type. /// The content and its ancestors, of the specified content type, in down-top order. /// May or may not begin with the content itself, depending on its content type. + public static IEnumerable AncestorsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string contentTypeAlias) + { + ArgumentNullException.ThrowIfNull(content); + + return content.EnumerateAncestorsOrSelfInternal( + navigationQueryService, + publishedStatusFilteringService, + true, + contentTypeAlias); + } + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -887,81 +792,49 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias) - { - ArgumentNullException.ThrowIfNull(content); + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); - return content.EnumerateAncestorsOrSelfInternal( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - true, - contentTypeAlias); - } - - /// - /// Gets the content and its ancestors, of a specified content type. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The content type. - /// The content and its ancestors, of the specified content type, in down-top order. - /// May or may not begin with the content itself, depending on its content type. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - string contentTypeAlias) => - AncestorsOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias); + string contentTypeAlias) + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); /// /// Gets the content and its ancestors, of a specified content type. /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The content and its ancestors, of the specified content type, in down-top order. /// May or may not begin with the content itself, depending on its content type. + public static IEnumerable AncestorsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + where T : class, IPublishedContent + => content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService) - where T : class, IPublishedContent => - content.AncestorsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService).OfType(); + where T : class, IPublishedContent + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the content and its ancestors, of a specified content type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The content and its ancestors, of the specified content type, in down-top order. - /// May or may not begin with the content itself, depending on its content type. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) - where T : class, IPublishedContent => AncestorsOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + where T : class, IPublishedContent + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the content and its ancestor, at a lever lesser or equal to a specified level, and of a specified content @@ -969,16 +842,23 @@ public static class PublishedContentExtensions /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The level. /// /// The content and its ancestors, at a level lesser or equal to the specified level, and of the specified /// content type, in down-top order. /// /// May or may not begin with the content itself, depending on its level and content type. + public static IEnumerable AncestorsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + where T : class, IPublishedContent + => content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService, maxLevel).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -986,210 +866,175 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, int maxLevel) - where T : class, IPublishedContent => - content.AncestorsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, maxLevel).OfType(); + where T : class, IPublishedContent + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the content and its ancestor, at a lever lesser or equal to a specified level, and of a specified content - /// type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// - /// The content and its ancestors, at a level lesser or equal to the specified level, and of the specified - /// content type, in down-top order. - /// - /// May or may not begin with the content itself, depending on its level and content type. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int maxLevel) - where T : class, IPublishedContent => AncestorsOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); + where T : class, IPublishedContent + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); /// /// Gets the ancestor of the content, ie its parent. /// /// The content. - /// The content cache. /// The query service for the in-memory navigation structure. + /// /// The ancestor of the content. /// This method is here for consistency purposes but does not make much sense. + public static IPublishedContent? Ancestor( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + => content.GetParent(navigationQueryService, publishedStatusFilteringService); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Ancestor( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) - => content.GetParent(publishedCache, navigationQueryService); + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the nearest ancestor of the content, at a lever lesser or equal to a specified level. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// The query service for the in-memory navigation structure. + /// /// The level. /// The nearest (in down-top order) ancestor of the content, at a level lesser or equal to the specified level. /// Does not consider the content itself. May return null. + public static IPublishedContent? Ancestor( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + => content + .EnumerateAncestors(navigationQueryService, publishedStatusFilteringService, false) + .FirstOrDefault(x => x.Level <= maxLevel); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Ancestor( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, - int maxLevel) => - content.EnumerateAncestors( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - false).FirstOrDefault(x => x.Level <= maxLevel); + int maxLevel) + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the nearest ancestor of the content, at a lever lesser or equal to a specified level. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// The nearest (in down-top order) ancestor of the content, at a level lesser or equal to the specified level. - /// Does not consider the content itself. May return null. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Ancestor( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - int maxLevel) => - Ancestor( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); + int maxLevel) + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); /// /// Gets the nearest ancestor of the content, of a specified content type. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The content type alias. /// The nearest (in down-top order) ancestor of the content, of the specified content type. /// Does not consider the content itself. May return null. public static IPublishedContent? Ancestor( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, string contentTypeAlias) { ArgumentNullException.ThrowIfNull(content); return content .EnumerateAncestorsOrSelfInternal( - variationContextAccessor, - publishedCache, navigationQueryService, - publishStatusQueryService, + publishedStatusFilteringService, false, - contentTypeAlias).FirstOrDefault(); + contentTypeAlias) + .FirstOrDefault(); } - /// - /// Gets the nearest ancestor of the content, of a specified content type. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The content type alias. - /// The nearest (in down-top order) ancestor of the content, of the specified content type. - /// Does not consider the content itself. May return null. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IPublishedContent? Ancestor( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + string contentTypeAlias) + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Ancestor( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - string contentTypeAlias) => - Ancestor( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias); + string contentTypeAlias) + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); /// /// Gets the nearest ancestor of the content, of a specified content type. /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The nearest (in down-top order) ancestor of the content, of the specified content type. /// Does not consider the content itself. May return null. + public static T? Ancestor( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, publishedStatusFilteringService).FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Ancestor( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService) - where T : class, IPublishedContent => - content.Ancestors(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService).FirstOrDefault(); + where T : class, IPublishedContent + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the nearest ancestor of the content, of a specified content type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The nearest (in down-top order) ancestor of the content, of the specified content type. - /// Does not consider the content itself. May return null. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Ancestor( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) - where T : class, IPublishedContent => - Ancestor( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + where T : class, IPublishedContent + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the nearest ancestor of the content, at the specified level and of the specified content type. /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The level. /// The ancestor of the content, at the specified level and of the specified content type. /// /// Does not consider the content itself. If the ancestor at the specified level is /// not of the specified type, returns null. /// + public static T? Ancestor( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + where T : class, IPublishedContent + => content.Ancestors(navigationQueryService, publishedStatusFilteringService, maxLevel).FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Ancestor( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1197,41 +1042,17 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, int maxLevel) - where T : class, IPublishedContent => - content.Ancestors( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - maxLevel).FirstOrDefault(); + where T : class, IPublishedContent + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the nearest ancestor of the content, at the specified level and of the specified content type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// The ancestor of the content, at the specified level and of the specified content type. - /// - /// Does not consider the content itself. If the ancestor at the specified level is - /// not of the specified type, returns null. - /// - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Ancestor( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int maxLevel) - where T : class, IPublishedContent => - Ancestor( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); + where T : class, IPublishedContent + => content.Ancestor(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); /// /// Gets the content or its nearest ancestor. @@ -1245,62 +1066,65 @@ public static class PublishedContentExtensions /// Gets the content or its nearest ancestor, at a lever lesser or equal to a specified level. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// The query service for the in-memory navigation structure. + /// /// The level. /// The content or its nearest (in down-top order) ancestor, at a level lesser or equal to the specified level. /// May or may not return the content itself depending on its level. May return null. + public static IPublishedContent AncestorOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + => content + .EnumerateAncestors(navigationQueryService, publishedStatusFilteringService, true) + .FirstOrDefault(x => x.Level <= maxLevel) ?? content; + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent AncestorOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, - int maxLevel) => - content.EnumerateAncestors( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - true) - .FirstOrDefault(x => x.Level <= maxLevel) ?? content; + int maxLevel) + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the content or its nearest ancestor, at a lever lesser or equal to a specified level. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// The content or its nearest (in down-top order) ancestor, at a level lesser or equal to the specified level. - /// May or may not return the content itself depending on its level. May return null. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent AncestorOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - int maxLevel) => - AncestorOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); + int maxLevel) + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); /// /// Gets the content or its nearest ancestor, of a specified content type. /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The content type. /// The content or its nearest (in down-top order) ancestor, of the specified content type. /// May or may not return the content itself depending on its content type. May return null. + public static IPublishedContent AncestorOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string contentTypeAlias) + { + ArgumentNullException.ThrowIfNull(content); + + return content + .EnumerateAncestorsOrSelfInternal( + navigationQueryService, + publishedStatusFilteringService, + true, + contentTypeAlias) + .FirstOrDefault() ?? content; + } + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent AncestorOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1308,83 +1132,49 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias) - { - ArgumentNullException.ThrowIfNull(content); + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); - return content - .EnumerateAncestorsOrSelfInternal( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - true, - contentTypeAlias) - .FirstOrDefault() ?? content; - } - - /// - /// Gets the content or its nearest ancestor, of a specified content type. - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The content type. - /// The content or its nearest (in down-top order) ancestor, of the specified content type. - /// May or may not return the content itself depending on its content type. May return null. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent AncestorOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - string contentTypeAlias) => AncestorOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias); + string contentTypeAlias) + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias); /// /// Gets the content or its nearest ancestor, of a specified content type. /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The content or its nearest (in down-top order) ancestor, of the specified content type. /// May or may not return the content itself depending on its content type. May return null. + public static T? AncestorOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + where T : class, IPublishedContent => + content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService).FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? AncestorOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService) - where T : class, IPublishedContent => - content.AncestorsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService).FirstOrDefault(); + where T : class, IPublishedContent + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the content or its nearest ancestor, of a specified content type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The content or its nearest (in down-top order) ancestor, of the specified content type. - /// May or may not return the content itself depending on its content type. May return null. - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? AncestorOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) - where T : class, IPublishedContent => - AncestorOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + where T : class, IPublishedContent + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the content or its nearest ancestor, at a lever lesser or equal to a specified level, and of a specified @@ -1392,12 +1182,19 @@ public static class PublishedContentExtensions /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// The level. /// + public static T? AncestorOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int maxLevel) + where T : class, IPublishedContent + => content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService, maxLevel).FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? AncestorOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1405,40 +1202,33 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, int maxLevel) - where T : class, IPublishedContent => - content.AncestorsOrSelf( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - maxLevel) - .FirstOrDefault(); + where T : class, IPublishedContent + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); - /// - /// Gets the content or its nearest ancestor, at a lever lesser or equal to a specified level, and of a specified - /// content type. - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// The level. - /// - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? AncestorOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int maxLevel) - where T : class, IPublishedContent => - AncestorOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - maxLevel); + where T : class, IPublishedContent + => content.AncestorOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), maxLevel); + public static IEnumerable AncestorsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + bool orSelf, + Func? func) + { + IEnumerable ancestorsOrSelf = content.EnumerateAncestors( + navigationQueryService, + publishedStatusFilteringService, + orSelf); + return func == null ? ancestorsOrSelf : ancestorsOrSelf.Where(func); + } + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1447,53 +1237,26 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, bool orSelf, Func? func) - { - IEnumerable ancestorsOrSelf = content.EnumerateAncestors( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - orSelf); - return func == null ? ancestorsOrSelf : ancestorsOrSelf.Where(func); - } + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), orSelf, func); - [Obsolete("Use the overload with IVariationContextAccessor and IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable AncestorsOrSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, bool orSelf, - Func? func) => - AncestorsOrSelf( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - orSelf, - func); + Func? func) + => content.AncestorsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), orSelf, func); - /// - /// Enumerates ancestors of the content, bottom-up. - /// - /// The content. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// - /// Indicates whether the content should be included. - /// Enumerates bottom-up ie walking up the tree (parent, grand-parent, etc). - internal static IEnumerable EnumerateAncestors( + private static IEnumerable EnumerateAncestors( this IPublishedContent? content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, bool orSelf) { ArgumentNullException.ThrowIfNull(content); - return content.EnumerateAncestorsOrSelfInternal(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, orSelf); + return content.EnumerateAncestorsOrSelfInternal(navigationQueryService, publishedStatusFilteringService, orSelf); } #endregion @@ -1504,39 +1267,56 @@ public static class PublishedContentExtensions /// Gets the breadcrumbs (ancestors and self, top to bottom) for the specified . /// /// The content. - /// The content cache. /// The query service for the in-memory navigation structure. + /// /// Indicates whether the specified content should be included. /// /// The breadcrumbs (ancestors and self, top to bottom) for the specified . /// + public static IEnumerable Breadcrumbs( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + bool andSelf = true) => + content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService, andSelf, null).Reverse(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Breadcrumbs( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - bool andSelf = true) => - content.AncestorsOrSelf(publishedCache, navigationQueryService, andSelf, null).Reverse(); + bool andSelf = true) + => content.Breadcrumbs(navigationQueryService, GetPublishedStatusFilteringService(content), andSelf); /// /// Gets the breadcrumbs (ancestors and self, top to bottom) for the specified at a level /// higher or equal to . /// /// The content. - /// The content cache. /// The query service for the in-memory navigation structure. + /// /// The minimum level. /// Indicates whether the specified content should be included. /// /// The breadcrumbs (ancestors and self, top to bottom) for the specified at a level higher /// or equal to . /// + public static IEnumerable Breadcrumbs( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int minLevel, + bool andSelf = true) + => content.AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService, andSelf, n => n.Level >= minLevel).Reverse(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Breadcrumbs( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int minLevel, - bool andSelf = true) => - content.AncestorsOrSelf(publishedCache, navigationQueryService, andSelf, n => n.Level >= minLevel).Reverse(); + bool andSelf = true) + => content.Breadcrumbs(navigationQueryService, GetPublishedStatusFilteringService(content), minLevel, andSelf); /// /// Gets the breadcrumbs (ancestors and self, top to bottom) for the specified at a level @@ -1544,56 +1324,38 @@ public static class PublishedContentExtensions /// /// The root content type. /// The content. - /// The content cache. /// The query service for the in-memory navigation structure. + /// /// Indicates whether the specified content should be included. /// /// The breadcrumbs (ancestors and self, top to bottom) for the specified at a level higher /// or equal to the specified root content type . /// + public static IEnumerable Breadcrumbs( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + bool andSelf = true) + where T : class, IPublishedContent + => content + .AncestorsOrSelf(navigationQueryService, publishedStatusFilteringService, andSelf, null) + .TakeWhile(n => n is T) + .Reverse(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Breadcrumbs( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, bool andSelf = true) where T : class, IPublishedContent - { - static IEnumerable TakeUntil(IEnumerable source, Func predicate) - { - foreach (IPublishedContent item in source) - { - yield return item; - if (predicate(item)) - { - yield break; - } - } - } - - return TakeUntil(content.AncestorsOrSelf(publishedCache, navigationQueryService, andSelf, null), n => n is T).Reverse(); - } + => content.Breadcrumbs(navigationQueryService, GetPublishedStatusFilteringService(content), andSelf); #endregion #region Axes: descendants, descendants-or-self - /// - /// 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 - /// + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelfOfType( this IEnumerable parentNodes, IVariationContextAccessor variationContextAccessor, @@ -1601,58 +1363,34 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string docTypeAlias, - string? culture = null) => parentNodes.SelectMany(x => - x.DescendantsOrSelfOfType(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, 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 - /// - [Obsolete("Use the overload with IPublishStatusQueryService instead, scheduled for removal in v17")] - public static IEnumerable DescendantsOrSelfOfType( - this IEnumerable parentNodes, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService, - string docTypeAlias, - string? culture = null) => - DescendantsOrSelfOfType( - parentNodes, - variationContextAccessor, - publishedCache, + string? culture = null) + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelfOfType( navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), 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 - /// + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable DescendantsOrSelfOfType( + this IEnumerable parentNodes, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + string docTypeAlias, + string? culture = null) + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelfOfType( + navigationQueryService, + GetPublishedStatusFilteringService(parentNodesAsArray.First()), + docTypeAlias, + culture); + } + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IEnumerable parentNodes, IVariationContextAccessor variationContextAccessor, @@ -1660,24 +1398,15 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - parentNodes.SelectMany(x => x.DescendantsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture)); + where T : class, IPublishedContent + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelf( + navigationQueryService, + GetPublishedStatusFilteringService(parentNodesAsArray.First()), + 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 - /// [Obsolete("Use the overload with IPublishStatusQueryService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IEnumerable parentNodes, @@ -1685,14 +1414,15 @@ public static class PublishedContentExtensions IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - DescendantsOrSelf( - parentNodes, - variationContextAccessor, - publishedCache, + where T : class, IPublishedContent + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelf( navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), culture); + } + // 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 @@ -1714,96 +1444,107 @@ public static class PublishedContentExtensions // - children and descendants occur before following siblings. public static IEnumerable Descendants( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.DescendantsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, false, null, culture); - - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] - public static IEnumerable Descendants( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService, - string? culture = null) => - Descendants( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => content.DescendantsOrSelf(navigationQueryService, publishedStatusFilteringService, false, null, culture); + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, + string? culture = null) + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + string? culture = null) + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + + public static IEnumerable Descendants( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, int level, - string? culture = null) => - content.DescendantsOrSelf( - variationContextAccessor, - publishedCache, + string? culture = null) + => content.DescendantsOrSelf( navigationQueryService, - publishStatusQueryService, + publishedStatusFilteringService, false, p => p.Level >= level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + int level, + string? culture = null) + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int level, - string? culture = null) => - Descendants( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + string? culture = null) + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); public static IEnumerable DescendantsOfType( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, string contentTypeAlias, - string? culture = null) => - content.EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - publishedCache, + string? culture = null) + => content.EnumerateDescendantsOrSelfInternal( navigationQueryService, - publishStatusQueryService, + publishedStatusFilteringService, culture, false, contentTypeAlias); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IEnumerable DescendantsOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + string contentTypeAlias, + string? culture = null) + => content.DescendantsOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string contentTypeAlias, - string? culture = null) => - DescendantsOfType( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); + string? culture = null) + => content.DescendantsOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + public static IEnumerable Descendants( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content.Descendants(navigationQueryService, publishedStatusFilteringService, culture).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1811,19 +1552,29 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Descendants(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture).OfType(); + where T : class, IPublishedContent + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Descendants(variationContextAccessor, publishedCache, navigationQueryService, culture).OfType(); + where T : class, IPublishedContent + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static IEnumerable Descendants( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int level, + string? culture = null) + where T : class, IPublishedContent + => content.Descendants(navigationQueryService, publishedStatusFilteringService, level, culture).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1832,10 +1583,10 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.Descendants(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, level, culture).OfType(); + where T : class, IPublishedContent + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1843,40 +1594,44 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - Descendants( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + where T : class, IPublishedContent + => content.Descendants(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => content.DescendantsOrSelf(navigationQueryService, publishedStatusFilteringService, true, null, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.DescendantsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, true, null, culture); + string? culture = null) + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - string? culture = null) => - DescendantsOrSelf( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + string? culture = null) + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int level, + string? culture = null) + => content.DescendantsOrSelf(navigationQueryService, publishedStatusFilteringService, true, p => p.Level >= level, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1884,60 +1639,62 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, int level, - string? culture = null) => - content.DescendantsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, true, p => p.Level >= level, culture); + string? culture = null) + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int level, - string? culture = null) => - DescendantsOrSelf( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + string? culture = null) + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); public static IEnumerable DescendantsOrSelfOfType( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, string contentTypeAlias, - string? culture = null) => - content.EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - publishedCache, + string? culture = null) + => content.EnumerateDescendantsOrSelfInternal( navigationQueryService, - publishStatusQueryService, + publishedStatusFilteringService, culture, true, contentTypeAlias); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IEnumerable DescendantsOrSelfOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + string contentTypeAlias, + string? culture = null) + => content.DescendantsOrSelfOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string contentTypeAlias, - string? culture = null) => - DescendantsOrSelfOfType( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); + string? culture = null) + => content.DescendantsOrSelfOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content.DescendantsOrSelf(navigationQueryService, publishedStatusFilteringService, culture).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1945,24 +1702,29 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.DescendantsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture).OfType(); + where T : class, IPublishedContent + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - content.DescendantsOrSelf( - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + where T : class, IPublishedContent + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static IEnumerable DescendantsOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int level, + string? culture = null) + where T : class, IPublishedContent + => content.DescendantsOrSelf(navigationQueryService, publishedStatusFilteringService, level, culture).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1971,10 +1733,10 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.DescendantsOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, level, culture).OfType(); + where T : class, IPublishedContent + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -1982,45 +1744,46 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - DescendantsOrSelf( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + where T : class, IPublishedContent + => content.DescendantsOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); + public static IPublishedContent? Descendant( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => content.Children(navigationQueryService, publishedStatusFilteringService, culture)?.FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.Children( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - culture)?.FirstOrDefault(); + string? culture = null) + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - string? culture = null) => - Descendant( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + string? culture = null) + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static IPublishedContent? Descendant( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int level, + string? culture = null) + => content + .EnumerateDescendants(navigationQueryService, publishedStatusFilteringService, false, culture) + .FirstOrDefault(x => x.Level == level); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2028,62 +1791,66 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, int level, - string? culture = null) => content - .EnumerateDescendants(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, false, culture).FirstOrDefault(x => x.Level == level); + string? culture = null) + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int level, - string? culture = null) => - content - .EnumerateDescendants( - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - false, - culture) - .FirstOrDefault(x => x.Level == level); + string? culture = null) + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); public static IPublishedContent? DescendantOfType( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, string contentTypeAlias, - string? culture = null) => content + string? culture = null) + => content .EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - publishedCache, navigationQueryService, - publishStatusQueryService, + publishedStatusFilteringService, culture, false, contentTypeAlias) .FirstOrDefault(); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IPublishedContent? DescendantOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + string contentTypeAlias, + string? culture = null) + => content.DescendantOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? DescendantOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string contentTypeAlias, - string? culture = null) => - DescendantOfType( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); + string? culture = null) + => content.DescendantOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + public static T? Descendant( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content + .EnumerateDescendants(navigationQueryService, publishedStatusFilteringService, false, culture) + .FirstOrDefault(x => x is T) as T; + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2091,25 +1858,29 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.EnumerateDescendants(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, false, culture).FirstOrDefault(x => x is T) as T; + where T : class, IPublishedContent + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - Descendant( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + where T : class, IPublishedContent + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static T? Descendant( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int level, + string? culture = null) + where T : class, IPublishedContent + => content.Descendant(navigationQueryService, publishedStatusFilteringService, level, culture) as T; + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2118,10 +1889,10 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.Descendant(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, level, culture) as T; + where T : class, IPublishedContent + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2129,42 +1900,48 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - Descendant( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + where T : class, IPublishedContent + => content.Descendant(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); public static IPublishedContent DescendantOrSelf( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.EnumerateDescendants( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => content.EnumerateDescendants( + navigationQueryService, + publishedStatusFilteringService, true, culture) .FirstOrDefault() ?? content; - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, - string? culture = null) => - DescendantOrSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + IPublishStatusQueryService publishStatusQueryService, + string? culture = null) + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IPublishedContent DescendantOrSelf( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + + public static IPublishedContent? DescendantOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int level, + string? culture = null) + => content + .EnumerateDescendants(navigationQueryService, publishedStatusFilteringService, true, culture) + .FirstOrDefault(x => x.Level == level); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2172,33 +1949,36 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, int level, - string? culture = null) => content - .EnumerateDescendants( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - true, - culture) - .FirstOrDefault(x => x.Level == level); + string? culture = null) + => content.DescendantOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, int level, - string? culture = null) => - DescendantOrSelf( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + string? culture = null) + => content.DescendantOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); + public static IPublishedContent? DescendantOrSelfOfType( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string contentTypeAlias, + string? culture = null) + => content + .EnumerateDescendantsOrSelfInternal( + navigationQueryService, + publishedStatusFilteringService, + culture, + true, + contentTypeAlias) + .FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2206,34 +1986,30 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias, - string? culture = null) => content - .EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - culture, - true, - contentTypeAlias) - .FirstOrDefault(); + string? culture = null) + => content.DescendantOrSelfOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string contentTypeAlias, - string? culture = null) => - DescendantOrSelfOfType( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); + string? culture = null) + => content.DescendantOrSelfOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + public static T? DescendantOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content + .EnumerateDescendants(navigationQueryService, publishedStatusFilteringService, true, culture) + .FirstOrDefault(x => x is T) as T; + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2241,25 +2017,29 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.EnumerateDescendants(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, true, culture).FirstOrDefault(x => x is T) as T; + where T : class, IPublishedContent + => content.DescendantOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - DescendantOrSelf( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + where T : class, IPublishedContent + => content.DescendantOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static T? DescendantOrSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + int level, + string? culture = null) + where T : class, IPublishedContent + => content.DescendantOrSelf(navigationQueryService, publishedStatusFilteringService, level, culture) as T; + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2268,10 +2048,10 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.DescendantOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, level, culture) as T; + where T : class, IPublishedContent + => content.DescendantOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQuery instead, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2279,44 +2059,32 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - DescendantOrSelf( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + where T : class, IPublishedContent + => content.DescendantOrSelf(navigationQueryService, GetPublishedStatusFilteringService(content), level, culture); - internal static IEnumerable DescendantsOrSelf( + private static IEnumerable DescendantsOrSelf( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, bool orSelf, Func? func, - string? culture = null) => - content.EnumerateDescendants(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, orSelf, culture) - .Where(x => func == null || func(x)); + string? culture = null) + => content + .EnumerateDescendants(navigationQueryService, publishedStatusFilteringService, orSelf, culture) + .Where(x => func == null || func(x)); - internal static IEnumerable EnumerateDescendants( + private static IEnumerable EnumerateDescendants( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, bool orSelf, string? culture = null) { ArgumentNullException.ThrowIfNull(content); foreach (IPublishedContent desc in content.EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - publishedCache, navigationQueryService, - publishStatusQueryService, + publishedStatusFilteringService, culture, orSelf)) { @@ -2324,28 +2092,6 @@ public static class PublishedContentExtensions } } - internal static IEnumerable EnumerateDescendants( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, - string? culture = null) - { - yield return content; - - foreach (IPublishedContent desc in content.EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - publishedCache, - navigationQueryService, - publishStatusQueryService, - culture, - false)) - { - yield return desc; - } - } - #endregion #region Axes: children @@ -2354,9 +2100,8 @@ public static class PublishedContentExtensions /// 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 /// @@ -2379,6 +2124,14 @@ public static class PublishedContentExtensions /// However, if an empty string is specified only invariant children are returned. /// /// + public static IEnumerable Children( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => GetChildren(navigationQueryService, publishedStatusFilteringService, content.Key, null, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, @@ -2386,59 +2139,23 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - { - IEnumerable children = GetChildren(navigationQueryService, publishedCache, content.Key, publishStatusQueryService, variationContextAccessor, null, culture); + => content.Children(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - return children.FilterByCulture(culture, variationContextAccessor); - } - - /// - /// 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. - /// - /// - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - { - IPublishStatusQueryService publishStatusQueryService = StaticServiceProvider.Instance.GetRequiredService(); - return Children(content, variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture); - } + => content.Children(navigationQueryService, GetPublishedStatusFilteringService(content), culture); /// /// Gets the children of the content, filtered by a predicate. /// /// The content. - /// The accessor for VariationContext /// - /// + /// /// The predicate. /// /// The specific culture to filter for. If null is used the current culture is used. (Default is @@ -2449,6 +2166,15 @@ public static class PublishedContentExtensions /// /// Children are sorted by their sortOrder. /// + public static IEnumerable Children( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + Func predicate, + string? culture = null) + => content.Children(navigationQueryService, publishedStatusFilteringService, culture).Where(predicate); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2456,90 +2182,61 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, Func predicate, - string? culture = null) => - content.Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture).Where(predicate); + string? culture = null) + => content.Children(navigationQueryService, GetPublishedStatusFilteringService(content), predicate, culture); - /// - /// Gets the children of the content, filtered by a predicate. - /// - /// The content. - /// The accessor for VariationContext - /// - /// 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. - /// - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, Func predicate, - string? culture = null) => - content.Children(variationContextAccessor, publishedCache, navigationQueryService, culture).Where(predicate); + string? culture = null) + => content.Children(navigationQueryService, GetPublishedStatusFilteringService(content), predicate, culture); /// /// Gets the children of the content, of any of the specified types. /// /// The content. - /// /// - /// The accessor for the VariationContext + /// + /// The content type alias. /// /// 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. - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] - public static IEnumerable ChildrenOfType( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService, - string? contentTypeAlias, - string? culture = null) - { - IPublishStatusQueryService publishStatusQueryService = StaticServiceProvider.Instance.GetRequiredService(); - return ChildrenOfType(content, variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, contentTypeAlias, culture); - } - - /// - /// Gets the children of the content, of any of the specified types. - /// - /// The content. - /// - /// - /// - /// The accessor for the VariationContext - /// - /// 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, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, string? contentTypeAlias, string? culture = null) - { - IEnumerable children = contentTypeAlias is not null - ? GetChildren(navigationQueryService, publishedCache, content.Key, publishStatusQueryService, variationContextAccessor, contentTypeAlias, culture) + => contentTypeAlias is not null + ? GetChildren(navigationQueryService, publishedStatusFilteringService, content.Key, contentTypeAlias, culture) : []; - return children.FilterByCulture(culture, variationContextAccessor); - } + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IEnumerable ChildrenOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + string? contentTypeAlias, + string? culture = null) + => content.ChildrenOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] + public static IEnumerable ChildrenOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + string? contentTypeAlias, + string? culture = null) + => content.ChildrenOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); /// /// Gets the children of the content, of a given content type. @@ -2558,6 +2255,15 @@ public static class PublishedContentExtensions /// /// Children are sorted by their sortOrder. /// + public static IEnumerable Children( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content.Children(navigationQueryService, publishedStatusFilteringService, culture).OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2565,56 +2271,61 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture).OfType(); + where T : class, IPublishedContent + => content.Children(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - /// - /// Gets the children of the content, of a given content type. - /// - /// The content type. - /// The content. - /// The accessor for the VariationContext - /// - /// - /// 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. - /// - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Children(variationContextAccessor, publishedCache, navigationQueryService, culture).OfType(); + where T : class, IPublishedContent + => content.Children(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static IPublishedContent? FirstChild( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => content + .Children(navigationQueryService, publishedStatusFilteringService, culture) + .FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture)?.FirstOrDefault(); + string? culture = null) + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - string? culture = null) => - FirstChild(content, variationContextAccessor, publishedCache, navigationQueryService, StaticServiceProvider.Instance.GetRequiredService(), culture); + string? culture = null) + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), culture); /// /// Gets the first child of the content, of a given content type. /// + public static IPublishedContent? FirstChildOfType( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string contentTypeAlias, + string? culture = null) + => content + .ChildrenOfType(navigationQueryService, publishedStatusFilteringService, contentTypeAlias, culture) + .FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChildOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2622,30 +2333,30 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias, - string? culture = null) => content - .ChildrenOfType(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, contentTypeAlias, culture) - .FirstOrDefault(); + string? culture = null) + => content.FirstChildOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - /// - /// Gets the first child of the content, of a given content type. - /// - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChildOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string contentTypeAlias, - string? culture = null) => - FirstChildOfType( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); + string? culture = null) + => content.FirstChildOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + public static IPublishedContent? FirstChild( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + Func predicate, + string? culture = null) + => content + .Children(navigationQueryService, publishedStatusFilteringService, predicate, culture) + .FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2654,25 +2365,29 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, Func predicate, string? culture = null) - => content.Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, predicate, culture)?.FirstOrDefault(); + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), predicate, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, Func predicate, - string? culture = null) => - FirstChild( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - predicate, - culture); + string? culture = null) + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), predicate, culture); + public static IPublishedContent? FirstChild( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + Guid uniqueId, + string? culture = null) + => content + .Children(navigationQueryService, publishedStatusFilteringService, x => x.Key == uniqueId, culture) + .FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2680,26 +2395,30 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, Guid uniqueId, - string? culture = null) => content - .Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, x => x.Key == uniqueId, culture)?.FirstOrDefault(); + string? culture = null) + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), uniqueId, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, Guid uniqueId, - string? culture = null) => - FirstChild( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - uniqueId, - culture); + string? culture = null) + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), uniqueId, culture); + public static T? FirstChild( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content + .Children(navigationQueryService, publishedStatusFilteringService, culture) + .FirstOrDefault(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2707,19 +2426,31 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture)?.FirstOrDefault(); + where T : class, IPublishedContent + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - FirstChild(content, variationContextAccessor, publishedCache, navigationQueryService, StaticServiceProvider.Instance.GetRequiredService(), culture); + where T : class, IPublishedContent + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + public static T? FirstChild( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + Func predicate, + string? culture = null) + where T : class, IPublishedContent + => content + .Children(navigationQueryService, publishedStatusFilteringService, culture) + .FirstOrDefault(predicate); + + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2728,10 +2459,10 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, Func predicate, string? culture = null) - where T : class, IPublishedContent => - content.Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture)?.FirstOrDefault(predicate); + where T : class, IPublishedContent + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), predicate, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService instead, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2739,15 +2470,8 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, Func predicate, string? culture = null) - where T : class, IPublishedContent => - FirstChild( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - predicate, - culture); + where T : class, IPublishedContent + => content.FirstChild(navigationQueryService, GetPublishedStatusFilteringService(content), predicate, culture); #endregion @@ -2758,127 +2482,96 @@ public static class PublishedContentExtensions /// /// The content. /// The navigation service - /// Variation context accessor. - /// + /// /// /// The specific culture to filter for. If null is used the current culture is used. (Default is /// null) /// - /// The content cache instance. /// The siblings of the content. /// /// Note that in V7 this method also return the content node self. /// public static IEnumerable Siblings( this IPublishedContent content, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IVariationContextAccessor variationContextAccessor, - IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - SiblingsAndSelf(content, publishedCache, navigationQueryService, variationContextAccessor, publishStatusQueryService, culture) - ?.Where(x => x.Id != content.Id) ?? Enumerable.Empty(); - - /// - /// Gets the siblings of the content. - /// - /// The content. - /// The navigation service - /// Variation context accessor. - /// - /// The specific culture to filter for. If null is used the current culture is used. (Default is - /// null) - /// - /// The content cache instance. - /// The siblings of the content. - /// - /// Note that in V7 this method also return the content node self. - /// - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] - public static IEnumerable Siblings( - this IPublishedContent content, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService, - IVariationContextAccessor variationContextAccessor, - string? culture = null) => - Siblings( - content, - publishedCache, - navigationQueryService, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); - - /// - /// Gets the siblings of the content, of a given content type. - /// - /// The content. - /// Variation context accessor. - /// - /// 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, of the given content type. - /// - /// Note that in V7 this method also return the content node self. - /// - public static IEnumerable SiblingsOfType( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, - string contentTypeAlias, - string? culture = null) => - SiblingsAndSelfOfType(content, variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, contentTypeAlias, culture) + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => content + .SiblingsAndSelf(navigationQueryService, publishedStatusFilteringService, culture) .Where(x => x.Id != content.Id); + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable Siblings( + this IPublishedContent content, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IVariationContextAccessor variationContextAccessor, + IPublishStatusQueryService publishStatusQueryService, + string? culture = null) + => content.Siblings(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable Siblings( + this IPublishedContent content, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + => content.Siblings(navigationQueryService, GetPublishedStatusFilteringService(content), culture); + /// /// Gets the siblings of the content, of a given content type. /// /// The content. - /// Variation context accessor. + /// + /// /// /// 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, of the given content type. /// /// Note that in V7 this method also return the content node self. /// - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + public static IEnumerable SiblingsOfType( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string contentTypeAlias, + string? culture = null) + => content + .SiblingsAndSelfOfType(navigationQueryService, publishedStatusFilteringService, contentTypeAlias, culture) + .Where(x => x.Id != content.Id); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable SiblingsOfType( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + string contentTypeAlias, + string? culture = null) + => content.SiblingsOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string contentTypeAlias, - string? culture = null) => - SiblingsOfType( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); + string? culture = null) + => content.SiblingsOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); /// /// Gets the siblings of the content, of a given content type. /// /// The content type. /// The content. - /// Variation context accessor. - /// /// - /// + /// /// /// The specific culture to filter for. If null is used the current culture is used. (Default is /// null) @@ -2887,6 +2580,17 @@ public static class PublishedContentExtensions /// /// Note that in V7 this method also return the content node self. /// + public static IEnumerable Siblings( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content + .SiblingsAndSelf(navigationQueryService, publishedStatusFilteringService, culture) + .Where(x => x.Id != content.Id); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Siblings( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -2894,126 +2598,77 @@ public static class PublishedContentExtensions INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - SiblingsAndSelf(content, variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, culture) - ?.Where(x => x.Id != content.Id) ?? Enumerable.Empty(); + where T : class, IPublishedContent + => content.Siblings(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - /// - /// Gets the siblings of the content, of a given content type. - /// - /// The content type. - /// The content. - /// Variation context accessor. - /// - /// - /// - /// 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. - /// - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Siblings( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => - Siblings( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + where T : class, IPublishedContent + => content.Siblings(navigationQueryService, GetPublishedStatusFilteringService(content), culture); /// /// Gets the siblings of the content including the node itself to indicate the position. /// /// The content. - /// Cache instance. /// The navigation service. - /// Variation context accessor. - /// + /// /// /// 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( + public static IEnumerable SiblingsAndSelf( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + => content.SiblingsAndSelfInternal(navigationQueryService, publishedStatusFilteringService, null, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable SiblingsAndSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - { - var success = navigationQueryService.TryGetParentKey(content.Key, out Guid? parentKey); + => content.SiblingsAndSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - if (success is false || parentKey is null) - { - if (navigationQueryService.TryGetRootKeys(out IEnumerable childrenKeys) is false) - { - return null; - } - - culture ??= variationContextAccessor.VariationContext?.Culture ?? string.Empty; - return childrenKeys - .Where(x => publishStatusQueryService.IsDocumentPublished(x , culture)) - .Select(publishedCache.GetById) - .WhereNotNull() - .WhereIsInvariantOrHasCulture(variationContextAccessor, culture); - } - - return navigationQueryService.TryGetChildrenKeys(parentKey.Value, out IEnumerable siblingKeys) is false - ? null - : siblingKeys.Select(publishedCache.GetById).WhereNotNull(); - } - - /// - /// Gets the siblings of the content including the node itself to indicate the position. - /// - /// The content. - /// Cache instance. - /// The navigation service. - /// Variation context accessor. - /// - /// 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. - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] - public static IEnumerable? SiblingsAndSelf( + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable SiblingsAndSelf( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IVariationContextAccessor variationContextAccessor, - string? culture = null) => - SiblingsAndSelf( - content, - publishedCache, - navigationQueryService, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + string? culture = null) + => content.SiblingsAndSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); /// /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. /// /// The content. - /// Variation context accessor. + /// + /// /// /// 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, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string contentTypeAlias, + string? culture = null) + => content.SiblingsAndSelfInternal(navigationQueryService, publishedStatusFilteringService, contentTypeAlias, culture); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsAndSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -3022,75 +2677,41 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias, string? culture = null) - { - var parentExists = navigationQueryService.TryGetParentKey(content.Key, out Guid? parentKey); + => content.SiblingsAndSelfOfType(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - IPublishedContent? parent = parentKey is null - ? null - : publishedCache.GetById(parentKey.Value); - - if (parentExists && parent is not null) - { - return parent.ChildrenOfType(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, contentTypeAlias, culture); - } - - if (navigationQueryService.TryGetRootKeysOfType(contentTypeAlias, out IEnumerable rootKeysOfType) is false) - { - return []; - } - - culture ??= variationContextAccessor.VariationContext?.Culture ?? string.Empty; - return rootKeysOfType - .Where(x => publishStatusQueryService.IsDocumentPublished(x, culture)) - .Select(publishedCache.GetById) - .WhereNotNull() - .WhereIsInvariantOrHasCulture(variationContextAccessor, culture); - } - - /// - /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. - /// - /// The content. - /// Variation context accessor. - /// - /// 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. - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsAndSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string contentTypeAlias, - string? culture = null) => - SiblingsAndSelfOfType( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); + string? culture = null) + => content.SiblingsAndSelfOfType(navigationQueryService, GetPublishedStatusFilteringService(content), 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. - /// Variation context accessor. /// - /// + /// /// /// 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, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => content + .SiblingsAndSelfInternal(navigationQueryService, publishedStatusFilteringService, null, culture) + .OfType(); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsAndSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -3099,57 +2720,17 @@ public static class PublishedContentExtensions IPublishStatusQueryService publishStatusQueryService, string? culture = null) where T : class, IPublishedContent - { - var parentSuccess = navigationQueryService.TryGetParentKey(content.Key, out Guid? parentKey); - IPublishedContent? parent = parentKey is null ? null : publishedCache.GetById(parentKey.Value); + => content.SiblingsAndSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); - if (parentSuccess is false || parent is null) - { - var rootSuccess = navigationQueryService.TryGetRootKeys(out IEnumerable rootKeys); - if (rootSuccess is false) - { - return []; - } - - culture ??= variationContextAccessor.VariationContext?.Culture ?? string.Empty; - return rootKeys - .Where(x => publishStatusQueryService.IsDocumentPublished(x, culture)) - .Select(publishedCache.GetById) - .WhereNotNull() - .WhereIsInvariantOrHasCulture(variationContextAccessor, culture) - .OfType(); - } - - return parent.Children(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, 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. - /// Variation context accessor. - /// - /// - /// 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. - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsAndSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, string? culture = null) - where T : class, IPublishedContent => SiblingsAndSelf( - content, - variationContextAccessor, - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + where T : class, IPublishedContent + => content.SiblingsAndSelf(navigationQueryService, GetPublishedStatusFilteringService(content), culture); #endregion @@ -3159,10 +2740,8 @@ public static class PublishedContentExtensions /// Gets the root content (ancestor or self at level 1) for the specified . /// /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// /// The root content (ancestor or self at level 1) for the specified . /// @@ -3171,37 +2750,27 @@ public static class PublishedContentExtensions /// with maxLevel /// set to 1. /// + public static IPublishedContent Root( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + => content.AncestorOrSelf(navigationQueryService, publishedStatusFilteringService, 1); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent Root( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService) => content.AncestorOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, 1); + IPublishStatusQueryService publishStatusQueryService) + => content.Root(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the root content (ancestor or self at level 1) for the specified . - /// - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// - /// The root content (ancestor or self at level 1) for the specified . - /// - /// - /// This is the same as calling - /// with maxLevel - /// set to 1. - /// - [Obsolete("Use the overload with IVariationContextAccessor & IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent Root( this IPublishedContent content, IPublishedCache publishedCache, - INavigationQueryService navigationQueryService) => Root( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + INavigationQueryService navigationQueryService) + => content.Root(navigationQueryService, GetPublishedStatusFilteringService(content)); /// /// Gets the root content (ancestor or self at level 1) for the specified if it's of the @@ -3209,10 +2778,8 @@ public static class PublishedContentExtensions /// /// The content type. /// The content. - /// - /// The content cache. /// The query service for the in-memory navigation structure. - /// + /// /// /// The root content (ancestor or self at level 1) for the specified of content type /// . @@ -3222,44 +2789,30 @@ public static class PublishedContentExtensions /// with /// maxLevel set to 1. /// + public static T? Root( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) + where T : class, IPublishedContent + => content.AncestorOrSelf(navigationQueryService, publishedStatusFilteringService, 1); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Root( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService) - where T : class, IPublishedContent => - content.AncestorOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, 1); + where T : class, IPublishedContent + => content.Root(navigationQueryService, GetPublishedStatusFilteringService(content)); - /// - /// Gets the root content (ancestor or self at level 1) for the specified if it's of the - /// specified content type . - /// - /// The content type. - /// The content. - /// The content cache. - /// The query service for the in-memory navigation structure. - /// - /// The root content (ancestor or self at level 1) for the specified of content type - /// . - /// - /// - /// This is the same as calling - /// with - /// maxLevel set to 1. - /// - [Obsolete("Use the overload with IVariationContextAccessor & PublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Root( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) - where T : class, IPublishedContent => - Root( - content, - StaticServiceProvider.Instance.GetRequiredService(), - publishedCache, - navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()); + where T : class, IPublishedContent + => content.Root(navigationQueryService, GetPublishedStatusFilteringService(content)); #endregion @@ -3285,8 +2838,8 @@ public static class PublishedContentExtensions /// 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. @@ -3296,9 +2849,21 @@ public static class PublishedContentExtensions /// The specific culture to filter for. If null is used the current culture is used. (Default is /// null) /// - /// /// The children of the content. [Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")] + public static DataTable ChildrenAsTable( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, + IPublishedUrlProvider publishedUrlProvider, + string contentTypeAliasFilter = "", + string? culture = null) + => content.GenerateDataTable(navigationQueryService, publishedStatusFilteringService, contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture); + + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static DataTable ChildrenAsTable( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, @@ -3310,30 +2875,12 @@ public static class PublishedContentExtensions IPublishedUrlProvider publishedUrlProvider, string contentTypeAliasFilter = "", string? culture = null) - => GenerateDataTable(content, variationContextAccessor, publishedCache, navigationQueryService, contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture); + => content.GenerateDataTable(navigationQueryService, GetPublishedStatusFilteringService(content), contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture); - /// - /// 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. private static DataTable GenerateDataTable( - IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, + this IPublishedContent content, INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, @@ -3341,12 +2888,12 @@ public static class PublishedContentExtensions string contentTypeAliasFilter = "", string? culture = null) { + IPublishedContent[] children = content.Children(navigationQueryService, publishedStatusFilteringService, culture).ToArray(); IPublishedContent? firstNode = contentTypeAliasFilter.IsNullOrWhiteSpace() - ? content.Children(variationContextAccessor, publishedCache, navigationQueryService, culture)?.Any() ?? false - ? content.Children(variationContextAccessor, publishedCache, navigationQueryService, culture)?.ElementAt(0) + ? children.Length > 0 + ? children.ElementAt(0) : null - : content.Children(variationContextAccessor, publishedCache, navigationQueryService, culture) - ?.FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAliasFilter)); + : children.FirstOrDefault(x => x.ContentType.Alias.InvariantEquals(contentTypeAliasFilter)); if (firstNode == null) { // No children found @@ -3368,7 +2915,7 @@ public static class PublishedContentExtensions List>, IEnumerable>>> tableData = DataTableExtensions.CreateTableData(); IOrderedEnumerable? children = - content.Children(variationContextAccessor, publishedCache, navigationQueryService)?.OrderBy(x => x.SortOrder); + content.Children(navigationQueryService, publishedStatusFilteringService).OrderBy(x => x.SortOrder); if (children is not null) { // loop through each child and create row data for it @@ -3385,7 +2932,7 @@ public static class PublishedContentExtensions var standardVals = new Dictionary { { "Id", n.Id }, - { "NodeName", n.Name(variationContextAccessor) }, + { "NodeName", n.Name(null, culture) }, { "NodeTypeAlias", n.ContentType.Alias }, { "CreateDate", n.CreateDate }, { "UpdateDate", n.UpdateDate }, @@ -3465,181 +3012,135 @@ public static class PublishedContentExtensions #endregion - public static IPublishedContent? Ancestor(this IPublishedContent content, int maxLevel) - { - return content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } - + => content.Ancestor(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); public static IPublishedContent? Ancestor(this IPublishedContent content, string contentTypeAlias) - { - return content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); - } - + => content.Ancestor(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias); public static T? Ancestor(this IPublishedContent content, int maxLevel) where T : class, IPublishedContent - { - return Ancestor(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } - + => content.Ancestor(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); public static IEnumerable Ancestors(this IPublishedContent content, int maxLevel) - { - return content.Ancestors(GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } - + => content.Ancestors(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); public static IEnumerable Ancestors(this IPublishedContent content, string contentTypeAlias) - { - return content.Ancestors(GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); - } - + => content.Ancestors(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias); public static IEnumerable Ancestors(this IPublishedContent content) where T : class, IPublishedContent - { - return Ancestors(content, GetPublishedCache(content), GetNavigationQueryService(content)); - } - + => content.Ancestors(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); public static IEnumerable Ancestors(this IPublishedContent content, int maxLevel) where T : class, IPublishedContent - { - return Ancestors(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } + => content.Ancestors(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); public static IPublishedContent AncestorOrSelf(this IPublishedContent content, int maxLevel) - { - return AncestorOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } + => content.AncestorOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); public static IPublishedContent AncestorOrSelf(this IPublishedContent content, string contentTypeAlias) - { - return AncestorOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); - } + => content.AncestorOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias); public static T? AncestorOrSelf(this IPublishedContent content, int maxLevel) where T : class, IPublishedContent - { - return AncestorOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } + => content.AncestorOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int maxLevel) - { - return content.AncestorsOrSelf(GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } + => content.AncestorsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); public static IEnumerable AncestorsOrSelf(this IPublishedContent content, string contentTypeAlias) - { - return content.Ancestors(GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias); - } + => content.Ancestors(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias); public static IEnumerable AncestorsOrSelf(this IPublishedContent content, int maxLevel) where T : class, IPublishedContent - { - return AncestorsOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), maxLevel); - } + => content.AncestorsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), maxLevel); - public static IEnumerable AncestorsOrSelf(this IPublishedContent content, bool orSelf, - Func? func) - { - return AncestorsOrSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), orSelf, func); - } + public static IEnumerable AncestorsOrSelf(this IPublishedContent content, bool orSelf, Func? func) + => content.AncestorsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), orSelf, func); public static IEnumerable Breadcrumbs( this IPublishedContent content, bool andSelf = true) => - content.Breadcrumbs(GetPublishedCache(content), GetNavigationQueryService(content), andSelf); + content.Breadcrumbs(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), andSelf); public static IEnumerable Breadcrumbs( this IPublishedContent content, int minLevel, bool andSelf = true) => - content.Breadcrumbs(GetPublishedCache(content), GetNavigationQueryService(content), minLevel, andSelf); + content.Breadcrumbs(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), minLevel, andSelf); public static IEnumerable Breadcrumbs( this IPublishedContent content, bool andSelf = true) where T : class, IPublishedContent=> - content.Breadcrumbs(GetPublishedCache(content), GetNavigationQueryService(content), andSelf); + content.Breadcrumbs(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), andSelf); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - => Children(content, variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, culture); + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor? variationContextAccessor, string? culture = null) - => Children(content, variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), StaticServiceProvider.Instance.GetRequiredService(), culture); + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, Func predicate, - string? culture = null) => - content.Children(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, culture).Where(predicate); + string? culture = null) + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, - string? culture = null) => - Children( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - predicate, - culture); + string? culture = null) + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable ChildrenOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? contentTypeAlias, string? culture = null) - { - IEnumerable children = contentTypeAlias is not null - ? GetChildren(GetNavigationQueryService(content), GetPublishedCache(content), content.Key, publishStatusQueryService, variationContextAccessor, contentTypeAlias, culture) - : []; + => content.ChildrenOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - return children.FilterByCulture(culture, variationContextAccessor); - } - - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable ChildrenOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? contentTypeAlias, string? culture = null) - { - IPublishStatusQueryService publishStatusQueryService = StaticServiceProvider.Instance.GetRequiredService(); - return ChildrenOfType(content, variationContextAccessor, publishStatusQueryService, contentTypeAlias, culture); - } + => content.ChildrenOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Children(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, culture).OfType(); + where T : class, IPublishedContent + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Children( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - Children(content, variationContextAccessor, StaticServiceProvider.Instance.GetRequiredService(), culture); + where T : class, IPublishedContent + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); [Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")] public static DataTable ChildrenAsTable( @@ -3651,760 +3152,576 @@ public static class PublishedContentExtensions IPublishedUrlProvider publishedUrlProvider, string contentTypeAliasFilter = "", string? culture = null) - => GenerateDataTable(content, variationContextAccessor, GetPublishedCache(content), - GetNavigationQueryService(content), contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture); + => content.GenerateDataTable(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeService, mediaTypeService, memberTypeService, publishedUrlProvider, contentTypeAliasFilter, culture); public static IEnumerable DescendantsOrSelfOfType( this IEnumerable parentNodes, - IVariationContextAccessor variationContextAccessor, - IPublishStatusQueryService publishStatusQueryService, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, string docTypeAlias, - string? culture = null) => - parentNodes.SelectMany(x => x.DescendantsOrSelfOfType( - variationContextAccessor, - GetPublishedCache(parentNodes.First()), - GetNavigationQueryService(parentNodes.First()), - publishStatusQueryService, + string? culture = null) + => parentNodes.SelectMany(x => x.DescendantsOrSelfOfType( + navigationQueryService, + publishedStatusFilteringService, docTypeAlias, culture)); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable DescendantsOrSelfOfType( + this IEnumerable parentNodes, + IVariationContextAccessor variationContextAccessor, + IPublishStatusQueryService publishStatusQueryService, + string docTypeAlias, + string? culture = null) + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelfOfType( + GetNavigationQueryService(parentNodesAsArray.First()), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), + docTypeAlias, + culture); + } + + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelfOfType( this IEnumerable parentNodes, IVariationContextAccessor variationContextAccessor, string docTypeAlias, - string? culture = null) => - DescendantsOrSelfOfType( - parentNodes, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), + string? culture = null) + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelfOfType( + GetNavigationQueryService(parentNodesAsArray.First()), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), docTypeAlias, culture); + } + public static IEnumerable DescendantsOrSelf( + this IEnumerable parentNodes, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? culture = null) + where T : class, IPublishedContent + => parentNodes.SelectMany(x => x.DescendantsOrSelf( + navigationQueryService, + publishedStatusFilteringService, + culture)); + + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IEnumerable parentNodes, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - parentNodes.SelectMany( - x => x.DescendantsOrSelf( - variationContextAccessor, - GetPublishedCache(parentNodes.First()), - GetNavigationQueryService(parentNodes.First()), - publishStatusQueryService, - culture)); + where T : class, IPublishedContent + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelf( + GetNavigationQueryService(parentNodesAsArray.First()), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), + culture); + } - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IEnumerable parentNodes, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - DescendantsOrSelf(parentNodes, variationContextAccessor, StaticServiceProvider.Instance.GetRequiredService(), culture); - - public static IEnumerable Descendants( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, false, null, culture); - - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] - public static IEnumerable Descendants( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - string? culture = null) => - Descendants(content, variationContextAccessor, StaticServiceProvider.Instance.GetRequiredService(), culture); - - - public static IEnumerable Descendants( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishStatusQueryService publishStatusQueryService, - int level, - string? culture = null) => - content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, false, p => p.Level >= level, culture); - - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] - public static IEnumerable Descendants( - this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - int level, - string? culture = null) => - Descendants( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - level, + where T : class, IPublishedContent + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + return parentNodesAsArray.DescendantsOrSelf( + GetNavigationQueryService(parentNodesAsArray.First()), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), culture); + } + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishStatusQueryService publishStatusQueryService, + string? culture = null) + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + string? culture = null) + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishStatusQueryService publishStatusQueryService, + int level, + string? culture = null) + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] + public static IEnumerable Descendants( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + int level, + string? culture = null) + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, - string contentTypeAlias, string? culture = null) => - content.EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture, - false, - contentTypeAlias); + string contentTypeAlias, + string? culture = null) + => content.DescendantsOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, - string? culture = null) => - DescendantsOfType( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); - + string? culture = null) + => content.DescendantsOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Descendants(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, culture).OfType(); + where T : class, IPublishedContent + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - Descendants(content, variationContextAccessor, StaticServiceProvider.Instance.GetRequiredService(), culture); - + where T : class, IPublishedContent + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.Descendants(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, level, culture).OfType(); + where T : class, IPublishedContent + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Descendants( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) - where T : class, IPublishedContent => - Descendants( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); - + where T : class, IPublishedContent + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.DescendantsOrSelf(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, true, null, culture); + string? culture = null) + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, - string? culture = null) => - DescendantsOrSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); - + string? culture = null) + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, int level, - string? culture = null) => - content.DescendantsOrSelf( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - true, - p => p.Level >= level, - culture); + string? culture = null) + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, - string? culture = null) => - DescendantsOrSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); + string? culture = null) + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias, - string? culture = null) => - content.EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture, - true, - contentTypeAlias); + string? culture = null) + => content.DescendantsOrSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, - string? culture = null) => - DescendantsOrSelfOfType( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); - + string? culture = null) + => content.DescendantsOrSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.DescendantsOrSelf( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture).OfType(); + where T : class, IPublishedContent + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - DescendantsOrSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); - + where T : class, IPublishedContent + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.DescendantsOrSelf( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - level, - culture).OfType(); + where T : class, IPublishedContent + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable DescendantsOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) - where T : class, IPublishedContent => - DescendantsOrSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); - + where T : class, IPublishedContent + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.Children(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, culture)?.FirstOrDefault(); + string? culture = null) + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, - string? culture = null) => - Descendant( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); - + string? culture = null) + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, int level, - string? culture = null) => content - .EnumerateDescendants( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - false, - culture).FirstOrDefault(x => x.Level == level); + string? culture = null) + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, - string? culture = null) => - Descendant( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); - + string? culture = null) + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? DescendantOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias, - string? culture = null) => content - .EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture, - false, - contentTypeAlias) - .FirstOrDefault(); + string? culture = null) + => content.DescendantOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? DescendantOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, - string? culture = null) => - DescendantOfType( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); - + string? culture = null) + => content.DescendantOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.EnumerateDescendants( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - false, - culture) - .FirstOrDefault(x => x is T) as T; + where T : class, IPublishedContent + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - Descendant( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); - + where T : class, IPublishedContent + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.Descendant( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - level, - culture) as T; + where T : class, IPublishedContent + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? Descendant( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) - where T : class, IPublishedContent => - Descendant( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); - + where T : class, IPublishedContent + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, int level, - string? culture = null) => content - .EnumerateDescendants( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - true, - culture).FirstOrDefault(x => x.Level == level); + string? culture = null) + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, - string? culture = null) => DescendantOrSelf(content, variationContextAccessor, StaticServiceProvider.Instance.GetRequiredService(), level, culture); - + string? culture = null) + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias, - string? culture = null) => content - .EnumerateDescendantsOrSelfInternal( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture, - true, - contentTypeAlias) - .FirstOrDefault(); + string? culture = null) + => content.DescendantOrSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? DescendantOrSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, - string? culture = null) => - DescendantOrSelfOfType( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); - + string? culture = null) + => content.DescendantOrSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.EnumerateDescendants( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - true, - culture).FirstOrDefault(x => x is T) as T; + where T : class, IPublishedContent + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - DescendantOrSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); + where T : class, IPublishedContent + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, int level, string? culture = null) - where T : class, IPublishedContent => - content.DescendantOrSelf(variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), publishStatusQueryService, level, culture) as T; + where T : class, IPublishedContent + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? DescendantOrSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, int level, string? culture = null) - where T : class, IPublishedContent => - DescendantOrSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - level, - culture); - - + where T : class, IPublishedContent + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, - string? culture = null) => - content.Children( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture)?.FirstOrDefault(); + string? culture = null) + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, - string? culture = null) => - FirstChild(content, variationContextAccessor, StaticServiceProvider.Instance.GetRequiredService(), culture); - + string? culture = null) + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChildOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string contentTypeAlias, - string? culture = null) => - content.ChildrenOfType( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - contentTypeAlias, - culture)?.FirstOrDefault(); + string? culture = null) + => content.FirstChildOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChildOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, - string? culture = null) => - FirstChildOfType( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - contentTypeAlias, - culture); - + string? culture = null) + => content.FirstChildOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, Func predicate, - string? culture = null) => - content.Children( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - predicate, - culture)?.FirstOrDefault(); + string? culture = null) + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string? culture = null) - => content.Children(variationContextAccessor, GetPublishedCache(content), - GetNavigationQueryService(content), predicate, culture)?.FirstOrDefault(); - + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, Guid uniqueId, - string? culture = null) => content - .Children( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - x => x.Key == uniqueId, - culture)?.FirstOrDefault(); + string? culture = null) + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), uniqueId, culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IPublishedContent? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Guid uniqueId, - string? culture = null) => - FirstChild( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - uniqueId, - culture); - + string? culture = null) + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), uniqueId, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - content.Children( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture)?.FirstOrDefault(); + where T : class, IPublishedContent + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - FirstChild( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); - + where T : class, IPublishedContent + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, Func predicate, string? culture = null) - where T : class, IPublishedContent => - content.Children( - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture)?.FirstOrDefault(predicate); + where T : class, IPublishedContent + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static T? FirstChild( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, Func predicate, string? culture = null) - where T : class, IPublishedContent => - FirstChild( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - predicate, - culture); + where T : class, IPublishedContent + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); - [Obsolete( - "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Siblings( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, - string? culture = null) => - Siblings(content, GetPublishedCache(content), GetNavigationQueryService(content), variationContextAccessor, culture); + string? culture = null) + => content.Siblings(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete( - "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, - string? culture = null) => - SiblingsOfType(content, variationContextAccessor, - GetPublishedCache(content), GetNavigationQueryService(content), contentTypeAlias, culture); + string? culture = null) + => content.SiblingsOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); - [Obsolete( - "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable Siblings( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => - Siblings(content, variationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), culture); + where T : class, IPublishedContent + => content.Siblings(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete( - "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable? SiblingsAndSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, - string? culture = null) => SiblingsAndSelf(content, GetPublishedCache(content), GetNavigationQueryService(content), variationContextAccessor, culture); + string? culture = null) + => content.SiblingsAndSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete( - "Please use IPublishedCache and IDocumentNavigationQueryService or IMediaNavigationQueryService directly. This will be removed in a future version of Umbraco")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsAndSelfOfType( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string contentTypeAlias, - string? culture = null) => SiblingsAndSelfOfType(content, variationContextAccessor, GetPublishedCache(content), - GetNavigationQueryService(content), contentTypeAlias, culture); - + string? culture = null) + => content.SiblingsAndSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsAndSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, IPublishStatusQueryService publishStatusQueryService, string? culture = null) - where T : class, IPublishedContent => - SiblingsAndSelf( - content, - variationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - publishStatusQueryService, - culture); + where T : class, IPublishedContent + => content.SiblingsAndSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); - [Obsolete("Use the overload with IPublishStatusQueryService, scheduled for removal in v17")] + [Obsolete("Use the overload with INavigationQueryService and IPublishedStatusFilteringService, scheduled for removal in v17")] public static IEnumerable SiblingsAndSelf( this IPublishedContent content, IVariationContextAccessor variationContextAccessor, string? culture = null) - where T : class, IPublishedContent => SiblingsAndSelf( - content, - variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - culture); - + where T : class, IPublishedContent + => content.SiblingsAndSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); private static INavigationQueryService GetNavigationQueryService(IPublishedContent content) { - switch (content.ContentType.ItemType) + switch (content.ItemType) { case PublishedItemType.Content: return StaticServiceProvider.Instance.GetRequiredService(); @@ -4413,17 +3730,16 @@ public static class PublishedContentExtensions default: throw new NotSupportedException("Unsupported content type."); } - } - private static IPublishedCache GetPublishedCache(IPublishedContent content) + private static IPublishedStatusFilteringService GetPublishedStatusFilteringService(IPublishedContent content) { - switch (content.ContentType.ItemType) + switch (content.ItemType) { case PublishedItemType.Content: - return StaticServiceProvider.Instance.GetRequiredService(); + return StaticServiceProvider.Instance.GetRequiredService(); case PublishedItemType.Media: - return StaticServiceProvider.Instance.GetRequiredService(); + return StaticServiceProvider.Instance.GetRequiredService(); default: throw new NotSupportedException("Unsupported content type."); } @@ -4431,10 +3747,8 @@ public static class PublishedContentExtensions private static IEnumerable GetChildren( INavigationQueryService navigationQueryService, - IPublishedCache publishedCache, + IPublishedStatusFilteringService publishedStatusFilteringService, Guid parentKey, - IPublishStatusQueryService publishStatusQueryService, - IVariationContextAccessor? variationContextAccessor, string? contentTypeAlias = null, string? culture = null) { @@ -4446,38 +3760,20 @@ public static class PublishedContentExtensions { return []; } + // We need to filter what keys are published, as calling the GetById // with a non-existing published node, will get cache misses and call the DB // making it a very slow operation. - culture ??= variationContextAccessor?.VariationContext?.Culture ?? string.Empty; - - return childrenKeys - .Where(x => publishStatusQueryService.IsDocumentPublished(x, culture)) - .Select(publishedCache.GetById) - .WhereNotNull() + return publishedStatusFilteringService + .FilterAvailable(childrenKeys, culture) .OrderBy(x => x.SortOrder); } - private static IEnumerable FilterByCulture( - this IEnumerable contentNodes, - string? culture, - IVariationContextAccessor? variationContextAccessor) - { - // Determine the culture if not provided - culture ??= variationContextAccessor?.VariationContext?.Culture ?? string.Empty; - - return culture == "*" - ? contentNodes - : contentNodes.Where(x => x.IsInvariantOrHasCulture(culture)); - } - private static IEnumerable EnumerateDescendantsOrSelfInternal( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, string? culture, bool orSelf, string? contentTypeAlias = null) @@ -4499,12 +3795,8 @@ public static class PublishedContentExtensions yield break; } - culture ??= variationContextAccessor?.VariationContext?.Culture ?? string.Empty; - IEnumerable descendants = descendantsKeys - .Where(x => publishStatusQueryService.IsDocumentPublished(x, culture)) - .Select(publishedCache.GetById) - .WhereNotNull() - .FilterByCulture(culture, variationContextAccessor); + IEnumerable descendants = publishedStatusFilteringService + .FilterAvailable(descendantsKeys, culture); foreach (IPublishedContent descendant in descendants) { @@ -4514,10 +3806,8 @@ public static class PublishedContentExtensions private static IEnumerable EnumerateAncestorsOrSelfInternal( this IPublishedContent content, - IVariationContextAccessor variationContextAccessor, - IPublishedCache publishedCache, INavigationQueryService navigationQueryService, - IPublishStatusQueryService publishStatusQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, bool orSelf, string? contentTypeAlias = null, string? culture = null) @@ -4539,19 +3829,50 @@ public static class PublishedContentExtensions yield break; } - culture ??= variationContextAccessor.VariationContext?.Culture ?? string.Empty; - foreach (Guid ancestorKey in ancestorsKeys) + IEnumerable ancestors = publishedStatusFilteringService.FilterAvailable(ancestorsKeys, culture); + foreach (IPublishedContent ancestor in ancestors) { - if (publishStatusQueryService.IsDocumentPublished(ancestorKey, culture) is false) - { - yield break; - } - - IPublishedContent? ancestor = publishedCache.GetById(ancestorKey); - if (ancestor is not null) - { - yield return ancestor; - } + yield return ancestor; } } + + private static IEnumerable SiblingsAndSelfInternal( + this IPublishedContent content, + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, + string? contentTypeAlias, + string? culture) + { + if (navigationQueryService.TryGetParentKey(content.Key, out Guid? parentKey) is false) + { + return []; + } + + if (parentKey.HasValue) + { + IEnumerable childrenKeys; + var foundChildrenKeys = contentTypeAlias is null + ? navigationQueryService.TryGetChildrenKeys(parentKey.Value, out childrenKeys) + : navigationQueryService.TryGetChildrenKeysOfType(parentKey.Value, contentTypeAlias, out childrenKeys); + + return foundChildrenKeys + ? publishedStatusFilteringService.FilterAvailable(childrenKeys, culture) + : []; + } + + IEnumerable rootKeys; + var foundRootKeys = contentTypeAlias is null + ? navigationQueryService.TryGetRootKeys(out rootKeys) + : navigationQueryService.TryGetRootKeysOfType(contentTypeAlias, out rootKeys); + + if (foundRootKeys) + { + IEnumerable rootKeysArray = rootKeys as Guid[] ?? rootKeys.ToArray(); + return rootKeysArray.Contains(content.Key) + ? publishedStatusFilteringService.FilterAvailable(rootKeysArray, culture) + : []; + } + + return []; + } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs index bf41498c43..3687e1b7f3 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentBase.cs @@ -90,23 +90,23 @@ namespace Umbraco.Cms.Core.Models.PublishedContent private IEnumerable GetChildren() { INavigationQueryService? navigationQueryService; - IPublishedCache? publishedCache; + IPublishedStatusFilteringService? publishedStatusFilteringService; switch (ContentType.ItemType) { case PublishedItemType.Content: - publishedCache = StaticServiceProvider.Instance.GetRequiredService(); navigationQueryService = StaticServiceProvider.Instance.GetRequiredService(); + publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService(); break; case PublishedItemType.Media: - publishedCache = StaticServiceProvider.Instance.GetRequiredService(); navigationQueryService = StaticServiceProvider.Instance.GetRequiredService(); + publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService(); break; default: throw new NotImplementedException("Level is not implemented for " + ContentType.ItemType); } - return this.Children(_variationContextAccessor, publishedCache, navigationQueryService); + return this.Children(navigationQueryService, publishedStatusFilteringService); } } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs index bec5250e01..3360b0e78a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs @@ -188,7 +188,7 @@ public class PublishedValueFallback : IPublishedValueFallback IPublishedProperty? property; // if we are here, content's property has no value do { - content = content?.Parent(StaticServiceProvider.Instance.GetRequiredService(), StaticServiceProvider.Instance.GetRequiredService()); + content = content?.Parent(StaticServiceProvider.Instance.GetRequiredService(), StaticServiceProvider.Instance.GetRequiredService()); IPublishedPropertyType? propertyType = content?.ContentType.GetPropertyType(alias); diff --git a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs index f8bffbba77..90c4070752 100644 --- a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs +++ b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs @@ -70,7 +70,7 @@ public sealed class InternalPublishedContent : IPublishedContent public PublishedItemType ItemType => PublishedItemType.Content; [Obsolete("Please use TryGetParentKey() on IDocumentNavigationQueryService or IMediaNavigationQueryService instead. Scheduled for removal in V16.")] - public IPublishedContent? Parent => this.Parent(StaticServiceProvider.Instance.GetRequiredService(), StaticServiceProvider.Instance.GetRequiredService()); + public IPublishedContent? Parent => this.Parent(StaticServiceProvider.Instance.GetRequiredService(), StaticServiceProvider.Instance.GetRequiredService()); public bool IsDraft(string? culture = null) => false; @@ -78,9 +78,8 @@ public sealed class InternalPublishedContent : IPublishedContent [Obsolete("Please use TryGetChildrenKeys() on IDocumentNavigationQueryService or IMediaNavigationQueryService instead. Scheduled for removal in V16.")] public IEnumerable Children => this.Children( - StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()); + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()); public IEnumerable ChildrenForAllCultures => Children; @@ -102,7 +101,7 @@ public sealed class InternalPublishedContent : IPublishedContent IPublishedContent? content = this; while (content != null && (property == null || property.HasValue() == false)) { - content = content.Parent(StaticServiceProvider.Instance.GetRequiredService(), StaticServiceProvider.Instance.GetRequiredService()); + content = content.Parent(StaticServiceProvider.Instance.GetRequiredService(), StaticServiceProvider.Instance.GetRequiredService()); property = content?.GetProperty(alias); } diff --git a/src/Umbraco.Core/Routing/AliasUrlProvider.cs b/src/Umbraco.Core/Routing/AliasUrlProvider.cs index 59e9e1d381..0809108564 100644 --- a/src/Umbraco.Core/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Core/Routing/AliasUrlProvider.cs @@ -18,8 +18,8 @@ public class AliasUrlProvider : IUrlProvider private readonly IPublishedValueFallback _publishedValueFallback; private readonly ISiteDomainMapper _siteDomainMapper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; private readonly UriUtility _uriUtility; private RequestHandlerSettings _requestConfig; @@ -29,21 +29,62 @@ public class AliasUrlProvider : IUrlProvider UriUtility uriUtility, IPublishedValueFallback publishedValueFallback, IUmbracoContextAccessor umbracoContextAccessor, - IPublishedContentCache contentCache, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) { _requestConfig = requestConfig.CurrentValue; _siteDomainMapper = siteDomainMapper; _uriUtility = uriUtility; _publishedValueFallback = publishedValueFallback; _umbracoContextAccessor = umbracoContextAccessor; - _contentCache = contentCache; _navigationQueryService = navigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; requestConfig.OnChange(x => _requestConfig = x); } - [Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")] + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public AliasUrlProvider( + IOptionsMonitor requestConfig, + ISiteDomainMapper siteDomainMapper, + UriUtility uriUtility, + IPublishedValueFallback publishedValueFallback, + IUmbracoContextAccessor umbracoContextAccessor, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + : this( + requestConfig, + siteDomainMapper, + uriUtility, + publishedValueFallback, + umbracoContextAccessor, + navigationQueryService, + publishedContentStatusFilteringService) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public AliasUrlProvider( + IOptionsMonitor requestConfig, + ISiteDomainMapper siteDomainMapper, + UriUtility uriUtility, + IPublishedValueFallback publishedValueFallback, + IUmbracoContextAccessor umbracoContextAccessor, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService) + : this( + requestConfig, + siteDomainMapper, + uriUtility, + publishedValueFallback, + umbracoContextAccessor, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public AliasUrlProvider( IOptionsMonitor requestConfig, ISiteDomainMapper siteDomainMapper, @@ -56,8 +97,8 @@ public class AliasUrlProvider : IUrlProvider uriUtility, publishedValueFallback, umbracoContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -108,7 +149,7 @@ public class AliasUrlProvider : IUrlProvider while (domainUris == null && n != null) { // move to parent node - n = n.Parent(_contentCache, _navigationQueryService); + n = n.Parent(_navigationQueryService, _publishedContentStatusFilteringService); domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.Domains, _siteDomainMapper, n.Id, current, false); diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs index 39cd4c8854..7f16b1fd48 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs @@ -24,14 +24,46 @@ public class ContentFinderByUrlAlias : IContentFinder private readonly ILogger _logger; private readonly IPublishedValueFallback _publishedValueFallback; private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _documentNavigationQueryService; - private readonly IPublishStatusQueryService _publishStatusQueryService; - private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; /// /// Initializes a new instance of the class. /// + public ContentFinderByUrlAlias( + ILogger logger, + IPublishedValueFallback publishedValueFallback, + IUmbracoContextAccessor umbracoContextAccessor, + IDocumentNavigationQueryService documentNavigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + { + _publishedValueFallback = publishedValueFallback; + _umbracoContextAccessor = umbracoContextAccessor; + _documentNavigationQueryService = documentNavigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; + _logger = logger; + } + + [Obsolete("Please use tne non-obsolete constructor instead. Scheduled removal in v17")] + public ContentFinderByUrlAlias( + ILogger logger, + IPublishedValueFallback publishedValueFallback, + IVariationContextAccessor variationContextAccessor, + IUmbracoContextAccessor umbracoContextAccessor, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService documentNavigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + : this( + logger, + publishedValueFallback, + umbracoContextAccessor, + documentNavigationQueryService, + publishedContentStatusFilteringService) + { + } + + [Obsolete("Please use tne non-obsolete constructor instead. Scheduled removal in v17")] public ContentFinderByUrlAlias( ILogger logger, IPublishedValueFallback publishedValueFallback, @@ -40,17 +72,17 @@ public class ContentFinderByUrlAlias : IContentFinder IPublishedContentCache contentCache, IDocumentNavigationQueryService documentNavigationQueryService, IPublishStatusQueryService publishStatusQueryService) + : this( + logger, + publishedValueFallback, + umbracoContextAccessor, + documentNavigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) { - _publishedValueFallback = publishedValueFallback; - _variationContextAccessor = variationContextAccessor; - _umbracoContextAccessor = umbracoContextAccessor; - _contentCache = contentCache; - _documentNavigationQueryService = documentNavigationQueryService; - _publishStatusQueryService = publishStatusQueryService; - _logger = logger; } - [Obsolete("Please use constructor that takes an IPublishStatusQueryService instead. Scheduled removal in v17")] + + [Obsolete("Please use tne non-obsolete constructor instead. Scheduled removal in v17")] public ContentFinderByUrlAlias( ILogger logger, IPublishedValueFallback publishedValueFallback, @@ -58,14 +90,12 @@ public class ContentFinderByUrlAlias : IContentFinder IUmbracoContextAccessor umbracoContextAccessor, IPublishedContentCache contentCache, IDocumentNavigationQueryService documentNavigationQueryService) - : this( - logger, - publishedValueFallback, - variationContextAccessor, - umbracoContextAccessor, - contentCache, - documentNavigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()) + : this( + logger, + publishedValueFallback, + umbracoContextAccessor, + documentNavigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -169,14 +199,14 @@ public class ContentFinderByUrlAlias : IContentFinder if (rootNodeId > 0) { IPublishedContent? rootNode = cache?.GetById(rootNodeId); - return rootNode?.Descendants(_variationContextAccessor, _contentCache, _documentNavigationQueryService, _publishStatusQueryService).FirstOrDefault(x => IsMatch(x, test1, test2)); + return rootNode?.Descendants(_documentNavigationQueryService, _publishedContentStatusFilteringService).FirstOrDefault(x => IsMatch(x, test1, test2)); } if (cache is not null) { foreach (IPublishedContent rootContent in cache.GetAtRoot()) { - IPublishedContent? c = rootContent.DescendantsOrSelf(_variationContextAccessor, _contentCache, _documentNavigationQueryService, _publishStatusQueryService) + IPublishedContent? c = rootContent.DescendantsOrSelf(_documentNavigationQueryService, _publishedContentStatusFilteringService) .FirstOrDefault(x => IsMatch(x, test1, test2)); if (c != null) { diff --git a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 72f1dac37e..1ec57e20e5 100644 --- a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -23,8 +23,8 @@ public class DefaultUrlProvider : IUrlProvider private readonly ILogger _logger; private readonly ISiteDomainMapper _siteDomainMapper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; private readonly UriUtility _uriUtility; private RequestHandlerSettings _requestSettings; @@ -35,8 +35,8 @@ public class DefaultUrlProvider : IUrlProvider IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility, ILocalizationService localizationService, - IPublishedContentCache contentCache, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) { _requestSettings = requestSettings.CurrentValue; _logger = logger; @@ -44,13 +44,58 @@ public class DefaultUrlProvider : IUrlProvider _umbracoContextAccessor = umbracoContextAccessor; _uriUtility = uriUtility; _localizationService = localizationService; - _contentCache = contentCache; _navigationQueryService = navigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; requestSettings.OnChange(x => _requestSettings = x); } - [Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")] + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public DefaultUrlProvider( + IOptionsMonitor requestSettings, + ILogger logger, + ISiteDomainMapper siteDomainMapper, + IUmbracoContextAccessor umbracoContextAccessor, + UriUtility uriUtility, + ILocalizationService localizationService, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + : this( + requestSettings, + logger, + siteDomainMapper, + umbracoContextAccessor, + uriUtility, + localizationService, + navigationQueryService, + publishedContentStatusFilteringService) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public DefaultUrlProvider( + IOptionsMonitor requestSettings, + ILogger logger, + ISiteDomainMapper siteDomainMapper, + IUmbracoContextAccessor umbracoContextAccessor, + UriUtility uriUtility, + ILocalizationService localizationService, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService) + : this( + requestSettings, + logger, + siteDomainMapper, + umbracoContextAccessor, + uriUtility, + localizationService, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public DefaultUrlProvider( IOptionsMonitor requestSettings, ILogger logger, @@ -65,8 +110,8 @@ public class DefaultUrlProvider : IUrlProvider umbracoContextAccessor, uriUtility, localizationService, - StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -101,7 +146,7 @@ public class DefaultUrlProvider : IUrlProvider // n is null at root while (domainUris == null && n != null) { - n = n.Parent(_contentCache, _navigationQueryService); // move to parent node + n = n.Parent(_navigationQueryService, _publishedContentStatusFilteringService); // move to parent node domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.Domains, _siteDomainMapper, n.Id, current); diff --git a/src/Umbraco.Core/Routing/DomainUtilities.cs b/src/Umbraco.Core/Routing/DomainUtilities.cs index 0f8296c919..560285b5cf 100644 --- a/src/Umbraco.Core/Routing/DomainUtilities.cs +++ b/src/Umbraco.Core/Routing/DomainUtilities.cs @@ -17,21 +17,7 @@ namespace Umbraco.Cms.Core.Routing { #region Document Culture - /// - /// Gets the culture assigned to a document by domains, in the context of a current Uri. - /// - /// The document identifier. - /// The document path. - /// An optional current Uri. - /// An Umbraco context. - /// The site domain helper. - /// 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. - /// - [Obsolete("Please use the method taking all parameters. This overload will be removed in V17.")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static string? GetCultureFromDomains( int contentId, string contentPath, @@ -45,8 +31,28 @@ namespace Umbraco.Cms.Core.Routing umbracoContext, siteDomainMapper, StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()); + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static string? GetCultureFromDomains( + int contentId, + string contentPath, + Uri? current, + IUmbracoContext umbracoContext, + ISiteDomainMapper siteDomainMapper, + IDomainCache domainCache, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService) + => GetCultureFromDomains( + contentId, + contentPath, + current, + umbracoContext, + siteDomainMapper, + domainCache, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()); /// /// Gets the culture assigned to a document by domains, in the context of a current Uri. @@ -57,8 +63,8 @@ namespace Umbraco.Cms.Core.Routing /// An Umbraco context. /// The site domain helper. /// The domain cache. - /// The published content cache. /// The navigation query service. + /// /// The culture assigned to the document by domains. /// /// In 1:1 multilingual setup, a document contains several cultures (there is not @@ -72,8 +78,8 @@ namespace Umbraco.Cms.Core.Routing IUmbracoContext umbracoContext, ISiteDomainMapper siteDomainMapper, IDomainCache domainCache, - IPublishedCache publishedCache, - INavigationQueryService navigationQueryService) + INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService) { if (umbracoContext == null) { @@ -85,7 +91,7 @@ namespace Umbraco.Cms.Core.Routing current = umbracoContext.CleanedUmbracoUrl; } - var domainNodeId = GetAncestorNodeWithDomainsAssigned(contentId, umbracoContext, domainCache, publishedCache, navigationQueryService); + var domainNodeId = GetAncestorNodeWithDomainsAssigned(contentId, umbracoContext, domainCache, navigationQueryService, publishedStatusFilteringService); DomainAndUri? domain = domainNodeId.HasValue ? DomainForNode(umbracoContext.Domains, siteDomainMapper, domainNodeId.Value, current) @@ -107,13 +113,13 @@ namespace Umbraco.Cms.Core.Routing return umbracoContext.Domains?.DefaultCulture; } - private static int? GetAncestorNodeWithDomainsAssigned(int contentId, IUmbracoContext umbracoContext, IDomainCache domainCache, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) + private static int? GetAncestorNodeWithDomainsAssigned(int contentId, IUmbracoContext umbracoContext, IDomainCache domainCache, INavigationQueryService navigationQueryService, IPublishedStatusFilteringService publishedStatusFilteringService) { IPublishedContent? content = umbracoContext.Content.GetById(contentId); var hasDomains = ContentHasAssignedDomains(content, domainCache); while (content is not null && !hasDomains) { - content = content.Parent(publishedCache, navigationQueryService); + content = content.Parent(navigationQueryService, publishedStatusFilteringService); hasDomains = content is not null && domainCache.HasAssigned(content.Id, true); } diff --git a/src/Umbraco.Core/Routing/NewDefaultUrlProvider.cs b/src/Umbraco.Core/Routing/NewDefaultUrlProvider.cs index 7f66f21933..3b140021a8 100644 --- a/src/Umbraco.Core/Routing/NewDefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/NewDefaultUrlProvider.cs @@ -1,7 +1,9 @@ using System.Globalization; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; @@ -23,6 +25,7 @@ public class NewDefaultUrlProvider : IUrlProvider private readonly IIdKeyMap _idKeyMap; private readonly IDocumentUrlService _documentUrlService; private readonly IDocumentNavigationQueryService _navigationQueryService; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; private readonly ILocalizedTextService? _localizedTextService; private readonly ILogger _logger; private readonly ISiteDomainMapper _siteDomainMapper; @@ -41,7 +44,8 @@ public class NewDefaultUrlProvider : IUrlProvider IDomainCache domainCache, IIdKeyMap idKeyMap, IDocumentUrlService documentUrlService, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) { _requestSettings = requestSettings.CurrentValue; _logger = logger; @@ -54,10 +58,40 @@ public class NewDefaultUrlProvider : IUrlProvider _idKeyMap = idKeyMap; _documentUrlService = documentUrlService; _navigationQueryService = navigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; requestSettings.OnChange(x => _requestSettings = x); } + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public NewDefaultUrlProvider( + IOptionsMonitor requestSettings, + ILogger logger, + ISiteDomainMapper siteDomainMapper, + IUmbracoContextAccessor umbracoContextAccessor, + UriUtility uriUtility, + ILocalizationService localizationService, + IPublishedContentCache publishedContentCache, + IDomainCache domainCache, + IIdKeyMap idKeyMap, + IDocumentUrlService documentUrlService, + IDocumentNavigationQueryService navigationQueryService) + : this( + requestSettings, + logger, + siteDomainMapper, + umbracoContextAccessor, + uriUtility, + localizationService, + publishedContentCache, + domainCache, + idKeyMap, + documentUrlService, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + #region GetOtherUrls /// @@ -97,7 +131,7 @@ public class NewDefaultUrlProvider : IUrlProvider // n is null at root while (domainUris == null && n != null) { - n = n.Parent(_publishedContentCache, _navigationQueryService); // move to parent node + n = n.Parent(_navigationQueryService, _publishedContentStatusFilteringService); // move to parent node domainUris = n == null ? null : DomainUtilities.DomainsForNode(_domainCache, _siteDomainMapper, n.Id, current); diff --git a/src/Umbraco.Core/Routing/UrlProvider.cs b/src/Umbraco.Core/Routing/UrlProvider.cs index f034fbf8b9..16dc21956b 100644 --- a/src/Umbraco.Core/Routing/UrlProvider.cs +++ b/src/Umbraco.Core/Routing/UrlProvider.cs @@ -25,9 +25,49 @@ namespace Umbraco.Cms.Core.Routing /// The list of URL providers. /// The list of media URL providers. /// The current variation accessor. - /// The content cache. /// The query service for the in-memory navigation structure. - /// The publish status query service, to query if a given content is published in a given culture. + /// + public UrlProvider( + IUmbracoContextAccessor umbracoContextAccessor, + IOptions routingSettings, + UrlProviderCollection urlProviders, + MediaUrlProviderCollection mediaUrlProviders, + IVariationContextAccessor variationContextAccessor, + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + { + _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); + _urlProviders = urlProviders; + _mediaUrlProviders = mediaUrlProviders; + _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); + _navigationQueryService = navigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; + Mode = routingSettings.Value.UrlProviderMode; + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public UrlProvider( + IUmbracoContextAccessor umbracoContextAccessor, + IOptions routingSettings, + UrlProviderCollection urlProviders, + MediaUrlProviderCollection mediaUrlProviders, + IVariationContextAccessor variationContextAccessor, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + : this( + umbracoContextAccessor, + routingSettings, + urlProviders, + mediaUrlProviders, + variationContextAccessor, + navigationQueryService, + publishedContentStatusFilteringService) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public UrlProvider( IUmbracoContextAccessor umbracoContextAccessor, IOptions routingSettings, @@ -37,18 +77,18 @@ namespace Umbraco.Cms.Core.Routing IPublishedContentCache contentCache, IDocumentNavigationQueryService navigationQueryService, IPublishStatusQueryService publishStatusQueryService) + : this( + umbracoContextAccessor, + routingSettings, + urlProviders, + mediaUrlProviders, + variationContextAccessor, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) { - _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); - _urlProviders = urlProviders; - _mediaUrlProviders = mediaUrlProviders; - _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - _contentCache = contentCache; - _navigationQueryService = navigationQueryService; - _publishStatusQueryService = publishStatusQueryService; - Mode = routingSettings.Value.UrlProviderMode; } - [Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")] + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public UrlProvider( IUmbracoContextAccessor umbracoContextAccessor, IOptions routingSettings, @@ -63,13 +103,12 @@ namespace Umbraco.Cms.Core.Routing urlProviders, mediaUrlProviders, variationContextAccessor, - contentCache, navigationQueryService, - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService()) { } - [Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")] + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public UrlProvider( IUmbracoContextAccessor umbracoContextAccessor, IOptions routingSettings, @@ -82,9 +121,8 @@ namespace Umbraco.Cms.Core.Routing urlProviders, mediaUrlProviders, variationContextAccessor, - StaticServiceProvider.Instance.GetRequiredService(), StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -92,9 +130,8 @@ namespace Umbraco.Cms.Core.Routing private readonly IEnumerable _urlProviders; private readonly IEnumerable _mediaUrlProviders; private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; - private readonly IPublishStatusQueryService _publishStatusQueryService; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; /// /// Gets or sets the provider URL mode. @@ -173,7 +210,7 @@ namespace Umbraco.Cms.Core.Routing // be nice with tests, assume things can be null, ultimately fall back to invariant // (but only for variant content of course) // We need to check all ancestors because urls are variant even for invariant content, if an ancestor is variant. - if (culture == null && content.AncestorsOrSelf(_variationContextAccessor, _contentCache, _navigationQueryService, _publishStatusQueryService).Any(x => x.ContentType.VariesByCulture())) + if (culture == null && content.AncestorsOrSelf(_navigationQueryService, _publishedContentStatusFilteringService).Any(x => x.ContentType.VariesByCulture())) { culture = _variationContextAccessor?.VariationContext?.Culture ?? string.Empty; } diff --git a/src/Umbraco.Core/Routing/UrlProviderExtensions.cs b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs index 6606ff8f6c..a4e7fbc209 100644 --- a/src/Umbraco.Core/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs @@ -14,7 +14,7 @@ namespace Umbraco.Extensions; public static class UrlProviderExtensions { - [Obsolete("Use GetContentUrlsAsync that takes all parameters. Will be removed in V17.")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static async Task> GetContentUrlsAsync( this IContent content, IPublishedRouter publishedRouter, @@ -36,8 +36,35 @@ public static class UrlProviderExtensions logger, uriUtility, publishedUrlProvider, - StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()); + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()); + + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] + public static async Task> GetContentUrlsAsync( + this IContent content, + IPublishedRouter publishedRouter, + IUmbracoContext umbracoContext, + ILanguageService languageService, + ILocalizedTextService textService, + IContentService contentService, + IVariationContextAccessor variationContextAccessor, + ILogger logger, + UriUtility uriUtility, + IPublishedUrlProvider publishedUrlProvider, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService) + => await content.GetContentUrlsAsync( + publishedRouter, + umbracoContext, + languageService, + textService, + contentService, + variationContextAccessor, + logger, + uriUtility, + publishedUrlProvider, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()); /// /// Gets the URLs of the content item. @@ -57,8 +84,8 @@ public static class UrlProviderExtensions ILogger logger, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider, - IPublishedContentCache contentCache, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) { ArgumentNullException.ThrowIfNull(content); ArgumentNullException.ThrowIfNull(publishedRouter); @@ -95,7 +122,7 @@ public static class UrlProviderExtensions // get all URLs for all cultures // in a HashSet, so de-duplicates too - foreach (UrlInfo cultureUrl in await GetContentUrlsByCultureAsync(content, cultures, publishedRouter, umbracoContext, contentService, textService, variationContextAccessor, logger, uriUtility, publishedUrlProvider, contentCache, navigationQueryService)) + foreach (UrlInfo cultureUrl in await GetContentUrlsByCultureAsync(content, cultures, publishedRouter, umbracoContext, contentService, textService, variationContextAccessor, logger, uriUtility, publishedUrlProvider, navigationQueryService, publishedContentStatusFilteringService)) { urls.Add(cultureUrl); } @@ -146,8 +173,8 @@ public static class UrlProviderExtensions ILogger logger, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider, - IPublishedContentCache contentCache, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) { var result = new List(); @@ -186,7 +213,7 @@ public static class UrlProviderExtensions // got a URL, deal with collisions, add URL default: // detect collisions, etc - Attempt hasCollision = await DetectCollisionAsync(logger, content, url, culture, umbracoContext, publishedRouter, textService, variationContextAccessor, uriUtility, contentCache, navigationQueryService); + Attempt hasCollision = await DetectCollisionAsync(logger, content, url, culture, umbracoContext, publishedRouter, textService, variationContextAccessor, uriUtility, navigationQueryService, publishedContentStatusFilteringService); if (hasCollision.Success && hasCollision.Result is not null) { result.Add(hasCollision.Result); @@ -243,8 +270,8 @@ public static class UrlProviderExtensions ILocalizedTextService textService, IVariationContextAccessor variationContextAccessor, UriUtility uriUtility, - IPublishedContentCache contentCache, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) { // test for collisions on the 'main' URL var uri = new Uri(url.TrimEnd(Constants.CharArrays.ForwardSlash), UriKind.RelativeOrAbsolute); @@ -283,7 +310,7 @@ public static class UrlProviderExtensions while (o != null) { l.Add(o.Name(variationContextAccessor)!); - o = o.Parent(contentCache, navigationQueryService); + o = o.Parent(navigationQueryService, publishedContentStatusFilteringService); } l.Reverse(); diff --git a/src/Umbraco.Core/Services/PublishStatus/IPublishedContentStatusFilteringService.cs b/src/Umbraco.Core/Services/PublishStatus/IPublishedContentStatusFilteringService.cs new file mode 100644 index 0000000000..1c54de959f --- /dev/null +++ b/src/Umbraco.Core/Services/PublishStatus/IPublishedContentStatusFilteringService.cs @@ -0,0 +1,5 @@ +namespace Umbraco.Cms.Core.Services.Navigation; + +public interface IPublishedContentStatusFilteringService : IPublishedStatusFilteringService +{ +} diff --git a/src/Umbraco.Core/Services/PublishStatus/IPublishedMediaStatusFilteringService.cs b/src/Umbraco.Core/Services/PublishStatus/IPublishedMediaStatusFilteringService.cs new file mode 100644 index 0000000000..5841bacd03 --- /dev/null +++ b/src/Umbraco.Core/Services/PublishStatus/IPublishedMediaStatusFilteringService.cs @@ -0,0 +1,5 @@ +namespace Umbraco.Cms.Core.Services.Navigation; + +public interface IPublishedMediaStatusFilteringService : IPublishedStatusFilteringService +{ +} diff --git a/src/Umbraco.Core/Services/PublishStatus/IPublishedStatusFilteringService.cs b/src/Umbraco.Core/Services/PublishStatus/IPublishedStatusFilteringService.cs new file mode 100644 index 0000000000..1cdffea26c --- /dev/null +++ b/src/Umbraco.Core/Services/PublishStatus/IPublishedStatusFilteringService.cs @@ -0,0 +1,8 @@ +using Umbraco.Cms.Core.Models.PublishedContent; + +namespace Umbraco.Cms.Core.Services.Navigation; + +public interface IPublishedStatusFilteringService +{ + IEnumerable FilterAvailable(IEnumerable candidateKeys, string? culture); +} diff --git a/src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs b/src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs new file mode 100644 index 0000000000..e31ae8e347 --- /dev/null +++ b/src/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringService.cs @@ -0,0 +1,50 @@ +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Services.Navigation; + +internal sealed class PublishedContentStatusFilteringService : IPublishedContentStatusFilteringService +{ + private readonly IVariationContextAccessor _variationContextAccessor; + private readonly IPublishStatusQueryService _publishStatusQueryService; + private readonly IPreviewService _previewService; + private readonly IPublishedContentCache _publishedContentCache; + + public PublishedContentStatusFilteringService( + IVariationContextAccessor variationContextAccessor, + IPublishStatusQueryService publishStatusQueryService, + IPreviewService previewService, + IPublishedContentCache publishedContentCache) + { + _variationContextAccessor = variationContextAccessor; + _publishStatusQueryService = publishStatusQueryService; + _previewService = previewService; + _publishedContentCache = publishedContentCache; + } + + public IEnumerable FilterAvailable(IEnumerable candidateKeys, string? culture) + { + culture ??= _variationContextAccessor.VariationContext?.Culture ?? string.Empty; + + Guid[] candidateKeysAsArray = candidateKeys as Guid[] ?? candidateKeys.ToArray(); + if (candidateKeysAsArray.Length == 0) + { + return []; + } + + var preview = _previewService.IsInPreview(); + candidateKeys = preview + ? candidateKeysAsArray + : candidateKeysAsArray.Where(key => _publishStatusQueryService.IsDocumentPublished(key, culture)); + + return WhereIsInvariantOrHasCulture(candidateKeys, culture, preview).ToArray(); + } + + private IEnumerable WhereIsInvariantOrHasCulture(IEnumerable keys, string culture, bool preview) + => keys + .Select(key => _publishedContentCache.GetById(preview, key)) + .WhereNotNull() + .Where(content => content.ContentType.VariesByCulture() is false + || content.Cultures.ContainsKey(culture)); +} diff --git a/src/Umbraco.Core/Services/PublishStatus/PublishedMediaStatusFilteringService.cs b/src/Umbraco.Core/Services/PublishStatus/PublishedMediaStatusFilteringService.cs new file mode 100644 index 0000000000..3c91e1d7bb --- /dev/null +++ b/src/Umbraco.Core/Services/PublishStatus/PublishedMediaStatusFilteringService.cs @@ -0,0 +1,19 @@ +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Services.Navigation; + +// NOTE: this class is basically a no-op implementation of IPublishStatusQueryService, because the published +// content extensions need a media equivalent to the content implementation. +// incidentally, if we'll ever support variant and/or draft media, this comes in really handy :-) +internal sealed class PublishedMediaStatusFilteringService : IPublishedMediaStatusFilteringService +{ + private readonly IPublishedMediaCache _publishedMediaCache; + + public PublishedMediaStatusFilteringService(IPublishedMediaCache publishedMediaCache) + => _publishedMediaCache = publishedMediaCache; + + public IEnumerable FilterAvailable(IEnumerable candidateKeys, string? culture) + => candidateKeys.Select(_publishedMediaCache.GetById).WhereNotNull().ToArray(); +} diff --git a/src/Umbraco.Infrastructure/Routing/RedirectTracker.cs b/src/Umbraco.Infrastructure/Routing/RedirectTracker.cs index e26fa7d35c..9a84a6ee56 100644 --- a/src/Umbraco.Infrastructure/Routing/RedirectTracker.cs +++ b/src/Umbraco.Infrastructure/Routing/RedirectTracker.cs @@ -1,27 +1,68 @@ using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.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.Services.Navigation; -using Umbraco.Cms.Core.Web; using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Routing { internal class RedirectTracker : IRedirectTracker { - private readonly IVariationContextAccessor _variationContextAccessor; private readonly ILocalizationService _localizationService; private readonly IRedirectUrlService _redirectUrlService; private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; private readonly ILogger _logger; private readonly IPublishedUrlProvider _publishedUrlProvider; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; + public RedirectTracker( + ILocalizationService localizationService, + IRedirectUrlService redirectUrlService, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService, + ILogger logger, + IPublishedUrlProvider publishedUrlProvider, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + { + _localizationService = localizationService; + _redirectUrlService = redirectUrlService; + _contentCache = contentCache; + _navigationQueryService = navigationQueryService; + _logger = logger; + _publishedUrlProvider = publishedUrlProvider; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public RedirectTracker( + IVariationContextAccessor variationContextAccessor, + ILocalizationService localizationService, + IRedirectUrlService redirectUrlService, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService, + ILogger logger, + IPublishedUrlProvider publishedUrlProvider, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + : this( + localizationService, + redirectUrlService, + contentCache, + navigationQueryService, + logger, + publishedUrlProvider, + publishedContentStatusFilteringService) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public RedirectTracker( IVariationContextAccessor variationContextAccessor, ILocalizationService localizationService, @@ -30,14 +71,15 @@ namespace Umbraco.Cms.Infrastructure.Routing IDocumentNavigationQueryService navigationQueryService, ILogger logger, IPublishedUrlProvider publishedUrlProvider) + : this( + localizationService, + redirectUrlService, + contentCache, + navigationQueryService, + logger, + publishedUrlProvider, + StaticServiceProvider.Instance.GetRequiredService()) { - _variationContextAccessor = variationContextAccessor; - _localizationService = localizationService; - _redirectUrlService = redirectUrlService; - _contentCache = contentCache; - _navigationQueryService = navigationQueryService; - _logger = logger; - _publishedUrlProvider = publishedUrlProvider; } /// @@ -50,12 +92,12 @@ namespace Umbraco.Cms.Infrastructure.Routing } // Get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures) - var defaultCultures = new Lazy(() => entityContent.AncestorsOrSelf(_contentCache, _navigationQueryService).FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray() ?? Array.Empty()); + var defaultCultures = new Lazy(() => entityContent.AncestorsOrSelf(_navigationQueryService, _publishedContentStatusFilteringService).FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray() ?? Array.Empty()); // Get all language ISO codes (in case we're dealing with invariant content with variant ancestors) var languageIsoCodes = new Lazy(() => _localizationService.GetAllLanguages().Select(x => x.IsoCode).ToArray()); - foreach (IPublishedContent publishedContent in entityContent.DescendantsOrSelf(_variationContextAccessor, _contentCache, _navigationQueryService)) + foreach (IPublishedContent publishedContent in entityContent.DescendantsOrSelf(_navigationQueryService, _publishedContentStatusFilteringService)) { // If this entity defines specific cultures, use those instead of the default ones IEnumerable cultures = publishedContent.Cultures.Any() ? publishedContent.Cultures.Keys : defaultCultures.Value; diff --git a/src/Umbraco.PublishedCache.HybridCache/PublishedContent.cs b/src/Umbraco.PublishedCache.HybridCache/PublishedContent.cs index 79d23397e9..29a865f099 100644 --- a/src/Umbraco.PublishedCache.HybridCache/PublishedContent.cs +++ b/src/Umbraco.PublishedCache.HybridCache/PublishedContent.cs @@ -270,22 +270,22 @@ internal class PublishedContent : PublishedContentBase private IPublishedContent? GetParent() { INavigationQueryService? navigationQueryService; - IPublishedCache? publishedCache; + IPublishedStatusFilteringService? publishedStatusFilteringService; switch (ContentType.ItemType) { case PublishedItemType.Content: - publishedCache = StaticServiceProvider.Instance.GetRequiredService(); navigationQueryService = StaticServiceProvider.Instance.GetRequiredService(); + publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService(); break; case PublishedItemType.Media: - publishedCache = StaticServiceProvider.Instance.GetRequiredService(); navigationQueryService = StaticServiceProvider.Instance.GetRequiredService(); + publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService(); break; default: throw new NotImplementedException("Level is not implemented for " + ContentType.ItemType); } - return this.Parent(publishedCache, navigationQueryService); + return this.Parent(navigationQueryService, publishedStatusFilteringService); } } diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs index 31171cc880..921c815e2f 100644 --- a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs @@ -25,9 +25,6 @@ public static class FriendlyPublishedContentExtensions private static IPublishedContentCache PublishedContentCache { get; } = StaticServiceProvider.Instance.GetRequiredService(); - private static IPublishedMediaCache PublishedMediaCache { get; } = - StaticServiceProvider.Instance.GetRequiredService(); - private static IDocumentNavigationQueryService DocumentNavigationQueryService { get; } = StaticServiceProvider.Instance.GetRequiredService(); @@ -70,9 +67,6 @@ public static class FriendlyPublishedContentExtensions private static IMemberTypeService MemberTypeService { get; } = StaticServiceProvider.Instance.GetRequiredService(); - private static IPublishStatusQueryService PublishStatusQueryService { get; } = - StaticServiceProvider.Instance.GetRequiredService(); - private static INavigationQueryService GetNavigationQueryService(IPublishedContent content) { switch (content.ContentType.ItemType) @@ -84,17 +78,16 @@ public static class FriendlyPublishedContentExtensions default: throw new NotSupportedException("Unsupported content type."); } - } - private static IPublishedCache GetPublishedCache(IPublishedContent content) + private static IPublishedStatusFilteringService GetPublishedStatusFilteringService(IPublishedContent content) { switch (content.ContentType.ItemType) { case PublishedItemType.Content: - return PublishedContentCache; + return StaticServiceProvider.Instance.GetRequiredService(); case PublishedItemType.Media: - return PublishedMediaCache; + return StaticServiceProvider.Instance.GetRequiredService(); default: throw new NotSupportedException("Unsupported content type."); } @@ -246,7 +239,7 @@ public static class FriendlyPublishedContentExtensions /// set to 1. /// public static IPublishedContent Root(this IPublishedContent content) - => content.Root(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); + => content.Root(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the root content (ancestor or self at level 1) for the specified if it's of the @@ -265,7 +258,7 @@ public static class FriendlyPublishedContentExtensions /// public static T? Root(this IPublishedContent content) where T : class, IPublishedContent - => content.Root(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); + => content.Root(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the parent of the content item. @@ -275,7 +268,7 @@ public static class FriendlyPublishedContentExtensions /// The parent of content of the specified content type or null. public static T? Parent(this IPublishedContent content) where T : class, IPublishedContent - => content.Parent(GetPublishedCache(content), GetNavigationQueryService(content)); + => content.Parent(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the parent of the content item. @@ -283,7 +276,7 @@ public static class FriendlyPublishedContentExtensions /// The content. /// The parent of content or null. public static IPublishedContent? Parent(this IPublishedContent content) - => content.Parent(GetPublishedCache(content), GetNavigationQueryService(content)); + => content.Parent(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the ancestors of the content. @@ -292,7 +285,7 @@ public static class FriendlyPublishedContentExtensions /// The ancestors of the content, in down-top order. /// Does not consider the content itself. public static IEnumerable Ancestors(this IPublishedContent content) - => content.Ancestors(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); + => content.Ancestors(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the content and its ancestors. @@ -300,7 +293,7 @@ public static class FriendlyPublishedContentExtensions /// The content. /// The content and its ancestors, in down-top order. public static IEnumerable AncestorsOrSelf(this IPublishedContent content) - => content.AncestorsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); + => content.AncestorsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the content and its ancestors, of a specified content type. @@ -311,7 +304,7 @@ public static class FriendlyPublishedContentExtensions /// May or may not begin with the content itself, depending on its content type. public static IEnumerable AncestorsOrSelf(this IPublishedContent content) where T : class, IPublishedContent - => content.AncestorsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); + => content.AncestorsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the ancestor of the content, i.e. its parent. @@ -319,7 +312,7 @@ public static class FriendlyPublishedContentExtensions /// The content. /// The ancestor of the content. public static IPublishedContent? Ancestor(this IPublishedContent content) - => content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content)); + => content.Ancestor(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the nearest ancestor of the content, of a specified content type. @@ -330,7 +323,7 @@ public static class FriendlyPublishedContentExtensions /// Does not consider the content itself. May return null. public static T? Ancestor(this IPublishedContent content) where T : class, IPublishedContent - => content.Ancestor(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); + => content.Ancestor(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Gets the content or its nearest ancestor, of a specified content type. @@ -341,7 +334,7 @@ public static class FriendlyPublishedContentExtensions /// May or may not return the content itself depending on its content type. May return null. public static T? AncestorOrSelf(this IPublishedContent content) where T : class, IPublishedContent - => content.AncestorOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); + => content.AncestorOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content)); /// /// Returns all DescendantsOrSelf of all content referenced @@ -358,7 +351,19 @@ public static class FriendlyPublishedContentExtensions /// public static IEnumerable DescendantsOrSelfOfType( this IEnumerable parentNodes, string docTypeAlias, string? culture = null) - => parentNodes.DescendantsOrSelfOfType(VariationContextAccessor, GetPublishedCache(parentNodes.First()), GetNavigationQueryService(parentNodes.First()), PublishStatusQueryService, docTypeAlias, culture); + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + if (parentNodesAsArray.Length == 0) + { + return []; + } + + return parentNodesAsArray.DescendantsOrSelfOfType( + GetNavigationQueryService(parentNodesAsArray.First()), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), + docTypeAlias, + culture); + } /// /// Returns all DescendantsOrSelf of all content referenced @@ -376,77 +381,88 @@ public static class FriendlyPublishedContentExtensions this IEnumerable parentNodes, string? culture = null) where T : class, IPublishedContent - => parentNodes.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(parentNodes.First()), GetNavigationQueryService(parentNodes.First()), PublishStatusQueryService, culture); + { + IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray(); + if (parentNodesAsArray.Length == 0) + { + return []; + } + + return parentNodesAsArray.DescendantsOrSelf( + GetNavigationQueryService(parentNodesAsArray.First()), + GetPublishedStatusFilteringService(parentNodesAsArray.First()), + culture); + } public static IEnumerable Descendants(this IPublishedContent content, string? culture = null) - => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static IEnumerable Descendants(this IPublishedContent content, int level, string? culture = null) - => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); public static IEnumerable DescendantsOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantsOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.DescendantsOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); public static IEnumerable Descendants(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static IEnumerable Descendants(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.Descendants(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.Descendants(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); public static IEnumerable DescendantsOrSelf( this IPublishedContent content, string? culture = null) - => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level, string? culture = null) - => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); public static IEnumerable DescendantsOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantsOrSelfOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.DescendantsOrSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static IEnumerable DescendantsOrSelf(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.DescendantsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.DescendantsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); public static IPublishedContent? Descendant(this IPublishedContent content, string? culture = null) - => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static IPublishedContent? Descendant(this IPublishedContent content, int level, string? culture = null) - => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); public static IPublishedContent? DescendantOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.DescendantOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); public static T? Descendant(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static T? Descendant(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.Descendant(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.Descendant(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); public static IPublishedContent DescendantOrSelf(this IPublishedContent content, string? culture = null) - => content.DescendantOrSelf(VariationContextAccessor, PublishStatusQueryService, culture); + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static IPublishedContent? DescendantOrSelf(this IPublishedContent content, int level, string? culture = null) - => content.DescendantOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); public static IPublishedContent? DescendantOrSelfOfType(this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.DescendantOrSelfOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.DescendantOrSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); public static T? DescendantOrSelf(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.DescendantOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static T? DescendantOrSelf(this IPublishedContent content, int level, string? culture = null) where T : class, IPublishedContent - => content.DescendantOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture); + => content.DescendantOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture); /// /// Gets the children of the content item. @@ -474,7 +490,7 @@ public static class FriendlyPublishedContentExtensions /// /// public static IEnumerable Children(this IPublishedContent content, string? culture = null) - => content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); /// /// Gets the children of the content, filtered by a predicate. @@ -493,7 +509,7 @@ public static class FriendlyPublishedContentExtensions this IPublishedContent content, Func predicate, string? culture = null) - => content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, predicate, culture); + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); /// /// Gets the children of the content, of any of the specified types. @@ -506,7 +522,7 @@ public static class FriendlyPublishedContentExtensions /// 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, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.ChildrenOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); /// /// Gets the children of the content, of a given content type. @@ -523,30 +539,30 @@ public static class FriendlyPublishedContentExtensions /// public static IEnumerable? Children(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static IPublishedContent? FirstChild(this IPublishedContent content, string? culture = null) - => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), 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, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.FirstChildOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); public static IPublishedContent? FirstChild(this IPublishedContent content, Func predicate, string? culture = null) - => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, predicate, culture); + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); public static IPublishedContent? FirstChild(this IPublishedContent content, Guid uniqueId, string? culture = null) - => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, uniqueId, culture); + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), uniqueId, culture); public static T? FirstChild(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); public static T? FirstChild(this IPublishedContent content, Func predicate, string? culture = null) where T : class, IPublishedContent - => content.FirstChild(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, predicate, culture); + => content.FirstChild(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture); /// /// Gets the siblings of the content. @@ -561,7 +577,7 @@ public static class FriendlyPublishedContentExtensions /// Note that in V7 this method also return the content node self. /// public static IEnumerable? Siblings(this IPublishedContent content, string? culture = null) - => content.Siblings(GetPublishedCache(content), GetNavigationQueryService(content), VariationContextAccessor, PublishStatusQueryService, culture); + => content.Siblings(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); /// /// Gets the siblings of the content, of a given content type. @@ -577,7 +593,7 @@ public static class FriendlyPublishedContentExtensions /// 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(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.SiblingsOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); /// /// Gets the siblings of the content, of a given content type. @@ -594,7 +610,7 @@ public static class FriendlyPublishedContentExtensions /// public static IEnumerable? Siblings(this IPublishedContent content, string? culture = null) where T : class, IPublishedContent - => content.Siblings(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.Siblings(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); /// /// Gets the siblings of the content including the node itself to indicate the position. @@ -608,7 +624,7 @@ public static class FriendlyPublishedContentExtensions public static IEnumerable? SiblingsAndSelf( this IPublishedContent content, string? culture = null) - => content.SiblingsAndSelf(GetPublishedCache(content), GetNavigationQueryService(content), VariationContextAccessor, PublishStatusQueryService, culture); + => content.SiblingsAndSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); /// /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. @@ -624,7 +640,7 @@ public static class FriendlyPublishedContentExtensions this IPublishedContent content, string contentTypeAlias, string? culture = null) - => content.SiblingsAndSelfOfType(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, contentTypeAlias, culture); + => content.SiblingsAndSelfOfType(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), contentTypeAlias, culture); /// /// Gets the siblings of the content including the node itself to indicate the position, of a given content type. @@ -638,7 +654,7 @@ public static class FriendlyPublishedContentExtensions /// 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(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture); + => content.SiblingsAndSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture); /// /// Gets the url of the content item. @@ -671,17 +687,15 @@ public static class FriendlyPublishedContentExtensions /// The children of the content. [Obsolete("This method is no longer used in Umbraco. The method will be removed in Umbraco 17.")] public static DataTable ChildrenAsTable(this IPublishedContent content, string contentTypeAliasFilter = "", string? culture = null) - => - content.ChildrenAsTable( - VariationContextAccessor, - GetPublishedCache(content), - GetNavigationQueryService(content), - ContentTypeService, - MediaTypeService, - MemberTypeService, - PublishedUrlProvider, - contentTypeAliasFilter, - culture); + => content.ChildrenAsTable( + GetNavigationQueryService(content), + GetPublishedStatusFilteringService(content), + ContentTypeService, + MediaTypeService, + MemberTypeService, + PublishedUrlProvider, + contentTypeAliasFilter, + culture); /// /// Gets the url for a media. diff --git a/src/Umbraco.Web.Common/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Web.Common/Extensions/PublishedContentExtensions.cs index d9ca747ba3..640d73ad10 100644 --- a/src/Umbraco.Web.Common/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/PublishedContentExtensions.cs @@ -17,22 +17,7 @@ public static class PublishedContentExtensions { #region Variations - /// - /// Gets the culture assigned to a document by domains, in the context of a current Uri. - /// - /// The document. - /// - /// The site domain helper. - /// 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. - /// - /// - [Obsolete("Please use the method taking all parameters. This overload will be removed in V17.")] + [Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")] public static string? GetCultureFromDomains( this IPublishedContent content, IUmbracoContextAccessor umbracoContextAccessor, @@ -43,6 +28,19 @@ public static class PublishedContentExtensions return DomainUtilities.GetCultureFromDomains(content.Id, content.Path, current, umbracoContext, siteDomainHelper); } + public static string? GetCultureFromDomains( + this IPublishedContent content, + IUmbracoContextAccessor umbracoContextAccessor, + ISiteDomainMapper siteDomainHelper, + IDomainCache domainCache, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + Uri? current = null) + { + IUmbracoContext umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + return DomainUtilities.GetCultureFromDomains(content.Id, content.Path, current, umbracoContext, siteDomainHelper, domainCache, publishedCache, navigationQueryService); + } + /// /// Gets the culture assigned to a document by domains, in the context of a current Uri. /// @@ -68,6 +66,7 @@ public static class PublishedContentExtensions IDomainCache domainCache, IPublishedCache publishedCache, INavigationQueryService navigationQueryService, + IPublishedStatusFilteringService publishedStatusFilteringService, Uri? current = null) { IUmbracoContext umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); diff --git a/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs b/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs index 57666cbc4f..ecad1a3484 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbLoginController.cs @@ -23,8 +23,8 @@ public class UmbLoginController : SurfaceController private readonly IMemberManager _memberManager; private readonly IMemberSignInManager _signInManager; private readonly ITwoFactorLoginService _twoFactorLoginService; - private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; + private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService; [ActivatorUtilitiesConstructor] public UmbLoginController( @@ -37,18 +37,75 @@ public class UmbLoginController : SurfaceController IMemberSignInManager signInManager, IMemberManager memberManager, ITwoFactorLoginService twoFactorLoginService, - IPublishedContentCache contentCache, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) : base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider) { _signInManager = signInManager; _memberManager = memberManager; _twoFactorLoginService = twoFactorLoginService; - _contentCache = contentCache; _navigationQueryService = navigationQueryService; + _publishedContentStatusFilteringService = publishedContentStatusFilteringService; } - [Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")] + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public UmbLoginController( + IUmbracoContextAccessor umbracoContextAccessor, + IUmbracoDatabaseFactory databaseFactory, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger profilingLogger, + IPublishedUrlProvider publishedUrlProvider, + IMemberSignInManager signInManager, + IMemberManager memberManager, + ITwoFactorLoginService twoFactorLoginService, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService, + IPublishedContentStatusFilteringService publishedContentStatusFilteringService) + : this( + umbracoContextAccessor, + databaseFactory, + services, + appCaches, + profilingLogger, + publishedUrlProvider, + signInManager, + memberManager, + twoFactorLoginService, + navigationQueryService, + publishedContentStatusFilteringService) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] + public UmbLoginController( + IUmbracoContextAccessor umbracoContextAccessor, + IUmbracoDatabaseFactory databaseFactory, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger profilingLogger, + IPublishedUrlProvider publishedUrlProvider, + IMemberSignInManager signInManager, + IMemberManager memberManager, + ITwoFactorLoginService twoFactorLoginService, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService) + : this( + umbracoContextAccessor, + databaseFactory, + services, + appCaches, + profilingLogger, + publishedUrlProvider, + signInManager, + memberManager, + twoFactorLoginService, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")] public UmbLoginController( IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, @@ -69,8 +126,8 @@ public class UmbLoginController : SurfaceController signInManager, memberManager, twoFactorLoginService, - StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -102,7 +159,7 @@ public class UmbLoginController : SurfaceController // If it's not a local URL we'll redirect to the root of the current site. return Redirect(Url.IsLocalUrl(model.RedirectUrl) ? model.RedirectUrl - : CurrentPage!.AncestorOrSelf(_contentCache, _navigationQueryService, 1)!.Url(PublishedUrlProvider)); + : CurrentPage!.AncestorOrSelf(_navigationQueryService, _publishedContentStatusFilteringService, 1)!.Url(PublishedUrlProvider)); } // Redirect to current URL by default. diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.cs index 6d1db8132f..d1ad93da3a 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentNavigationServiceTests.cs @@ -281,4 +281,47 @@ public partial class DocumentNavigationServiceTests : DocumentNavigationServiceT Assert.AreEqual(3, allSiblingsList.Count); }); } + + // a lot of structural querying assumes a specific order of descendants, so let's ensure that. + [Test] + public void Descendants_Are_In_Top_Down_Order_Of_Structure() + { + var result = DocumentNavigationQueryService.TryGetDescendantsKeysOrSelfKeys(Root.Key, out IEnumerable descendantsKeys); + Assert.IsTrue(result); + + var descendantsKeysAsArray = descendantsKeys.ToArray(); + Assert.AreEqual(9, descendantsKeysAsArray.Length); + + Assert.Multiple(() => + { + Assert.AreEqual(Root.Key, descendantsKeysAsArray[0]); + Assert.AreEqual(Child1.Key, descendantsKeysAsArray[1]); + Assert.AreEqual(Grandchild1.Key, descendantsKeysAsArray[2]); + Assert.AreEqual(Grandchild2.Key, descendantsKeysAsArray[3]); + Assert.AreEqual(Child2.Key, descendantsKeysAsArray[4]); + Assert.AreEqual(Grandchild3.Key, descendantsKeysAsArray[5]); + Assert.AreEqual(GreatGrandchild1.Key, descendantsKeysAsArray[6]); + Assert.AreEqual(Child3.Key, descendantsKeysAsArray[7]); + Assert.AreEqual(Grandchild4.Key, descendantsKeysAsArray[8]); + }); + } + + // a lot of structural querying assumes a specific order of ancestors, so let's ensure that. + [Test] + public void Ancestors_Are_In_Down_Top_Order() + { + var result = DocumentNavigationQueryService.TryGetAncestorsOrSelfKeys(GreatGrandchild1.Key, out IEnumerable ancestorsKeys); + Assert.IsTrue(result); + + var ancestorKeysAsArray = ancestorsKeys.ToArray(); + Assert.AreEqual(4, ancestorKeysAsArray.Length); + + Assert.Multiple(() => + { + Assert.AreEqual(GreatGrandchild1.Key, ancestorKeysAsArray[0]); + Assert.AreEqual(Grandchild3.Key, ancestorKeysAsArray[1]); + Assert.AreEqual(Child2.Key, ancestorKeysAsArray[2]); + Assert.AreEqual(Root.Key, ancestorKeysAsArray[3]); + }); + } } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs index 144f987da8..ae1e877e15 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs @@ -413,6 +413,6 @@ public class DomainAndUrlsTests : UmbracoIntegrationTest GetRequiredService>(), GetRequiredService(), GetRequiredService(), - GetRequiredService(), - GetRequiredService()).GetAwaiter().GetResult(); + GetRequiredService(), + GetRequiredService()).GetAwaiter().GetResult(); } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs index aa94d9147b..99ccbd7a61 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs @@ -5,7 +5,7 @@ using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services.Navigation; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.DeliveryApi; @@ -36,7 +36,11 @@ public class ContentBuilderTests : DeliveryApiTests .Setup(p => p.GetContentPath(It.IsAny(), It.IsAny())) .Returns((IPublishedContent c, string? culture) => $"url:{c.UrlSegment}"); - var routeBuilder = CreateContentRouteBuilder(apiContentRouteProvider.Object, CreateGlobalSettings()); + var navigationQueryServiceMock = new Mock(); + IEnumerable ancestorsKeys = []; + navigationQueryServiceMock.Setup(x => x.TryGetAncestorsKeys(key, out ancestorsKeys)).Returns(true); + + var routeBuilder = CreateContentRouteBuilder(apiContentRouteProvider.Object, CreateGlobalSettings(), navigationQueryService: navigationQueryServiceMock.Object); var builder = new ApiContentBuilder(new ApiContentNameProvider(), routeBuilder, CreateOutputExpansionStrategyAccessor()); var result = builder.Build(content.Object); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs index c0001bf067..9da66ba10c 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Models.DeliveryApi; 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.Services.Navigation; using Umbraco.Extensions; @@ -26,7 +27,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests var rootKey = Guid.NewGuid(); var root = SetupInvariantPublishedContent("The Root", rootKey, navigationQueryServiceMock); - var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, navigationQueryServiceMock.Object); var result = builder.Build(root); Assert.IsNotNull(result); Assert.AreEqual("/", result.Path); @@ -47,13 +48,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests var child = SetupInvariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root); IEnumerable ancestorsKeys = [rootKey]; - navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); + navigationQueryServiceMock.Setup(x => x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); - var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, contentCache: contentCache, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, navigationQueryServiceMock.Object, contentCache: contentCache); var result = builder.Build(child); Assert.IsNotNull(result); Assert.AreEqual("/the-child", result.Path); @@ -77,14 +78,14 @@ public class ContentRouteBuilderTests : DeliveryApiTests var grandchild = SetupInvariantPublishedContent("The Grandchild", grandchildKey, navigationQueryServiceMock, child); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); - Mock.Get(contentCache).Setup(x => x.GetById(grandchild.Key)).Returns(grandchild); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), grandchild.Key)).Returns(grandchild); IEnumerable ancestorsKeys = [childKey, rootKey]; navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(grandchildKey, out ancestorsKeys)).Returns(true); - var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, contentCache: contentCache, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, navigationQueryServiceMock.Object, contentCache: contentCache); var result = builder.Build(grandchild); Assert.IsNotNull(result); Assert.AreEqual("/the-child/the-grandchild", result.Path); @@ -104,13 +105,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests var child = SetupVariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); IEnumerable ancestorsKeys = [rootKey]; navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); - var builder = CreateApiContentRouteBuilder(false, contentCache: contentCache, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(false, navigationQueryServiceMock.Object, contentCache: contentCache); var result = builder.Build(child, "en-us"); Assert.IsNotNull(result); Assert.AreEqual("/the-child-en-us", result.Path); @@ -136,13 +137,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests var child = SetupInvariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); IEnumerable ancestorsKeys = [rootKey]; navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); - var builder = CreateApiContentRouteBuilder(false, contentCache: contentCache, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(false, navigationQueryServiceMock.Object, contentCache: contentCache); var result = builder.Build(child, "en-us"); Assert.IsNotNull(result); Assert.AreEqual("/the-child", result.Path); @@ -168,13 +169,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests var child = SetupVariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); IEnumerable ancestorsKeys = [rootKey]; navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); - var builder = CreateApiContentRouteBuilder(false, contentCache: contentCache, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(false, navigationQueryServiceMock.Object, contentCache: contentCache); var result = builder.Build(child, "en-us"); Assert.IsNotNull(result); Assert.AreEqual("/the-child-en-us", result.Path); @@ -197,7 +198,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests var content = new Mock(); content.SetupGet(c => c.ItemType).Returns(itemType); - var builder = CreateApiContentRouteBuilder(true); + var builder = CreateApiContentRouteBuilder(true, Mock.Of()); Assert.Throws(() => builder.Build(content.Object)); } @@ -236,9 +237,9 @@ public class ContentRouteBuilderTests : DeliveryApiTests var grandchild = SetupInvariantPublishedContent("The Grandchild", grandchildKey, navigationQueryServiceMock, child); var contentCache = Mock.Of(); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); - Mock.Get(contentCache).Setup(x => x.GetById(grandchild.Key)).Returns(grandchild); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), grandchild.Key)).Returns(grandchild); IEnumerable grandchildAncestorsKeys = [childKey, rootKey]; navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(grandchildKey, out grandchildAncestorsKeys)).Returns(true); @@ -262,17 +263,17 @@ public class ContentRouteBuilderTests : DeliveryApiTests var rootKey = Guid.NewGuid(); var root = SetupInvariantPublishedContent("The Root", rootKey, navigationQueryServiceMock); - IEnumerable rootKeys = rootKey.Yield(); - navigationQueryServiceMock.Setup(x => x.TryGetRootKeys(out rootKeys)).Returns(true); - var childKey = Guid.NewGuid(); var child = SetupInvariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root, false); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(true, root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(true, child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); - var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, contentCache: contentCache, isPreview: true, navigationQueryService: navigationQueryServiceMock.Object); + IEnumerable ancestorsKeys = [rootKey]; + navigationQueryServiceMock.Setup(x => x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); + + var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, navigationQueryServiceMock.Object, contentCache: contentCache, isPreview: true); var result = builder.Build(child); Assert.IsNotNull(result); Assert.AreEqual($"/{Constants.DeliveryApi.Routing.PreviewContentPathPrefix}{childKey:D}", result.Path); @@ -289,17 +290,17 @@ public class ContentRouteBuilderTests : DeliveryApiTests var rootKey = Guid.NewGuid(); var root = SetupInvariantPublishedContent("The Root", rootKey, navigationQueryServiceMock); - IEnumerable rootKeys = rootKey.Yield(); - navigationQueryServiceMock.Setup(x => x.TryGetRootKeys(out rootKeys)).Returns(true); - var childKey = Guid.NewGuid(); var child = SetupInvariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root, false); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(true, root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(true, child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); - var builder = CreateApiContentRouteBuilder(true, addTrailingSlash, contentCache: contentCache, isPreview: true, navigationQueryService: navigationQueryServiceMock.Object); + IEnumerable ancestorsKeys = [rootKey]; + navigationQueryServiceMock.Setup(x => x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); + + var builder = CreateApiContentRouteBuilder(true, navigationQueryServiceMock.Object, addTrailingSlash, contentCache: contentCache, isPreview: true); var result = builder.Build(child); Assert.IsNotNull(result); Assert.AreEqual(addTrailingSlash, result.Path.EndsWith("/")); @@ -314,9 +315,6 @@ public class ContentRouteBuilderTests : DeliveryApiTests var rootKey = Guid.NewGuid(); var root = SetupInvariantPublishedContent("The Root", rootKey, navigationQueryServiceMock, published: false); - IEnumerable rootKeys = rootKey.Yield(); - navigationQueryServiceMock.Setup(x => x.TryGetRootKeys(out rootKeys)).Returns(true); - var childKey = Guid.NewGuid(); var child = SetupInvariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root); @@ -324,13 +322,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests requestPreviewServiceMock.Setup(m => m.IsPreview()).Returns(isPreview); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); IEnumerable ancestorsKeys = [rootKey]; navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); - var builder = CreateApiContentRouteBuilder(true, contentCache: contentCache, isPreview: isPreview, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(true, navigationQueryServiceMock.Object, contentCache: contentCache, isPreview: isPreview); var result = builder.Build(child); if (isPreview) @@ -358,8 +356,8 @@ public class ContentRouteBuilderTests : DeliveryApiTests var child = SetupInvariantPublishedContent("The Child", childKey, navigationQueryServiceMock, root); var contentCache = CreatePublishedContentCache("#"); - Mock.Get(contentCache).Setup(x => x.GetById(root.Key)).Returns(root); - Mock.Get(contentCache).Setup(x => x.GetById(child.Key)).Returns(child); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), root.Key)).Returns(root); + Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny(), child.Key)).Returns(child); var apiContentPathProvider = new Mock(); apiContentPathProvider @@ -369,7 +367,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests IEnumerable ancestorsKeys = [rootKey]; navigationQueryServiceMock.Setup(x=>x.TryGetAncestorsKeys(childKey, out ancestorsKeys)).Returns(true); - var builder = CreateApiContentRouteBuilder(true, contentCache: contentCache, apiContentPathProvider: apiContentPathProvider.Object, navigationQueryService: navigationQueryServiceMock.Object); + var builder = CreateApiContentRouteBuilder(true, navigationQueryServiceMock.Object, contentCache: contentCache, apiContentPathProvider: apiContentPathProvider.Object); var result = builder.Build(root); Assert.NotNull(result); Assert.AreEqual("/my-custom-path-for-the-root", result.Path); @@ -432,7 +430,12 @@ public class ContentRouteBuilderTests : DeliveryApiTests string Url(IPublishedContent content, string? culture) { - var ancestorsOrSelf = content.AncestorsOrSelf(variantContextAccessor, contentCache, navigationQueryService, PublishStatusQueryService).ToArray(); + var publishedContentStatusFilteringService = new PublishedContentStatusFilteringService( + variantContextAccessor, + PublishStatusQueryService, + Mock.Of(), + contentCache); + var ancestorsOrSelf = content.AncestorsOrSelf(navigationQueryService, publishedContentStatusFilteringService).ToArray(); return ancestorsOrSelf.All(c => c.IsPublished(culture)) ? string.Join("/", ancestorsOrSelf.Reverse().Skip(hideTopLevelNodeFromPath ? 1 : 0).Select(c => c.UrlSegment(variantContextAccessor, culture))).EnsureStartsWith("/") : "#"; @@ -448,7 +451,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests private IApiContentPathProvider SetupApiContentPathProvider(bool hideTopLevelNodeFromPath, IPublishedContentCache contentCache, IDocumentNavigationQueryService navigationQueryService) => new ApiContentPathProvider(SetupPublishedUrlProvider(hideTopLevelNodeFromPath, contentCache, navigationQueryService)); - private ApiContentRouteBuilder CreateApiContentRouteBuilder(bool hideTopLevelNodeFromPath, bool addTrailingSlash = false, bool isPreview = false, IPublishedContentCache? contentCache = null, IApiContentPathProvider? apiContentPathProvider = null, IDocumentNavigationQueryService navigationQueryService = null) + private ApiContentRouteBuilder CreateApiContentRouteBuilder(bool hideTopLevelNodeFromPath, IDocumentNavigationQueryService navigationQueryService, bool addTrailingSlash = false, bool isPreview = false, IPublishedContentCache? contentCache = null, IApiContentPathProvider? apiContentPathProvider = null) { var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = addTrailingSlash }; var requestHandlerSettingsMonitorMock = new Mock>(); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs index 2764dd21d3..08c09c92f2 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs @@ -113,13 +113,14 @@ public class DeliveryApiTests content.SetupGet(c => c.ContentType).Returns(contentType); content.SetupGet(c => c.Properties).Returns(properties); content.SetupGet(c => c.ItemType).Returns(contentType.ItemType); + content.SetupGet(c => c.Level).Returns(1); content.Setup(c => c.IsPublished(It.IsAny())).Returns(true); } protected string DefaultUrlSegment(string name, string? culture = null) => $"{name.ToLowerInvariant().Replace(" ", "-")}{(culture.IsNullOrWhiteSpace() ? string.Empty : $"-{culture}")}"; - protected ApiContentRouteBuilder CreateContentRouteBuilder( + protected virtual ApiContentRouteBuilder CreateContentRouteBuilder( IApiContentPathProvider contentPathProvider, IOptions globalSettings, IVariationContextAccessor? variationContextAccessor = null, diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs index fc232d6c95..4b6c052392 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs @@ -21,7 +21,7 @@ public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTest var contentNameProvider = new ApiContentNameProvider(); var apiUrProvider = new ApiMediaUrlProvider(PublishedUrlProvider); - routeBuilder = routeBuilder ?? CreateContentRouteBuilder(ApiContentPathProvider, CreateGlobalSettings()); + routeBuilder ??= CreateContentRouteBuilder(ApiContentPathProvider, CreateGlobalSettings()); return new MultiNodeTreePickerValueConverter( Mock.Of(), Mock.Of(), diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PropertyValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PropertyValueConverterTests.cs index 7f952c35da..2751cf6f13 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PropertyValueConverterTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PropertyValueConverterTests.cs @@ -1,9 +1,12 @@ +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services.Navigation; namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.DeliveryApi; @@ -31,6 +34,8 @@ public class PropertyValueConverterTests : DeliveryApiTests protected VariationContext VariationContext { get; } = new(); + protected Mock DocumentNavigationQueryServiceMock { get; private set; } + [SetUp] public override void Setup() { @@ -76,6 +81,10 @@ public class PropertyValueConverterTests : DeliveryApiTests .Returns("the-media-url"); PublishedUrlProvider = PublishedUrlProviderMock.Object; ApiContentPathProvider = new ApiContentPathProvider(PublishedUrlProvider); + + DocumentNavigationQueryServiceMock = new Mock(); + IEnumerable ancestorsKeys = []; + DocumentNavigationQueryServiceMock.Setup(x => x.TryGetAncestorsKeys(contentKey, out ancestorsKeys)).Returns(true); } protected Mock SetupPublishedContent(string name, Guid key, PublishedItemType itemType, IPublishedContentType contentType) @@ -109,4 +118,28 @@ public class PropertyValueConverterTests : DeliveryApiTests .Setup(pcc => pcc.GetById(It.IsAny(), media.Key)) .Returns(media); } + + protected override ApiContentRouteBuilder CreateContentRouteBuilder( + IApiContentPathProvider contentPathProvider, + IOptions globalSettings, + IVariationContextAccessor? variationContextAccessor = null, + IRequestPreviewService? requestPreviewService = null, + IOptionsMonitor? requestHandlerSettingsMonitor = null, + IPublishedContentCache? contentCache = null, + IDocumentNavigationQueryService? navigationQueryService = null, + IPublishStatusQueryService? publishStatusQueryService = null) + { + contentCache ??= PublishedContentCacheMock.Object; + navigationQueryService ??= DocumentNavigationQueryServiceMock.Object; + + return base.CreateContentRouteBuilder( + contentPathProvider, + globalSettings, + variationContextAccessor, + requestPreviewService, + requestHandlerSettingsMonitor, + contentCache, + navigationQueryService, + publishStatusQueryService); + } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs index d7f86ff11e..11872abbfb 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using AutoFixture.NUnit3; +using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; @@ -30,16 +31,14 @@ public class ContentFinderByUrlAliasTests [Frozen] IPublishedContentCache publishedContentCache, [Frozen] IUmbracoContextAccessor umbracoContextAccessor, [Frozen] IUmbracoContext umbracoContext, - [Frozen] IVariationContextAccessor variationContextAccessor, - [Frozen] IPublishStatusQueryService publishStatusQueryService, + [Frozen] IDocumentNavigationQueryService documentNavigationQueryService, + [Frozen] IPublishedContentStatusFilteringService publishedContentStatusFilteringService, IFileService fileService, - ContentFinderByUrlAlias sut, IPublishedContent[] rootContents, IPublishedProperty urlProperty) { // Arrange var absoluteUrl = "http://localhost" + relativeUrl; - var variationContext = new VariationContext(); var contentItem = rootContents[0]; Mock.Get(umbracoContextAccessor).Setup(x => x.TryGetUmbracoContext(out umbracoContext)).Returns(true); @@ -47,13 +46,22 @@ public class ContentFinderByUrlAliasTests Mock.Get(publishedContentCache).Setup(x => x.GetAtRoot(null)).Returns(rootContents); Mock.Get(contentItem).Setup(x => x.Id).Returns(nodeMatch); Mock.Get(contentItem).Setup(x => x.GetProperty(Constants.Conventions.Content.UrlAlias)).Returns(urlProperty); + Mock.Get(contentItem).Setup(x => x.ItemType).Returns(PublishedItemType.Content); Mock.Get(urlProperty).Setup(x => x.GetValue(null, null)).Returns(relativeUrl); - Mock.Get(variationContextAccessor).Setup(x => x.VariationContext).Returns(variationContext); - Mock.Get(publishStatusQueryService).Setup(x => x.IsDocumentPublished(It.IsAny(), It.IsAny())).Returns(true); + IEnumerable descendantKeys = []; + Mock.Get(documentNavigationQueryService).Setup(x => x.TryGetDescendantsKeys(It.IsAny(), out descendantKeys)).Returns(true); + + Mock.Get(publishedContentStatusFilteringService).Setup(x => x.FilterAvailable(It.IsAny>(), It.IsAny())).Returns([]); var publishedRequestBuilder = new PublishedRequestBuilder(new Uri(absoluteUrl, UriKind.Absolute), fileService); // Act + var sut = new ContentFinderByUrlAlias( + Mock.Of>(), + Mock.Of(), + umbracoContextAccessor, + documentNavigationQueryService, + publishedContentStatusFilteringService); var result = await sut.TryFindContent(publishedRequestBuilder); Assert.IsTrue(result); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs new file mode 100644 index 0000000000..674c06d643 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/PublishStatus/PublishedContentStatusFilteringServiceTests.cs @@ -0,0 +1,357 @@ +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.Navigation; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Services.PublishStatus; + +[TestFixture] +public partial class PublishedContentStatusFilteringServiceTests +{ + [Test] + public void FilterAvailable_Invariant_ForNonPreview_YieldsPublishedItems() + { + var (sut, items) = SetupInvariant(false); + + var children = sut.FilterAvailable(items.Keys, null).ToArray(); + Assert.AreEqual(5, children.Length); + Assert.Multiple(() => + { + Assert.AreEqual(0, children[0].Id); + Assert.AreEqual(2, children[1].Id); + Assert.AreEqual(4, children[2].Id); + Assert.AreEqual(6, children[3].Id); + Assert.AreEqual(8, children[4].Id); + }); + } + + [Test] + public void FilterAvailable_Invariant_ForPreview_YieldsUnpublishedItems() + { + var (sut, items) = SetupInvariant(true); + + var children = sut.FilterAvailable(items.Keys, null).ToArray(); + Assert.AreEqual(10, children.Length); + for (var i = 0; i < 10; i++) + { + Assert.AreEqual(i, children[i].Id); + } + } + + [TestCase("da-DK", 3)] + [TestCase("en-US", 4)] + public void FilterAvailable_Variant_ForNonPreview_YieldsPublishedItemsInCulture(string culture, int expectedNumberOfChildren) + { + var (sut, items) = SetupVariant(false, culture); + + var children = sut.FilterAvailable(items.Keys, culture).ToArray(); + Assert.AreEqual(expectedNumberOfChildren, children.Length); + + // IDs 0 through 3 exist in both en-US and da-DK - only even IDs are published + Assert.Multiple(() => + { + Assert.AreEqual(0, children[0].Id); + Assert.AreEqual(2, children[1].Id); + }); + + // IDs 4 through 6 exist only in en-US - only even IDs are published + if (culture == "en-US") + { + Assert.AreEqual(4, children[2].Id); + Assert.AreEqual(6, children[3].Id); + } + + // IDs 7 through 9 exist only in da-DK - only even IDs are published + if (culture == "da-DK") + { + Assert.AreEqual(8, children[2].Id); + } + } + + [TestCase("da-DK")] + [TestCase("en-US")] + public void FilterAvailable_Variant_ForPreview_YieldsUnpublishedItemsInCulture(string culture) + { + var (sut, items) = SetupVariant(true, culture); + + var children = sut.FilterAvailable(items.Keys, culture).ToArray(); + Assert.AreEqual(7, children.Length); + + // IDs 0 through 3 exist in both en-US and da-DK + Assert.Multiple(() => + { + Assert.AreEqual(0, children[0].Id); + Assert.AreEqual(1, children[1].Id); + Assert.AreEqual(2, children[2].Id); + Assert.AreEqual(3, children[3].Id); + }); + + // IDs 4 through 6 exist only in en-US + if (culture == "en-US") + { + Assert.AreEqual(4, children[4].Id); + Assert.AreEqual(5, children[5].Id); + Assert.AreEqual(6, children[6].Id); + } + + // IDs 7 through 9 exist only in da-DK + if (culture == "da-DK") + { + Assert.AreEqual(7, children[4].Id); + Assert.AreEqual(8, children[5].Id); + Assert.AreEqual(9, children[6].Id); + } + } + + [TestCase("da-DK")] + [TestCase("en-US")] + public void FilterAvailable_MixedVariance_ForNonPreview_YieldsPublishedItemsInCultureOrInvariant(string culture) + { + var (sut, items) = SetupMixedVariance(false, culture); + + var children = sut.FilterAvailable(items.Keys, culture).ToArray(); + Assert.AreEqual(4, children.Length); + + // IDs 0 through 2 are invariant - only even IDs are published + Assert.Multiple(() => + { + Assert.AreEqual(0, children[0].Id); + Assert.AreEqual(2, children[1].Id); + }); + + // IDs 3 through 5 exist in both en-US and da-DK - only even IDs are published + Assert.Multiple(() => + { + Assert.AreEqual(4, children[2].Id); + }); + + // IDs 6 and 7 exist only in en-US - only even IDs are published + if (culture == "en-US") + { + Assert.AreEqual(6, children[3].Id); + } + + // IDs 8 and 9 exist only in da-DK - only even IDs are published + if (culture == "da-DK") + { + Assert.AreEqual(8, children[3].Id); + } + } + + [TestCase("da-DK")] + [TestCase("en-US")] + public void FilterAvailable_MixedVariance_FoPreview_YieldsPublishedItemsInCultureOrInvariant(string culture) + { + var (sut, items) = SetupMixedVariance(true, culture); + + var children = sut.FilterAvailable(items.Keys, culture).ToArray(); + Assert.AreEqual(8, children.Length); + + // IDs 0 through 2 are invariant + Assert.Multiple(() => + { + Assert.AreEqual(0, children[0].Id); + Assert.AreEqual(1, children[1].Id); + Assert.AreEqual(2, children[2].Id); + }); + + // IDs 3 through 5 exist in both en-US and da-DK + Assert.Multiple(() => + { + Assert.AreEqual(3, children[3].Id); + Assert.AreEqual(4, children[4].Id); + Assert.AreEqual(5, children[5].Id); + }); + + // IDs 6 and 7 exist only in en-US + if (culture == "en-US") + { + Assert.AreEqual(6, children[6].Id); + Assert.AreEqual(7, children[7].Id); + } + + // IDs 8 and 9 exist only in da-DK + if (culture == "da-DK") + { + Assert.AreEqual(8, children[6].Id); + Assert.AreEqual(9, children[7].Id); + } + } + + // sets up invariant test data: + // - 10 documents with IDs 0 through 9 + // - even IDs (0, 2, ...) are published, odd are unpublished + private (PublishedContentStatusFilteringService PublishedContentStatusFilteringService, Dictionary Items) SetupInvariant(bool forPreview) + { + var contentType = new Mock(); + contentType.SetupGet(c => c.Variations).Returns(ContentVariation.Nothing); + + var items = new Dictionary(); + for (var i = 0; i < 10; i++) + { + var content = new Mock(); + + var key = Guid.NewGuid(); + content.SetupGet(c => c.Key).Returns(key); + content.SetupGet(c => c.ContentType).Returns(contentType.Object); + content.SetupGet(c => c.Cultures).Returns(new Dictionary()); + content.SetupGet(c => c.Id).Returns(i); + + items[key] = content.Object; + } + + var publishedContentCache = SetupPublishedContentCache(forPreview, items); + var previewService = SetupPreviewService(forPreview); + var publishStatusQueryService = SetupPublishStatusQueryService(items); + var variationContextAccessor = SetupVariantContextAccessor(null); + + return ( + new PublishedContentStatusFilteringService( + variationContextAccessor, + publishStatusQueryService, + previewService, + publishedContentCache), + items); + } + + // sets up variant test data: + // - 10 documents with IDs 0 through 9 + // - IDs 0 through 3 exist in both en-US and da-DK + // - IDs 4 through 6 exist only in en-US + // - IDs 7 through 9 exist only in da-DK + // - even IDs (0, 2, ...) are published, odd are unpublished + private (PublishedContentStatusFilteringService PublishedContentStatusFilteringService, Dictionary Items) SetupVariant(bool forPreview, string requestCulture) + { + var contentType = new Mock(); + contentType.SetupGet(c => c.Variations).Returns(ContentVariation.Culture); + + var items = new Dictionary(); + for (var i = 0; i < 10; i++) + { + var content = new Mock(); + + var key = Guid.NewGuid(); + string[] cultures = i <= 3 + ? ["da-DK", "en-US"] + : i <= 6 + ? ["en-US"] + : ["da-DK"]; + var cultureDictionary = cultures.ToDictionary(culture => culture, culture => new PublishedCultureInfo(culture, culture, $"{i}-{culture}", DateTime.MinValue)); + content.SetupGet(c => c.Key).Returns(key); + content.SetupGet(c => c.ContentType).Returns(contentType.Object); + content.SetupGet(c => c.Cultures).Returns(cultureDictionary); + content.SetupGet(c => c.Id).Returns(i); + + items[key] = content.Object; + } + + var publishedContentCache = SetupPublishedContentCache(forPreview, items); + var previewService = SetupPreviewService(forPreview); + var publishStatusQueryService = SetupPublishStatusQueryService(items); + var variationContextAccessor = SetupVariantContextAccessor(requestCulture); + + return ( + new PublishedContentStatusFilteringService( + variationContextAccessor, + publishStatusQueryService, + previewService, + publishedContentCache), + items); + } + + // sets up mixed variant test data: + // - 10 documents with IDs 0 through 9 + // - IDs 0 through 2 are invariant + // - IDs 3 through 5 exist in both en-US and da-DK + // - IDs 6 and 7 exist only in en-US + // - IDs 8 and 9 exist only in da-DK + // - even IDs (0, 2, ...) are published, odd are unpublished + private (PublishedContentStatusFilteringService PublishedContentStatusFilteringService, Dictionary Items) SetupMixedVariance(bool forPreview, string requestCulture) + { + var invariantContentType = new Mock(); + invariantContentType.SetupGet(c => c.Variations).Returns(ContentVariation.Nothing); + + var variantContentType = new Mock(); + variantContentType.SetupGet(c => c.Variations).Returns(ContentVariation.Culture); + + var items = new Dictionary(); + for (var i = 0; i < 10; i++) + { + var content = new Mock(); + var contentType = i <= 2 + ? invariantContentType + : variantContentType; + + var key = Guid.NewGuid(); + string[] cultures = i <= 2 + ? [] + : i <= 5 + ? ["da-DK", "en-US"] + : i <= 7 + ? ["en-US"] + : ["da-DK"]; + var cultureDictionary = cultures.ToDictionary(culture => culture, culture => new PublishedCultureInfo(culture, culture, $"{i}-{culture}", DateTime.MinValue)); + content.SetupGet(c => c.Key).Returns(key); + content.SetupGet(c => c.ContentType).Returns(contentType.Object); + content.SetupGet(c => c.Cultures).Returns(cultureDictionary); + content.SetupGet(c => c.Id).Returns(i); + + items[key] = content.Object; + } + + var publishedContentCache = SetupPublishedContentCache(forPreview, items); + var previewService = SetupPreviewService(forPreview); + var publishStatusQueryService = SetupPublishStatusQueryService(items); + var variationContextAccessor = SetupVariantContextAccessor(requestCulture); + + return ( + new PublishedContentStatusFilteringService( + variationContextAccessor, + publishStatusQueryService, + previewService, + publishedContentCache), + items); + } + + private IPublishStatusQueryService SetupPublishStatusQueryService(Dictionary items) + => SetupPublishStatusQueryService(items, id => id % 2 == 0); + + private IPublishStatusQueryService SetupPublishStatusQueryService(Dictionary items, Func idIsPublished) + { + var publishStatusQueryService = new Mock(); + publishStatusQueryService + .Setup(p => p.IsDocumentPublished(It.IsAny(), It.IsAny())) + .Returns((Guid key, string culture) => items + .TryGetValue(key, out var item) + && idIsPublished(item.Id) + && (item.ContentType.VariesByCulture() is false || item.Cultures.ContainsKey(culture))); + return publishStatusQueryService.Object; + } + + private IPreviewService SetupPreviewService(bool forPreview) + { + var previewService = new Mock(); + previewService.Setup(p => p.IsInPreview()).Returns(forPreview); + return previewService.Object; + } + + private IVariationContextAccessor SetupVariantContextAccessor(string? requestCulture) + { + var variationContextAccessor = new Mock(); + variationContextAccessor.SetupGet(v => v.VariationContext).Returns(new VariationContext(requestCulture)); + return variationContextAccessor.Object; + } + + private IPublishedContentCache SetupPublishedContentCache(bool forPreview, Dictionary items) + { + var publishedContentCache = new Mock(); + publishedContentCache + .Setup(c => c.GetById(forPreview, It.IsAny())) + .Returns((bool preview, Guid key) => items.TryGetValue(key, out var item) ? item : null); + return publishedContentCache.Object; + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs index 1a4a73f800..399633da2a 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs @@ -98,9 +98,8 @@ public class HtmlImageSourceParserTests new UrlProviderCollection(() => Enumerable.Empty()), new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }), Mock.Of(), - Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of()); using (var reference = umbracoContextFactory.EnsureUmbracoContext()) { diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs index ad2fb0b231..d1e5e0f494 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs @@ -222,8 +222,12 @@ public class HtmlLocalLinkParserTests var webRoutingSettings = new WebRoutingSettings(); var navigationQueryService = new Mock(); - Guid? parentKey = null; - navigationQueryService.Setup(x => x.TryGetParentKey(It.IsAny(), out parentKey)).Returns(true); + // Guid? parentKey = null; + // navigationQueryService.Setup(x => x.TryGetParentKey(It.IsAny(), out parentKey)).Returns(true); + IEnumerable ancestorKeys = []; + navigationQueryService.Setup(x => x.TryGetAncestorsKeys(It.IsAny(), out ancestorKeys)).Returns(true); + + var publishedContentStatusFilteringService = new Mock(); using (var reference = umbracoContextFactory.EnsureUmbracoContext()) { @@ -246,9 +250,8 @@ public class HtmlLocalLinkParserTests new UrlProviderCollection(() => new[] { contentUrlProvider.Object }), new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }), Mock.Of(), - contentCache.Object, navigationQueryService.Object, - publishStatusQueryService.Object); + publishedContentStatusFilteringService.Object); var linkParser = new HtmlLocalLinkParser(publishedUrlProvider); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj index 8661e94e37..0a1c88c5b8 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Tests.UnitTests.csproj @@ -3,7 +3,7 @@ true Umbraco.Cms.Tests.UnitTests - + $(WarningsNotAsErrors),SYSLIB0013,CS0618,CS1998,SA1117,CS0067,CA1822,CA1416,IDE0028,SA1401,SA1405,IDE0060,ASP0019,CS0114,CS0661,CS0659,CS0414,CS0252,CS0612,IDE1006 - +