diff --git a/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs b/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs index 306cf7f3ee..221a1a1af5 100644 --- a/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs +++ b/src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs @@ -1,5 +1,7 @@ -using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; @@ -16,6 +18,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder private readonly IRequestPreviewService _requestPreviewService; private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; + private readonly IPublishStatusQueryService _publishStatusQueryService; private RequestHandlerSettings _requestSettings; public ApiContentRouteBuilder( @@ -25,18 +28,41 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder IRequestPreviewService requestPreviewService, IOptionsMonitor requestSettings, IPublishedContentCache contentCache, - IDocumentNavigationQueryService navigationQueryService) + IDocumentNavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService) { _apiContentPathProvider = apiContentPathProvider; _variationContextAccessor = variationContextAccessor; _requestPreviewService = requestPreviewService; _contentCache = contentCache; _navigationQueryService = navigationQueryService; + _publishStatusQueryService = publishStatusQueryService; _globalSettings = globalSettings.Value; _requestSettings = requestSettings.CurrentValue; requestSettings.OnChange(settings => _requestSettings = settings); } + [Obsolete("Use constructor that takes an IPublishStatusQueryService instead, scheduled for removal in v17")] + public ApiContentRouteBuilder( + IApiContentPathProvider apiContentPathProvider, + IOptions globalSettings, + IVariationContextAccessor variationContextAccessor, + IRequestPreviewService requestPreviewService, + IOptionsMonitor requestSettings, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService) + : this( + apiContentPathProvider, + globalSettings, + variationContextAccessor, + requestPreviewService, + requestSettings, + contentCache, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + public IApiContentRoute? Build(IPublishedContent content, string? culture = null) { if (content.ItemType != PublishedItemType.Content) @@ -105,7 +131,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder { if (isPreview is false) { - return content.Root(_contentCache, _navigationQueryService); + return content.Root(_variationContextAccessor, _contentCache, _navigationQueryService, _publishStatusQueryService); } _navigationQueryService.TryGetRootKeys(out IEnumerable rootKeys); @@ -114,6 +140,6 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder // 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(_contentCache, _navigationQueryService); + ?? content.Root(_variationContextAccessor, _contentCache, _navigationQueryService, _publishStatusQueryService); } } diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index fdf47a2c82..adfdbb6076 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -3159,8 +3159,10 @@ public static class PublishedContentExtensions /// Gets the root content (ancestor or self at level 1) for the specified . /// /// The content. + /// /// The content cache. /// The query service for the in-memory navigation structure. + /// /// /// The root content (ancestor or self at level 1) for the specified . /// @@ -3171,8 +3173,63 @@ public static class PublishedContentExtensions /// public static IPublishedContent Root( this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, IPublishedCache publishedCache, - INavigationQueryService navigationQueryService) => content.AncestorOrSelf(publishedCache, navigationQueryService, 1); + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService) => content.AncestorOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, 1); + + /// + /// Gets the root content (ancestor or self at level 1) for the specified . + /// + /// The content. + /// The content cache. + /// The query service for the in-memory navigation structure. + /// + /// The root content (ancestor or self at level 1) for the specified . + /// + /// + /// This is the same as calling + /// with maxLevel + /// set to 1. + /// + [Obsolete("Use the overload with IVariationContextAccessor & IPublishStatusQueryService, scheduled for removal in v17")] + public static IPublishedContent Root( + this IPublishedContent content, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService) => Root( + content, + StaticServiceProvider.Instance.GetRequiredService(), + publishedCache, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()); + + /// + /// Gets the root content (ancestor or self at level 1) for the specified if it's of the + /// specified content type . + /// + /// The content type. + /// The content. + /// + /// The content cache. + /// The query service for the in-memory navigation structure. + /// + /// + /// The root content (ancestor or self at level 1) for the specified of content type + /// . + /// + /// + /// This is the same as calling + /// with + /// maxLevel set to 1. + /// + public static T? Root( + this IPublishedContent content, + IVariationContextAccessor variationContextAccessor, + IPublishedCache publishedCache, + INavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService) + where T : class, IPublishedContent => + content.AncestorOrSelf(variationContextAccessor, publishedCache, navigationQueryService, publishStatusQueryService, 1); /// /// Gets the root content (ancestor or self at level 1) for the specified if it's of the @@ -3191,12 +3248,18 @@ public static class PublishedContentExtensions /// with /// maxLevel set to 1. /// + [Obsolete("Use the overload with IVariationContextAccessor & PublishStatusQueryService, scheduled for removal in v17")] public static T? Root( this IPublishedContent content, IPublishedCache publishedCache, INavigationQueryService navigationQueryService) where T : class, IPublishedContent => - content.AncestorOrSelf(publishedCache, navigationQueryService, 1); + Root( + content, + StaticServiceProvider.Instance.GetRequiredService(), + publishedCache, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()); #endregion diff --git a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs index f592fb0a0f..39cd4c8854 100644 --- a/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs @@ -1,4 +1,6 @@ +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Services.Navigation; @@ -24,6 +26,7 @@ public class ContentFinderByUrlAlias : IContentFinder private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _documentNavigationQueryService; + private readonly IPublishStatusQueryService _publishStatusQueryService; private readonly IVariationContextAccessor _variationContextAccessor; /// @@ -35,16 +38,37 @@ public class ContentFinderByUrlAlias : IContentFinder IVariationContextAccessor variationContextAccessor, IUmbracoContextAccessor umbracoContextAccessor, IPublishedContentCache contentCache, - IDocumentNavigationQueryService documentNavigationQueryService) + IDocumentNavigationQueryService documentNavigationQueryService, + IPublishStatusQueryService publishStatusQueryService) { _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")] + public ContentFinderByUrlAlias( + ILogger logger, + IPublishedValueFallback publishedValueFallback, + IVariationContextAccessor variationContextAccessor, + IUmbracoContextAccessor umbracoContextAccessor, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService documentNavigationQueryService) + : this( + logger, + publishedValueFallback, + variationContextAccessor, + umbracoContextAccessor, + contentCache, + documentNavigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + /// /// Tries to find and assign an Umbraco document to a PublishedRequest. /// @@ -145,14 +169,14 @@ public class ContentFinderByUrlAlias : IContentFinder if (rootNodeId > 0) { IPublishedContent? rootNode = cache?.GetById(rootNodeId); - return rootNode?.Descendants(_variationContextAccessor, _contentCache, _documentNavigationQueryService).FirstOrDefault(x => IsMatch(x, test1, test2)); + return rootNode?.Descendants(_variationContextAccessor, _contentCache, _documentNavigationQueryService, _publishStatusQueryService).FirstOrDefault(x => IsMatch(x, test1, test2)); } if (cache is not null) { foreach (IPublishedContent rootContent in cache.GetAtRoot()) { - IPublishedContent? c = rootContent.DescendantsOrSelf(_variationContextAccessor, _contentCache, _documentNavigationQueryService) + IPublishedContent? c = rootContent.DescendantsOrSelf(_variationContextAccessor, _contentCache, _documentNavigationQueryService, _publishStatusQueryService) .FirstOrDefault(x => IsMatch(x, test1, test2)); if (c != null) { diff --git a/src/Umbraco.Core/Routing/UrlProvider.cs b/src/Umbraco.Core/Routing/UrlProvider.cs index f40c240a73..f034fbf8b9 100644 --- a/src/Umbraco.Core/Routing/UrlProvider.cs +++ b/src/Umbraco.Core/Routing/UrlProvider.cs @@ -27,6 +27,28 @@ namespace Umbraco.Cms.Core.Routing /// The current variation accessor. /// The content cache. /// The query service for the in-memory navigation structure. + /// The publish status query service, to query if a given content is published in a given culture. + public UrlProvider( + IUmbracoContextAccessor umbracoContextAccessor, + IOptions routingSettings, + UrlProviderCollection urlProviders, + MediaUrlProviderCollection mediaUrlProviders, + IVariationContextAccessor variationContextAccessor, + IPublishedContentCache contentCache, + IDocumentNavigationQueryService navigationQueryService, + IPublishStatusQueryService publishStatusQueryService) + { + _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.")] public UrlProvider( IUmbracoContextAccessor umbracoContextAccessor, IOptions routingSettings, @@ -35,14 +57,16 @@ namespace Umbraco.Cms.Core.Routing IVariationContextAccessor variationContextAccessor, IPublishedContentCache contentCache, IDocumentNavigationQueryService navigationQueryService) + : this( + umbracoContextAccessor, + routingSettings, + urlProviders, + mediaUrlProviders, + variationContextAccessor, + contentCache, + navigationQueryService, + StaticServiceProvider.Instance.GetRequiredService()) { - _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); - _urlProviders = urlProviders; - _mediaUrlProviders = mediaUrlProviders; - _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - _contentCache = contentCache; - _navigationQueryService = navigationQueryService; - Mode = routingSettings.Value.UrlProviderMode; } [Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")] @@ -59,7 +83,8 @@ namespace Umbraco.Cms.Core.Routing mediaUrlProviders, variationContextAccessor, StaticServiceProvider.Instance.GetRequiredService(), - StaticServiceProvider.Instance.GetRequiredService()) + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService()) { } @@ -69,6 +94,7 @@ namespace Umbraco.Cms.Core.Routing private readonly IVariationContextAccessor _variationContextAccessor; private readonly IPublishedContentCache _contentCache; private readonly IDocumentNavigationQueryService _navigationQueryService; + private readonly IPublishStatusQueryService _publishStatusQueryService; /// /// Gets or sets the provider URL mode. @@ -147,7 +173,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(_contentCache, _navigationQueryService).Any(x => x.ContentType.VariesByCulture())) + if (culture == null && content.AncestorsOrSelf(_variationContextAccessor, _contentCache, _navigationQueryService, _publishStatusQueryService).Any(x => x.ContentType.VariesByCulture())) { culture = _variationContextAccessor?.VariationContext?.Culture ?? string.Empty; } diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs index d2bb93ac5d..c4c839f695 100644 --- a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs @@ -243,7 +243,7 @@ public static class FriendlyPublishedContentExtensions /// set to 1. /// public static IPublishedContent Root(this IPublishedContent content) - => content.Root(GetPublishedCache(content), GetNavigationQueryService(content)); + => content.Root(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); /// /// Gets the root content (ancestor or self at level 1) for the specified if it's of the @@ -262,7 +262,7 @@ public static class FriendlyPublishedContentExtensions /// public static T? Root(this IPublishedContent content) where T : class, IPublishedContent - => content.Root(GetPublishedCache(content), GetNavigationQueryService(content)); + => content.Root(VariationContextAccessor, GetPublishedCache(content), GetNavigationQueryService(content), PublishStatusQueryService); /// /// Gets the parent of the content item. diff --git a/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs b/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs index ea794faeb7..eb29633166 100644 --- a/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs +++ b/tests/Umbraco.Tests.UnitTests/AutoFixture/Customizations/UmbracoCustomizations.cs @@ -11,6 +11,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Install; @@ -34,7 +35,8 @@ internal class UmbracoCustomizations : ICustomization .Customize(new ConstructorCustomization(typeof(DatabaseSchemaCreatorFactory), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(InstallHelper), new GreedyConstructorQuery())) .Customize(new ConstructorCustomization(typeof(DatabaseBuilder), new GreedyConstructorQuery())) - .Customize(new ConstructorCustomization(typeof(ContentVersionService), new GreedyConstructorQuery())); + .Customize(new ConstructorCustomization(typeof(ContentVersionService), new GreedyConstructorQuery())) + .Customize(new ConstructorCustomization(typeof(ContentFinderByUrlAlias), new GreedyConstructorQuery())); // When requesting an IUserStore ensure we actually uses a IUserLockoutStore fixture.Customize>(cc => diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs index 06950c4ba9..c0001bf067 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs @@ -429,9 +429,10 @@ public class ContentRouteBuilderTests : DeliveryApiTests { var variantContextAccessor = Mock.Of(); + string Url(IPublishedContent content, string? culture) { - var ancestorsOrSelf = content.AncestorsOrSelf(contentCache, navigationQueryService).ToArray(); + var ancestorsOrSelf = content.AncestorsOrSelf(variantContextAccessor, contentCache, navigationQueryService, PublishStatusQueryService).ToArray(); return ancestorsOrSelf.All(c => c.IsPublished(culture)) ? string.Join("/", ancestorsOrSelf.Reverse().Skip(hideTopLevelNodeFromPath ? 1 : 0).Select(c => c.UrlSegment(variantContextAccessor, culture))).EnsureStartsWith("/") : "#"; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs index 5b349671d6..2764dd21d3 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/DeliveryApiTests.cs @@ -20,6 +20,8 @@ public class DeliveryApiTests protected IPublishedPropertyType DefaultPropertyType { get; private set; } + protected IPublishStatusQueryService PublishStatusQueryService { get; private set; } + [SetUp] public virtual void Setup() { @@ -57,6 +59,13 @@ public class DeliveryApiTests defaultPropertyValueConverter.Setup(p => p.GetPropertyCacheLevel(It.IsAny())).Returns(PropertyCacheLevel.None); DefaultPropertyType = SetupPublishedPropertyType(defaultPropertyValueConverter.Object, "default", "Default.Editor"); + + var publishStatusQueryService = new Mock(); + publishStatusQueryService + .Setup(x => x.IsDocumentPublished(It.IsAny(), It.IsAny())) + .Returns(true); + + PublishStatusQueryService = publishStatusQueryService.Object; } protected IPublishedPropertyType SetupPublishedPropertyType(IPropertyValueConverter valueConverter, string propertyTypeAlias, string editorAlias, object? dataTypeConfiguration = null) @@ -117,7 +126,8 @@ public class DeliveryApiTests IRequestPreviewService? requestPreviewService = null, IOptionsMonitor? requestHandlerSettingsMonitor = null, IPublishedContentCache? contentCache = null, - IDocumentNavigationQueryService? navigationQueryService = null) + IDocumentNavigationQueryService? navigationQueryService = null, + IPublishStatusQueryService? publishStatusQueryService = null) { if (requestHandlerSettingsMonitor == null) { @@ -133,6 +143,7 @@ public class DeliveryApiTests requestPreviewService ?? Mock.Of(), requestHandlerSettingsMonitor, contentCache ?? Mock.Of(), - navigationQueryService ?? Mock.Of()); + navigationQueryService ?? Mock.Of(), + publishStatusQueryService ?? PublishStatusQueryService); } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs index 1977c918a5..d7f86ff11e 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAliasTests.cs @@ -7,6 +7,7 @@ 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.Cms.Tests.UnitTests.AutoFixture; @@ -30,6 +31,7 @@ public class ContentFinderByUrlAliasTests [Frozen] IUmbracoContextAccessor umbracoContextAccessor, [Frozen] IUmbracoContext umbracoContext, [Frozen] IVariationContextAccessor variationContextAccessor, + [Frozen] IPublishStatusQueryService publishStatusQueryService, IFileService fileService, ContentFinderByUrlAlias sut, IPublishedContent[] rootContents, @@ -48,6 +50,7 @@ public class ContentFinderByUrlAliasTests Mock.Get(urlProperty).Setup(x => x.GetValue(null, null)).Returns(relativeUrl); Mock.Get(variationContextAccessor).Setup(x => x.VariationContext).Returns(variationContext); + Mock.Get(publishStatusQueryService).Setup(x => x.IsDocumentPublished(It.IsAny(), It.IsAny())).Returns(true); var publishedRequestBuilder = new PublishedRequestBuilder(new Uri(absoluteUrl, UriKind.Absolute), fileService); // Act diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs index b98d1b1731..1a4a73f800 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs @@ -99,7 +99,8 @@ public class HtmlImageSourceParserTests new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of(), + Mock.Of()); using (var reference = umbracoContextFactory.EnsureUmbracoContext()) { diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs index 5a35a096ee..ad2fb0b231 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs @@ -235,6 +235,11 @@ public class HtmlLocalLinkParserTests mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); + var publishStatusQueryService = new Mock(); + publishStatusQueryService + .Setup(x => x.IsDocumentPublished(It.IsAny(), It.IsAny())) + .Returns(true); + var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, Options.Create(webRoutingSettings), @@ -242,7 +247,8 @@ public class HtmlLocalLinkParserTests new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }), Mock.Of(), contentCache.Object, - navigationQueryService.Object); + navigationQueryService.Object, + publishStatusQueryService.Object); var linkParser = new HtmlLocalLinkParser(publishedUrlProvider);