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>
This commit is contained in:
@@ -16,12 +16,18 @@ internal sealed class ApiMediaQueryService : IApiMediaQueryService
|
||||
private readonly IPublishedMediaCache _publishedMediaCache;
|
||||
private readonly ILogger<ApiMediaQueryService> _logger;
|
||||
private readonly IMediaNavigationQueryService _mediaNavigationQueryService;
|
||||
private readonly IPublishedMediaStatusFilteringService _publishedMediaStatusFilteringService;
|
||||
|
||||
public ApiMediaQueryService(IPublishedMediaCache publishedMediaCache, ILogger<ApiMediaQueryService> logger, IMediaNavigationQueryService mediaNavigationQueryService)
|
||||
public ApiMediaQueryService(
|
||||
IPublishedMediaCache publishedMediaCache,
|
||||
ILogger<ApiMediaQueryService> logger,
|
||||
IMediaNavigationQueryService mediaNavigationQueryService,
|
||||
IPublishedMediaStatusFilteringService publishedMediaStatusFilteringService)
|
||||
{
|
||||
_publishedMediaCache = publishedMediaCache;
|
||||
_logger = logger;
|
||||
_mediaNavigationQueryService = mediaNavigationQueryService;
|
||||
_publishedMediaStatusFilteringService = publishedMediaStatusFilteringService;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -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<IPublishedContent>();
|
||||
return parent?.Children(_mediaNavigationQueryService, _publishedMediaStatusFilteringService) ?? Array.Empty<IPublishedContent>();
|
||||
}
|
||||
|
||||
private IEnumerable<IPublishedContent>? ApplyFilters(IEnumerable<IPublishedContent> source, IEnumerable<string> filters)
|
||||
|
||||
@@ -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<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
_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<IPublishedContent>()
|
||||
: rootContent.ChildrenOfType(_variationContextAccessor, _contentCache, _documentNavigationQueryService, model.DocumentTypeAlias);
|
||||
: rootContent.ChildrenOfType(_documentNavigationQueryService, _publishedContentStatusFilteringService, model.DocumentTypeAlias);
|
||||
}
|
||||
|
||||
queryExpression.Append(".Children()");
|
||||
return rootContent == null
|
||||
? Enumerable.Empty<IPublishedContent>()
|
||||
: rootContent.Children(_variationContextAccessor, _contentCache, _documentNavigationQueryService);
|
||||
: rootContent.Children(_documentNavigationQueryService, _publishedContentStatusFilteringService);
|
||||
}
|
||||
|
||||
private IEnumerable<IPublishedContent> ApplyFiltering(IEnumerable<TemplateQueryExecuteFilterPresentationModel>? filters, IEnumerable<IPublishedContent> contentQuery, StringBuilder queryExpression)
|
||||
|
||||
@@ -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> 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<Guid> rootKeys);
|
||||
IEnumerable<IPublishedContent> rootContent = rootKeys.Select(x => _contentCache.GetById(true, x)).WhereNotNull();
|
||||
if (_navigationQueryService.TryGetAncestorsKeys(content.Key, out IEnumerable<Guid> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,6 +387,9 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
Services.AddUnique<IPublishStatusManagementService>(x => x.GetRequiredService<PublishStatusService>());
|
||||
Services.AddUnique<IPublishStatusQueryService>(x => x.GetRequiredService<PublishStatusService>());
|
||||
|
||||
Services.AddUnique<IPublishedContentStatusFilteringService, PublishedContentStatusFilteringService>();
|
||||
Services.AddUnique<IPublishedMediaStatusFilteringService, PublishedMediaStatusFilteringService>();
|
||||
|
||||
// Register a noop IHtmlSanitizer & IMarkdownSanitizer to be replaced
|
||||
Services.AddUnique<IHtmlSanitizer, NoopHtmlSanitizer>();
|
||||
Services.AddUnique<IMarkdownSanitizer, NoopMarkdownSanitizer>();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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); }
|
||||
|
||||
<div class="sitemap">
|
||||
@* 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();
|
||||
|
||||
|
||||
@@ -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> 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> 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> contentSettings)
|
||||
: this(
|
||||
publishedRouter,
|
||||
umbracoContextAccessor,
|
||||
languageService,
|
||||
localizedTextService,
|
||||
contentService,
|
||||
variationContextAccessor,
|
||||
loggerFactory,
|
||||
uriUtility,
|
||||
publishedUrlProvider,
|
||||
navigationQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>(),
|
||||
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<IContent>(),
|
||||
_uriUtility,
|
||||
_publishedUrlProvider,
|
||||
_publishedContentCache,
|
||||
_navigationQueryService)).ToArray();
|
||||
_navigationQueryService,
|
||||
_publishedContentStatusFilteringService)).ToArray();
|
||||
|
||||
|
||||
EventMessages eventMessages = _eventMessagesFactory.Get();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -90,23 +90,23 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
private IEnumerable<IPublishedContent> GetChildren()
|
||||
{
|
||||
INavigationQueryService? navigationQueryService;
|
||||
IPublishedCache? publishedCache;
|
||||
IPublishedStatusFilteringService? publishedStatusFilteringService;
|
||||
|
||||
switch (ContentType.ItemType)
|
||||
{
|
||||
case PublishedItemType.Content:
|
||||
publishedCache = StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>();
|
||||
navigationQueryService = StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>();
|
||||
publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>();
|
||||
break;
|
||||
case PublishedItemType.Media:
|
||||
publishedCache = StaticServiceProvider.Instance.GetRequiredService<IPublishedMediaCache>();
|
||||
navigationQueryService = StaticServiceProvider.Instance.GetRequiredService<IMediaNavigationQueryService>();
|
||||
publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService<IPublishedMediaStatusFilteringService>();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Level is not implemented for " + ContentType.ItemType);
|
||||
}
|
||||
|
||||
return this.Children(_variationContextAccessor, publishedCache, navigationQueryService);
|
||||
return this.Children(navigationQueryService, publishedStatusFilteringService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ public class PublishedValueFallback : IPublishedValueFallback
|
||||
IPublishedProperty? property; // if we are here, content's property has no value
|
||||
do
|
||||
{
|
||||
content = content?.Parent<IPublishedContent>(StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(), StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>());
|
||||
content = content?.Parent<IPublishedContent>(StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(), StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>());
|
||||
|
||||
IPublishedPropertyType? propertyType = content?.ContentType.GetPropertyType(alias);
|
||||
|
||||
|
||||
@@ -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<IPublishedContent>(StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(), StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>());
|
||||
public IPublishedContent? Parent => this.Parent<IPublishedContent>(StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(), StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>());
|
||||
|
||||
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<IPublishedContent> Children => this.Children(
|
||||
StaticServiceProvider.Instance.GetRequiredService<IVariationContextAccessor>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>());
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>());
|
||||
|
||||
public IEnumerable<IPublishedContent> 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<IPublishedContent>(StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(), StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>());
|
||||
content = content.Parent<IPublishedContent>(StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(), StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>());
|
||||
property = content?.GetProperty(alias);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<RequestHandlerSettings> 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<RequestHandlerSettings> requestConfig,
|
||||
ISiteDomainMapper siteDomainMapper,
|
||||
UriUtility uriUtility,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
IPublishedContentCache contentCache,
|
||||
IDocumentNavigationQueryService navigationQueryService)
|
||||
: this(
|
||||
requestConfig,
|
||||
siteDomainMapper,
|
||||
uriUtility,
|
||||
publishedValueFallback,
|
||||
umbracoContextAccessor,
|
||||
navigationQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")]
|
||||
public AliasUrlProvider(
|
||||
IOptionsMonitor<RequestHandlerSettings> requestConfig,
|
||||
ISiteDomainMapper siteDomainMapper,
|
||||
@@ -56,8 +97,8 @@ public class AliasUrlProvider : IUrlProvider
|
||||
uriUtility,
|
||||
publishedValueFallback,
|
||||
umbracoContextAccessor,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -108,7 +149,7 @@ public class AliasUrlProvider : IUrlProvider
|
||||
while (domainUris == null && n != null)
|
||||
{
|
||||
// move to parent node
|
||||
n = n.Parent<IPublishedContent>(_contentCache, _navigationQueryService);
|
||||
n = n.Parent<IPublishedContent>(_navigationQueryService, _publishedContentStatusFilteringService);
|
||||
domainUris = n == null
|
||||
? null
|
||||
: DomainUtilities.DomainsForNode(umbracoContext.Domains, _siteDomainMapper, n.Id, current, false);
|
||||
|
||||
@@ -24,14 +24,46 @@ public class ContentFinderByUrlAlias : IContentFinder
|
||||
private readonly ILogger<ContentFinderByUrlAlias> _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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentFinderByUrlAlias" /> class.
|
||||
/// </summary>
|
||||
public ContentFinderByUrlAlias(
|
||||
ILogger<ContentFinderByUrlAlias> 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<ContentFinderByUrlAlias> 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<ContentFinderByUrlAlias> 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<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
_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<ContentFinderByUrlAlias> logger,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
@@ -61,11 +93,9 @@ public class ContentFinderByUrlAlias : IContentFinder
|
||||
: this(
|
||||
logger,
|
||||
publishedValueFallback,
|
||||
variationContextAccessor,
|
||||
umbracoContextAccessor,
|
||||
contentCache,
|
||||
documentNavigationQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -23,8 +23,8 @@ public class DefaultUrlProvider : IUrlProvider
|
||||
private readonly ILogger<DefaultUrlProvider> _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<RequestHandlerSettings> requestSettings,
|
||||
ILogger<DefaultUrlProvider> 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<RequestHandlerSettings> requestSettings,
|
||||
ILogger<DefaultUrlProvider> logger,
|
||||
ISiteDomainMapper siteDomainMapper,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
UriUtility uriUtility,
|
||||
ILocalizationService localizationService,
|
||||
IPublishedContentCache contentCache,
|
||||
IDocumentNavigationQueryService navigationQueryService)
|
||||
: this(
|
||||
requestSettings,
|
||||
logger,
|
||||
siteDomainMapper,
|
||||
umbracoContextAccessor,
|
||||
uriUtility,
|
||||
localizationService,
|
||||
navigationQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use the non-obsolete constructor. Scheduled for removal in V17.")]
|
||||
public DefaultUrlProvider(
|
||||
IOptionsMonitor<RequestHandlerSettings> requestSettings,
|
||||
ILogger<DefaultUrlProvider> logger,
|
||||
@@ -65,8 +110,8 @@ public class DefaultUrlProvider : IUrlProvider
|
||||
umbracoContextAccessor,
|
||||
uriUtility,
|
||||
localizationService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -101,7 +146,7 @@ public class DefaultUrlProvider : IUrlProvider
|
||||
// n is null at root
|
||||
while (domainUris == null && n != null)
|
||||
{
|
||||
n = n.Parent<IPublishedContent>(_contentCache, _navigationQueryService); // move to parent node
|
||||
n = n.Parent<IPublishedContent>(_navigationQueryService, _publishedContentStatusFilteringService); // move to parent node
|
||||
domainUris = n == null
|
||||
? null
|
||||
: DomainUtilities.DomainsForNode(umbracoContext.Domains, _siteDomainMapper, n.Id, current);
|
||||
|
||||
@@ -17,21 +17,7 @@ namespace Umbraco.Cms.Core.Routing
|
||||
{
|
||||
#region Document Culture
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture assigned to a document by domains, in the context of a current Uri.
|
||||
/// </summary>
|
||||
/// <param name="contentId">The document identifier.</param>
|
||||
/// <param name="contentPath">The document path.</param>
|
||||
/// <param name="current">An optional current Uri.</param>
|
||||
/// <param name="umbracoContext">An Umbraco context.</param>
|
||||
/// <param name="siteDomainMapper">The site domain helper.</param>
|
||||
/// <returns>The culture assigned to the document by domains.</returns>
|
||||
/// <remarks>
|
||||
/// <para>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.</para>
|
||||
/// </remarks>
|
||||
[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<IDomainCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<INavigationQueryService>());
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>());
|
||||
|
||||
[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<IPublishedContentStatusFilteringService>());
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <param name="umbracoContext">An Umbraco context.</param>
|
||||
/// <param name="siteDomainMapper">The site domain helper.</param>
|
||||
/// <param name="domainCache">The domain cache.</param>
|
||||
/// <param name="publishedCache">The published content cache.</param>
|
||||
/// <param name="navigationQueryService">The navigation query service.</param>
|
||||
/// <param name="publishedStatusFilteringService"></param>
|
||||
/// <returns>The culture assigned to the document by domains.</returns>
|
||||
/// <remarks>
|
||||
/// <para>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<IPublishedContent>(publishedCache, navigationQueryService);
|
||||
content = content.Parent<IPublishedContent>(navigationQueryService, publishedStatusFilteringService);
|
||||
hasDomains = content is not null && domainCache.HasAssigned(content.Id, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<DefaultUrlProvider> _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<RequestHandlerSettings> requestSettings,
|
||||
ILogger<DefaultUrlProvider> 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<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
#region GetOtherUrls
|
||||
|
||||
/// <summary>
|
||||
@@ -97,7 +131,7 @@ public class NewDefaultUrlProvider : IUrlProvider
|
||||
// n is null at root
|
||||
while (domainUris == null && n != null)
|
||||
{
|
||||
n = n.Parent<IPublishedContent>(_publishedContentCache, _navigationQueryService); // move to parent node
|
||||
n = n.Parent<IPublishedContent>(_navigationQueryService, _publishedContentStatusFilteringService); // move to parent node
|
||||
domainUris = n == null
|
||||
? null
|
||||
: DomainUtilities.DomainsForNode(_domainCache, _siteDomainMapper, n.Id, current);
|
||||
|
||||
@@ -25,9 +25,49 @@ namespace Umbraco.Cms.Core.Routing
|
||||
/// <param name="urlProviders">The list of URL providers.</param>
|
||||
/// <param name="mediaUrlProviders">The list of media URL providers.</param>
|
||||
/// <param name="variationContextAccessor">The current variation accessor.</param>
|
||||
/// <param name="contentCache">The content cache.</param>
|
||||
/// <param name="navigationQueryService">The query service for the in-memory navigation structure.</param>
|
||||
/// <param name="publishStatusQueryService">The publish status query service, to query if a given content is published in a given culture.</param>
|
||||
/// <param name="publishedContentStatusFilteringService"></param>
|
||||
public UrlProvider(
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
IOptions<WebRoutingSettings> 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<WebRoutingSettings> 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<WebRoutingSettings> 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<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
_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<WebRoutingSettings> routingSettings,
|
||||
@@ -63,13 +103,12 @@ namespace Umbraco.Cms.Core.Routing
|
||||
urlProviders,
|
||||
mediaUrlProviders,
|
||||
variationContextAccessor,
|
||||
contentCache,
|
||||
navigationQueryService,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
[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<WebRoutingSettings> routingSettings,
|
||||
@@ -82,9 +121,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
urlProviders,
|
||||
mediaUrlProviders,
|
||||
variationContextAccessor,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -92,9 +130,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
private readonly IEnumerable<IUrlProvider> _urlProviders;
|
||||
private readonly IEnumerable<IMediaUrlProvider> _mediaUrlProviders;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
private readonly IPublishedContentCache _contentCache;
|
||||
private readonly IDocumentNavigationQueryService _navigationQueryService;
|
||||
private readonly IPublishStatusQueryService _publishStatusQueryService;
|
||||
private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService;
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
@@ -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<IEnumerable<UrlInfo>> GetContentUrlsAsync(
|
||||
this IContent content,
|
||||
IPublishedRouter publishedRouter,
|
||||
@@ -36,8 +36,35 @@ public static class UrlProviderExtensions
|
||||
logger,
|
||||
uriUtility,
|
||||
publishedUrlProvider,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>());
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>());
|
||||
|
||||
[Obsolete("Use the overload with IPublishedStatusFilteringService, scheduled for removal in v17")]
|
||||
public static async Task<IEnumerable<UrlInfo>> GetContentUrlsAsync(
|
||||
this IContent content,
|
||||
IPublishedRouter publishedRouter,
|
||||
IUmbracoContext umbracoContext,
|
||||
ILanguageService languageService,
|
||||
ILocalizedTextService textService,
|
||||
IContentService contentService,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
ILogger<IContent> 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<IPublishedContentStatusFilteringService>());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URLs of the content item.
|
||||
@@ -57,8 +84,8 @@ public static class UrlProviderExtensions
|
||||
ILogger<IContent> 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<UrlInfo>();
|
||||
|
||||
@@ -186,7 +213,7 @@ public static class UrlProviderExtensions
|
||||
// got a URL, deal with collisions, add URL
|
||||
default:
|
||||
// detect collisions, etc
|
||||
Attempt<UrlInfo?> hasCollision = await DetectCollisionAsync(logger, content, url, culture, umbracoContext, publishedRouter, textService, variationContextAccessor, uriUtility, contentCache, navigationQueryService);
|
||||
Attempt<UrlInfo?> 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<IPublishedContent>(contentCache, navigationQueryService);
|
||||
o = o.Parent<IPublishedContent>(navigationQueryService, publishedContentStatusFilteringService);
|
||||
}
|
||||
|
||||
l.Reverse();
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Cms.Core.Services.Navigation;
|
||||
|
||||
public interface IPublishedContentStatusFilteringService : IPublishedStatusFilteringService
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Cms.Core.Services.Navigation;
|
||||
|
||||
public interface IPublishedMediaStatusFilteringService : IPublishedStatusFilteringService
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Navigation;
|
||||
|
||||
public interface IPublishedStatusFilteringService
|
||||
{
|
||||
IEnumerable<IPublishedContent> FilterAvailable(IEnumerable<Guid> candidateKeys, string? culture);
|
||||
}
|
||||
@@ -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<IPublishedContent> FilterAvailable(IEnumerable<Guid> 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<IPublishedContent> WhereIsInvariantOrHasCulture(IEnumerable<Guid> keys, string culture, bool preview)
|
||||
=> keys
|
||||
.Select(key => _publishedContentCache.GetById(preview, key))
|
||||
.WhereNotNull()
|
||||
.Where(content => content.ContentType.VariesByCulture() is false
|
||||
|| content.Cultures.ContainsKey(culture));
|
||||
}
|
||||
@@ -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<IPublishedContent> FilterAvailable(IEnumerable<Guid> candidateKeys, string? culture)
|
||||
=> candidateKeys.Select(_publishedMediaCache.GetById).WhereNotNull().ToArray();
|
||||
}
|
||||
@@ -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<RedirectTracker> _logger;
|
||||
private readonly IPublishedUrlProvider _publishedUrlProvider;
|
||||
private readonly IPublishedContentStatusFilteringService _publishedContentStatusFilteringService;
|
||||
|
||||
public RedirectTracker(
|
||||
ILocalizationService localizationService,
|
||||
IRedirectUrlService redirectUrlService,
|
||||
IPublishedContentCache contentCache,
|
||||
IDocumentNavigationQueryService navigationQueryService,
|
||||
ILogger<RedirectTracker> 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<RedirectTracker> 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<RedirectTracker> logger,
|
||||
IPublishedUrlProvider publishedUrlProvider)
|
||||
: this(
|
||||
localizationService,
|
||||
redirectUrlService,
|
||||
contentCache,
|
||||
navigationQueryService,
|
||||
logger,
|
||||
publishedUrlProvider,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
_localizationService = localizationService;
|
||||
_redirectUrlService = redirectUrlService;
|
||||
_contentCache = contentCache;
|
||||
_navigationQueryService = navigationQueryService;
|
||||
_logger = logger;
|
||||
_publishedUrlProvider = publishedUrlProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -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<string[]>(() => entityContent.AncestorsOrSelf(_contentCache, _navigationQueryService).FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray() ?? Array.Empty<string>());
|
||||
var defaultCultures = new Lazy<string[]>(() => entityContent.AncestorsOrSelf(_navigationQueryService, _publishedContentStatusFilteringService).FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray() ?? Array.Empty<string>());
|
||||
|
||||
// Get all language ISO codes (in case we're dealing with invariant content with variant ancestors)
|
||||
var languageIsoCodes = new Lazy<string[]>(() => _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<string> cultures = publishedContent.Cultures.Any() ? publishedContent.Cultures.Keys : defaultCultures.Value;
|
||||
|
||||
@@ -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<IPublishedContentCache>();
|
||||
navigationQueryService = StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>();
|
||||
publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>();
|
||||
break;
|
||||
case PublishedItemType.Media:
|
||||
publishedCache = StaticServiceProvider.Instance.GetRequiredService<IPublishedMediaCache>();
|
||||
navigationQueryService = StaticServiceProvider.Instance.GetRequiredService<IMediaNavigationQueryService>();
|
||||
publishedStatusFilteringService = StaticServiceProvider.Instance.GetRequiredService<IPublishedMediaStatusFilteringService>();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Level is not implemented for " + ContentType.ItemType);
|
||||
}
|
||||
|
||||
return this.Parent<IPublishedContent>(publishedCache, navigationQueryService);
|
||||
return this.Parent<IPublishedContent>(navigationQueryService, publishedStatusFilteringService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,6 @@ public static class FriendlyPublishedContentExtensions
|
||||
private static IPublishedContentCache PublishedContentCache { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>();
|
||||
|
||||
private static IPublishedMediaCache PublishedMediaCache { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedMediaCache>();
|
||||
|
||||
private static IDocumentNavigationQueryService DocumentNavigationQueryService { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>();
|
||||
|
||||
@@ -70,9 +67,6 @@ public static class FriendlyPublishedContentExtensions
|
||||
private static IMemberTypeService MemberTypeService { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IMemberTypeService>();
|
||||
|
||||
private static IPublishStatusQueryService PublishStatusQueryService { get; } =
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>();
|
||||
|
||||
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<IPublishedContentStatusFilteringService>();
|
||||
case PublishedItemType.Media:
|
||||
return PublishedMediaCache;
|
||||
return StaticServiceProvider.Instance.GetRequiredService<IPublishedMediaStatusFilteringService>();
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported content type.");
|
||||
}
|
||||
@@ -246,7 +239,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// set to 1.
|
||||
/// </remarks>
|
||||
public static IPublishedContent Root(this IPublishedContent content)
|
||||
=> content.Root(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService);
|
||||
=> content.Root(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root content (ancestor or self at level 1) for the specified <paramref name="content" /> if it's of the
|
||||
@@ -265,7 +258,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// </remarks>
|
||||
public static T? Root<T>(this IPublishedContent content)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Root<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService);
|
||||
=> content.Root<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of the content item.
|
||||
@@ -275,7 +268,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <returns>The parent of content of the specified content type or <c>null</c>.</returns>
|
||||
public static T? Parent<T>(this IPublishedContent content)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Parent<T>(GetPublishedCache(content), GetNavigationQueryService(content));
|
||||
=> content.Parent<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of the content item.
|
||||
@@ -283,7 +276,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>The parent of content or <c>null</c>.</returns>
|
||||
public static IPublishedContent? Parent(this IPublishedContent content)
|
||||
=> content.Parent<IPublishedContent>(GetPublishedCache(content), GetNavigationQueryService(content));
|
||||
=> content.Parent<IPublishedContent>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ancestors of the content.
|
||||
@@ -292,7 +285,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <returns>The ancestors of the content, in down-top order.</returns>
|
||||
/// <remarks>Does not consider the content itself.</remarks>
|
||||
public static IEnumerable<IPublishedContent> Ancestors(this IPublishedContent content)
|
||||
=> content.Ancestors(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService);
|
||||
=> content.Ancestors(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content and its ancestors.
|
||||
@@ -300,7 +293,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>The content and its ancestors, in down-top order.</returns>
|
||||
public static IEnumerable<IPublishedContent> AncestorsOrSelf(this IPublishedContent content)
|
||||
=> content.AncestorsOrSelf(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService);
|
||||
=> content.AncestorsOrSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content and its ancestors, of a specified content type.
|
||||
@@ -311,7 +304,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <remarks>May or may not begin with the content itself, depending on its content type.</remarks>
|
||||
public static IEnumerable<T> AncestorsOrSelf<T>(this IPublishedContent content)
|
||||
where T : class, IPublishedContent
|
||||
=> content.AncestorsOrSelf<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService);
|
||||
=> content.AncestorsOrSelf<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ancestor of the content, i.e. its parent.
|
||||
@@ -319,7 +312,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>The ancestor of the content.</returns>
|
||||
public static IPublishedContent? Ancestor(this IPublishedContent content)
|
||||
=> content.Ancestor(GetPublishedCache(content), GetNavigationQueryService(content));
|
||||
=> content.Ancestor(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nearest ancestor of the content, of a specified content type.
|
||||
@@ -330,7 +323,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <remarks>Does not consider the content itself. May return <c>null</c>.</remarks>
|
||||
public static T? Ancestor<T>(this IPublishedContent content)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Ancestor<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService);
|
||||
=> content.Ancestor<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content or its nearest ancestor, of a specified content type.
|
||||
@@ -341,7 +334,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <remarks>May or may not return the content itself depending on its content type. May return <c>null</c>.</remarks>
|
||||
public static T? AncestorOrSelf<T>(this IPublishedContent content)
|
||||
where T : class, IPublishedContent
|
||||
=> content.AncestorOrSelf<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService);
|
||||
=> content.AncestorOrSelf<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content));
|
||||
|
||||
/// <summary>
|
||||
/// Returns all DescendantsOrSelf of all content referenced
|
||||
@@ -358,7 +351,19 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent> DescendantsOrSelfOfType(
|
||||
this IEnumerable<IPublishedContent> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all DescendantsOrSelf of all content referenced
|
||||
@@ -376,77 +381,88 @@ public static class FriendlyPublishedContentExtensions
|
||||
this IEnumerable<IPublishedContent> parentNodes,
|
||||
string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> parentNodes.DescendantsOrSelf<T>(VariationContextAccessor, GetPublishedCache(parentNodes.First()), GetNavigationQueryService(parentNodes.First()), PublishStatusQueryService, culture);
|
||||
{
|
||||
IPublishedContent[] parentNodesAsArray = parentNodes as IPublishedContent[] ?? parentNodes.ToArray();
|
||||
if (parentNodesAsArray.Length == 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
return parentNodesAsArray.DescendantsOrSelf<T>(
|
||||
GetNavigationQueryService(parentNodesAsArray.First()),
|
||||
GetPublishedStatusFilteringService(parentNodesAsArray.First()),
|
||||
culture);
|
||||
}
|
||||
|
||||
public static IEnumerable<IPublishedContent> 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<IPublishedContent> 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<IPublishedContent> 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<T> Descendants<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Descendants<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.Descendants<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
public static IEnumerable<T> Descendants<T>(this IPublishedContent content, int level, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Descendants<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture);
|
||||
=> content.Descendants<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture);
|
||||
|
||||
public static IEnumerable<IPublishedContent> 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<IPublishedContent> 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<IPublishedContent> 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<T> DescendantsOrSelf<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.DescendantsOrSelf<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.DescendantsOrSelf<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
public static IEnumerable<T> DescendantsOrSelf<T>(this IPublishedContent content, int level, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.DescendantsOrSelf<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture);
|
||||
=> content.DescendantsOrSelf<T>(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<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Descendant<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.Descendant<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
public static T? Descendant<T>(this IPublishedContent content, int level, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Descendant<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture);
|
||||
=> content.Descendant<T>(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<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.DescendantOrSelf<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.DescendantOrSelf<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
public static T? DescendantOrSelf<T>(this IPublishedContent content, int level, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.DescendantOrSelf<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, level, culture);
|
||||
=> content.DescendantOrSelf<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), level, culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content item.
|
||||
@@ -474,7 +490,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent> Children(this IPublishedContent content, string? culture = null)
|
||||
=> content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content, filtered by a predicate.
|
||||
@@ -493,7 +509,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
this IPublishedContent content,
|
||||
Func<IPublishedContent, bool> predicate,
|
||||
string? culture = null)
|
||||
=> content.Children(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, predicate, culture);
|
||||
=> content.Children(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), predicate, culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content, of any of the specified types.
|
||||
@@ -506,7 +522,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
/// <returns>The children of the content, of any of the specified types.</returns>
|
||||
public static IEnumerable<IPublishedContent>? 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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content, of a given content type.
|
||||
@@ -523,30 +539,30 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// </remarks>
|
||||
public static IEnumerable<T>? Children<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Children<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.Children<T>(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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first child of the content, of a given content type.
|
||||
/// </summary>
|
||||
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<IPublishedContent, bool> 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<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.FirstChild<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.FirstChild<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
public static T? FirstChild<T>(this IPublishedContent content, Func<T, bool> 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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content.
|
||||
@@ -561,7 +577,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent>? Siblings(this IPublishedContent content, string? culture = null)
|
||||
=> content.Siblings(GetPublishedCache(content), GetNavigationQueryService(content), VariationContextAccessor, PublishStatusQueryService, culture);
|
||||
=> content.Siblings(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content, of a given content type.
|
||||
@@ -577,7 +593,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <para>Note that in V7 this method also return the content node self.</para>
|
||||
/// </remarks>
|
||||
public static IEnumerable<IPublishedContent>? 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);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the siblings of the content, of a given content type.
|
||||
@@ -594,7 +610,7 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// </remarks>
|
||||
public static IEnumerable<T>? Siblings<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.Siblings<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.Siblings<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
/// <summary>
|
||||
/// 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<IPublishedContent>? SiblingsAndSelf(
|
||||
this IPublishedContent content,
|
||||
string? culture = null)
|
||||
=> content.SiblingsAndSelf(GetPublishedCache(content), GetNavigationQueryService(content), VariationContextAccessor, PublishStatusQueryService, culture);
|
||||
=> content.SiblingsAndSelf(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// <returns>The siblings of the content including the node itself, of the given content type.</returns>
|
||||
public static IEnumerable<T>? SiblingsAndSelf<T>(this IPublishedContent content, string? culture = null)
|
||||
where T : class, IPublishedContent
|
||||
=> content.SiblingsAndSelf<T>(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService, culture);
|
||||
=> content.SiblingsAndSelf<T>(GetNavigationQueryService(content), GetPublishedStatusFilteringService(content), culture);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url of the content item.
|
||||
@@ -671,11 +687,9 @@ public static class FriendlyPublishedContentExtensions
|
||||
/// <returns>The children of the content.</returns>
|
||||
[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),
|
||||
=> content.ChildrenAsTable(
|
||||
GetNavigationQueryService(content),
|
||||
GetPublishedStatusFilteringService(content),
|
||||
ContentTypeService,
|
||||
MediaTypeService,
|
||||
MemberTypeService,
|
||||
|
||||
@@ -17,22 +17,7 @@ public static class PublishedContentExtensions
|
||||
{
|
||||
#region Variations
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture assigned to a document by domains, in the context of a current Uri.
|
||||
/// </summary>
|
||||
/// <param name="content">The document.</param>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
/// <param name="siteDomainHelper">The site domain helper.</param>
|
||||
/// <param name="current">An optional current Uri.</param>
|
||||
/// <returns>The culture assigned to the document by domains.</returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture assigned to a document by domains, in the context of a current Uri.
|
||||
/// </summary>
|
||||
@@ -68,6 +66,7 @@ public static class PublishedContentExtensions
|
||||
IDomainCache domainCache,
|
||||
IPublishedCache publishedCache,
|
||||
INavigationQueryService navigationQueryService,
|
||||
IPublishedStatusFilteringService publishedStatusFilteringService,
|
||||
Uri? current = null)
|
||||
{
|
||||
IUmbracoContext umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext();
|
||||
|
||||
@@ -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<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
[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<IPublishedContentCache>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>())
|
||||
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentStatusFilteringService>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<Guid> 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<Guid> 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]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,6 +413,6 @@ public class DomainAndUrlsTests : UmbracoIntegrationTest
|
||||
GetRequiredService<ILogger<IContent>>(),
|
||||
GetRequiredService<UriUtility>(),
|
||||
GetRequiredService<IPublishedUrlProvider>(),
|
||||
GetRequiredService<IPublishedContentCache>(),
|
||||
GetRequiredService<IDocumentNavigationQueryService>()).GetAwaiter().GetResult();
|
||||
GetRequiredService<IDocumentNavigationQueryService>(),
|
||||
GetRequiredService<IPublishedContentStatusFilteringService>()).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
@@ -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<IPublishedContent>(), It.IsAny<string?>()))
|
||||
.Returns((IPublishedContent c, string? culture) => $"url:{c.UrlSegment}");
|
||||
|
||||
var routeBuilder = CreateContentRouteBuilder(apiContentRouteProvider.Object, CreateGlobalSettings());
|
||||
var navigationQueryServiceMock = new Mock<IDocumentNavigationQueryService>();
|
||||
IEnumerable<Guid> 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);
|
||||
|
||||
@@ -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<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), grandchild.Key)).Returns(grandchild);
|
||||
|
||||
IEnumerable<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
|
||||
IEnumerable<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
|
||||
IEnumerable<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
|
||||
IEnumerable<Guid> 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<IPublishedContent>();
|
||||
content.SetupGet(c => c.ItemType).Returns(itemType);
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(true);
|
||||
var builder = CreateApiContentRouteBuilder(true, Mock.Of<IDocumentNavigationQueryService>());
|
||||
Assert.Throws<ArgumentException>(() => builder.Build(content.Object));
|
||||
}
|
||||
|
||||
@@ -236,9 +237,9 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
var grandchild = SetupInvariantPublishedContent("The Grandchild", grandchildKey, navigationQueryServiceMock, child);
|
||||
|
||||
var contentCache = Mock.Of<IPublishedContentCache>();
|
||||
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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), grandchild.Key)).Returns(grandchild);
|
||||
|
||||
IEnumerable<Guid> 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<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath, contentCache: contentCache, isPreview: true, navigationQueryService: navigationQueryServiceMock.Object);
|
||||
IEnumerable<Guid> 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<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(true, addTrailingSlash, contentCache: contentCache, isPreview: true, navigationQueryService: navigationQueryServiceMock.Object);
|
||||
IEnumerable<Guid> 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<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
|
||||
IEnumerable<Guid> 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<bool>(), root.Key)).Returns(root);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<bool>(), child.Key)).Returns(child);
|
||||
|
||||
var apiContentPathProvider = new Mock<IApiContentPathProvider>();
|
||||
apiContentPathProvider
|
||||
@@ -369,7 +367,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
IEnumerable<Guid> 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<IPreviewService>(),
|
||||
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<IOptionsMonitor<RequestHandlerSettings>>();
|
||||
|
||||
@@ -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<string?>())).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> globalSettings,
|
||||
IVariationContextAccessor? variationContextAccessor = null,
|
||||
|
||||
@@ -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<IUmbracoContextAccessor>(),
|
||||
Mock.Of<IMemberService>(),
|
||||
|
||||
@@ -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<IDocumentNavigationQueryService> 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<IDocumentNavigationQueryService>();
|
||||
IEnumerable<Guid> ancestorsKeys = [];
|
||||
DocumentNavigationQueryServiceMock.Setup(x => x.TryGetAncestorsKeys(contentKey, out ancestorsKeys)).Returns(true);
|
||||
}
|
||||
|
||||
protected Mock<IPublishedContent> SetupPublishedContent(string name, Guid key, PublishedItemType itemType, IPublishedContentType contentType)
|
||||
@@ -109,4 +118,28 @@ public class PropertyValueConverterTests : DeliveryApiTests
|
||||
.Setup(pcc => pcc.GetById(It.IsAny<bool>(), media.Key))
|
||||
.Returns(media);
|
||||
}
|
||||
|
||||
protected override ApiContentRouteBuilder CreateContentRouteBuilder(
|
||||
IApiContentPathProvider contentPathProvider,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IVariationContextAccessor? variationContextAccessor = null,
|
||||
IRequestPreviewService? requestPreviewService = null,
|
||||
IOptionsMonitor<RequestHandlerSettings>? 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Guid>(), It.IsAny<string>())).Returns(true);
|
||||
IEnumerable<Guid> descendantKeys = [];
|
||||
Mock.Get(documentNavigationQueryService).Setup(x => x.TryGetDescendantsKeys(It.IsAny<Guid>(), out descendantKeys)).Returns(true);
|
||||
|
||||
Mock.Get(publishedContentStatusFilteringService).Setup(x => x.FilterAvailable(It.IsAny<IEnumerable<Guid>>(), It.IsAny<string?>())).Returns([]);
|
||||
var publishedRequestBuilder = new PublishedRequestBuilder(new Uri(absoluteUrl, UriKind.Absolute), fileService);
|
||||
|
||||
// Act
|
||||
var sut = new ContentFinderByUrlAlias(
|
||||
Mock.Of<ILogger<ContentFinderByUrlAlias>>(),
|
||||
Mock.Of<IPublishedValueFallback>(),
|
||||
umbracoContextAccessor,
|
||||
documentNavigationQueryService,
|
||||
publishedContentStatusFilteringService);
|
||||
var result = await sut.TryFindContent(publishedRequestBuilder);
|
||||
|
||||
Assert.IsTrue(result);
|
||||
|
||||
@@ -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<Guid, IPublishedContent> Items) SetupInvariant(bool forPreview)
|
||||
{
|
||||
var contentType = new Mock<IPublishedContentType>();
|
||||
contentType.SetupGet(c => c.Variations).Returns(ContentVariation.Nothing);
|
||||
|
||||
var items = new Dictionary<Guid, IPublishedContent>();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var content = new Mock<IPublishedContent>();
|
||||
|
||||
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<string, PublishedCultureInfo>());
|
||||
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<Guid, IPublishedContent> Items) SetupVariant(bool forPreview, string requestCulture)
|
||||
{
|
||||
var contentType = new Mock<IPublishedContentType>();
|
||||
contentType.SetupGet(c => c.Variations).Returns(ContentVariation.Culture);
|
||||
|
||||
var items = new Dictionary<Guid, IPublishedContent>();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var content = new Mock<IPublishedContent>();
|
||||
|
||||
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<Guid, IPublishedContent> Items) SetupMixedVariance(bool forPreview, string requestCulture)
|
||||
{
|
||||
var invariantContentType = new Mock<IPublishedContentType>();
|
||||
invariantContentType.SetupGet(c => c.Variations).Returns(ContentVariation.Nothing);
|
||||
|
||||
var variantContentType = new Mock<IPublishedContentType>();
|
||||
variantContentType.SetupGet(c => c.Variations).Returns(ContentVariation.Culture);
|
||||
|
||||
var items = new Dictionary<Guid, IPublishedContent>();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var content = new Mock<IPublishedContent>();
|
||||
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<Guid, IPublishedContent> items)
|
||||
=> SetupPublishStatusQueryService(items, id => id % 2 == 0);
|
||||
|
||||
private IPublishStatusQueryService SetupPublishStatusQueryService(Dictionary<Guid, IPublishedContent> items, Func<int, bool> idIsPublished)
|
||||
{
|
||||
var publishStatusQueryService = new Mock<IPublishStatusQueryService>();
|
||||
publishStatusQueryService
|
||||
.Setup(p => p.IsDocumentPublished(It.IsAny<Guid>(), It.IsAny<string>()))
|
||||
.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<IPreviewService>();
|
||||
previewService.Setup(p => p.IsInPreview()).Returns(forPreview);
|
||||
return previewService.Object;
|
||||
}
|
||||
|
||||
private IVariationContextAccessor SetupVariantContextAccessor(string? requestCulture)
|
||||
{
|
||||
var variationContextAccessor = new Mock<IVariationContextAccessor>();
|
||||
variationContextAccessor.SetupGet(v => v.VariationContext).Returns(new VariationContext(requestCulture));
|
||||
return variationContextAccessor.Object;
|
||||
}
|
||||
|
||||
private IPublishedContentCache SetupPublishedContentCache(bool forPreview, Dictionary<Guid, IPublishedContent> items)
|
||||
{
|
||||
var publishedContentCache = new Mock<IPublishedContentCache>();
|
||||
publishedContentCache
|
||||
.Setup(c => c.GetById(forPreview, It.IsAny<Guid>()))
|
||||
.Returns((bool preview, Guid key) => items.TryGetValue(key, out var item) ? item : null);
|
||||
return publishedContentCache.Object;
|
||||
}
|
||||
}
|
||||
@@ -98,9 +98,8 @@ public class HtmlImageSourceParserTests
|
||||
new UrlProviderCollection(() => Enumerable.Empty<IUrlProvider>()),
|
||||
new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }),
|
||||
Mock.Of<IVariationContextAccessor>(),
|
||||
Mock.Of<IPublishedContentCache>(),
|
||||
Mock.Of<IDocumentNavigationQueryService>(),
|
||||
Mock.Of<IPublishStatusQueryService>());
|
||||
Mock.Of<IPublishedContentStatusFilteringService>());
|
||||
|
||||
using (var reference = umbracoContextFactory.EnsureUmbracoContext())
|
||||
{
|
||||
|
||||
@@ -222,8 +222,12 @@ public class HtmlLocalLinkParserTests
|
||||
var webRoutingSettings = new WebRoutingSettings();
|
||||
|
||||
var navigationQueryService = new Mock<IDocumentNavigationQueryService>();
|
||||
Guid? parentKey = null;
|
||||
navigationQueryService.Setup(x => x.TryGetParentKey(It.IsAny<Guid>(), out parentKey)).Returns(true);
|
||||
// Guid? parentKey = null;
|
||||
// navigationQueryService.Setup(x => x.TryGetParentKey(It.IsAny<Guid>(), out parentKey)).Returns(true);
|
||||
IEnumerable<Guid> ancestorKeys = [];
|
||||
navigationQueryService.Setup(x => x.TryGetAncestorsKeys(It.IsAny<Guid>(), out ancestorKeys)).Returns(true);
|
||||
|
||||
var publishedContentStatusFilteringService = new Mock<IPublishedContentStatusFilteringService>();
|
||||
|
||||
using (var reference = umbracoContextFactory.EnsureUmbracoContext())
|
||||
{
|
||||
@@ -246,9 +250,8 @@ public class HtmlLocalLinkParserTests
|
||||
new UrlProviderCollection(() => new[] { contentUrlProvider.Object }),
|
||||
new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }),
|
||||
Mock.Of<IVariationContextAccessor>(),
|
||||
contentCache.Object,
|
||||
navigationQueryService.Object,
|
||||
publishStatusQueryService.Object);
|
||||
publishedContentStatusFilteringService.Object);
|
||||
|
||||
var linkParser = new HtmlLocalLinkParser(publishedUrlProvider);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user