Skip to content

Commit

Permalink
Merge branch 'release/15.1.1' into v15/dev
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/Umbraco.Core/Extensions/PublishedContentExtensions.cs
#	src/Umbraco.Web.UI.Client/package-lock.json
#	src/Umbraco.Web.UI.Client/package.json
#	version.json
  • Loading branch information
Migaroez committed Dec 12, 2024
2 parents 0920019 + 4f3dd04 commit cd25c9a
Show file tree
Hide file tree
Showing 105 changed files with 2,761 additions and 564 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public async Task<IActionResult> GetPublicAccess(CancellationToken cancellationT
}

Attempt<PublicAccessEntry?, PublicAccessOperationStatus> accessAttempt =
await _publicAccessService.GetEntryByContentKeyAsync(id);
await _publicAccessService.GetEntryByContentKeyWithoutAncestorsAsync(id);

if (accessAttempt.Success is false || accessAttempt.Result is null)
{
Expand Down
34 changes: 30 additions & 4 deletions src/Umbraco.Core/DeliveryApi/ApiContentRouteBuilder.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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(
Expand All @@ -25,18 +28,41 @@ public ApiContentRouteBuilder(
IRequestPreviewService requestPreviewService,
IOptionsMonitor<RequestHandlerSettings> 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> globalSettings,
IVariationContextAccessor variationContextAccessor,
IRequestPreviewService requestPreviewService,
IOptionsMonitor<RequestHandlerSettings> requestSettings,
IPublishedContentCache contentCache,
IDocumentNavigationQueryService navigationQueryService)
: this(
apiContentPathProvider,
globalSettings,
variationContextAccessor,
requestPreviewService,
requestSettings,
contentCache,
navigationQueryService,
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
{
}

public IApiContentRoute? Build(IPublishedContent content, string? culture = null)
{
if (content.ItemType != PublishedItemType.Content)
Expand Down Expand Up @@ -105,7 +131,7 @@ private IPublishedContent GetRoot(IPublishedContent content, bool isPreview)
{
if (isPreview is false)
{
return content.Root(_contentCache, _navigationQueryService);
return content.Root(_variationContextAccessor, _contentCache, _navigationQueryService, _publishStatusQueryService);
}

_navigationQueryService.TryGetRootKeys(out IEnumerable<Guid> rootKeys);
Expand All @@ -114,6 +140,6 @@ private IPublishedContent GetRoot(IPublishedContent content, bool isPreview)
// 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);
}
}
2,570 changes: 2,266 additions & 304 deletions src/Umbraco.Core/Extensions/PublishedContentExtensions.cs

Large diffs are not rendered by default.

30 changes: 27 additions & 3 deletions src/Umbraco.Core/Routing/ContentFinderByUrlAlias.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

/// <summary>
Expand All @@ -35,16 +38,37 @@ public ContentFinderByUrlAlias(
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<ContentFinderByUrlAlias> logger,
IPublishedValueFallback publishedValueFallback,
IVariationContextAccessor variationContextAccessor,
IUmbracoContextAccessor umbracoContextAccessor,
IPublishedContentCache contentCache,
IDocumentNavigationQueryService documentNavigationQueryService)
: this(
logger,
publishedValueFallback,
variationContextAccessor,
umbracoContextAccessor,
contentCache,
documentNavigationQueryService,
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
{
}

/// <summary>
/// Tries to find and assign an Umbraco document to a <c>PublishedRequest</c>.
/// </summary>
Expand Down Expand Up @@ -145,14 +169,14 @@ bool IsMatch(IPublishedContent c, string a1, string a2)
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)
{
Expand Down
32 changes: 29 additions & 3 deletions src/Umbraco.Core/Routing/UrlProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,48 @@ public class UrlProvider : IPublishedUrlProvider
/// <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>
public UrlProvider(
IUmbracoContextAccessor umbracoContextAccessor,
IOptions<WebRoutingSettings> routingSettings,
UrlProviderCollection urlProviders,
MediaUrlProviderCollection mediaUrlProviders,
IVariationContextAccessor variationContextAccessor,
IPublishedContentCache contentCache,
IDocumentNavigationQueryService navigationQueryService)
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<WebRoutingSettings> routingSettings,
UrlProviderCollection urlProviders,
MediaUrlProviderCollection mediaUrlProviders,
IVariationContextAccessor variationContextAccessor,
IPublishedContentCache contentCache,
IDocumentNavigationQueryService navigationQueryService)
: this(
umbracoContextAccessor,
routingSettings,
urlProviders,
mediaUrlProviders,
variationContextAccessor,
contentCache,
navigationQueryService,
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
{
}

[Obsolete("Use the constructor that takes all parameters. Scheduled for removal in V17.")]
public UrlProvider(
IUmbracoContextAccessor umbracoContextAccessor,
Expand All @@ -59,7 +83,8 @@ public UrlProvider(
mediaUrlProviders,
variationContextAccessor,
StaticServiceProvider.Instance.GetRequiredService<IPublishedContentCache>(),
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>())
StaticServiceProvider.Instance.GetRequiredService<IDocumentNavigationQueryService>(),
StaticServiceProvider.Instance.GetRequiredService<IPublishStatusQueryService>())
{
}

Expand All @@ -69,6 +94,7 @@ public UrlProvider(
private readonly IVariationContextAccessor _variationContextAccessor;
private readonly IPublishedContentCache _contentCache;
private readonly IDocumentNavigationQueryService _navigationQueryService;
private readonly IPublishStatusQueryService _publishStatusQueryService;

/// <summary>
/// Gets or sets the provider URL mode.
Expand Down Expand Up @@ -147,7 +173,7 @@ public string GetUrl(IPublishedContent? content, UrlMode mode = UrlMode.Default,
// 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;
}
Expand Down
17 changes: 11 additions & 6 deletions src/Umbraco.Core/Services/DomainService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,17 @@ private IDomain[] CalculateNewAssignedDomains(int contentId, DomainsUpdateModel
var sortOrder = 0;
foreach (DomainModel domainModel in updateModel.Domains)
{
IDomain assignedDomain = currentlyAssignedDomains.FirstOrDefault(domain => domainModel.DomainName.InvariantEquals(domain.DomainName))
?? new UmbracoDomain(domainModel.DomainName)
{
LanguageId = languageIdByIsoCode[domainModel.IsoCode],
RootContentId = contentId
};
IDomain? assignedDomain = currentlyAssignedDomains.FirstOrDefault(domain => domainModel.DomainName.InvariantEquals(domain.DomainName));

// If we do not have an assigned domain, or the domain-language has been changed, create new domain.
if (assignedDomain is null || assignedDomain.LanguageId != languageIdByIsoCode[domainModel.IsoCode])
{
assignedDomain = new UmbracoDomain(domainModel.DomainName)
{
LanguageId = languageIdByIsoCode[domainModel.IsoCode],
RootContentId = contentId
};
}

assignedDomain.SortOrder = sortOrder++;
newAssignedDomains.Add(assignedDomain);
Expand Down
15 changes: 15 additions & 0 deletions src/Umbraco.Core/Services/IPublicAccessService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,23 @@ public interface IPublicAccessService : IService
/// </summary>
/// <param name="key"></param>
/// <returns>Returns null if no entry is found</returns>
/// <remarks>
/// This method supports inheritance by considering ancestor entries (if any),
/// if no entry is found for the specified content key.
/// </remarks>
Task<Attempt<PublicAccessEntry?, PublicAccessOperationStatus>> GetEntryByContentKeyAsync(Guid key);

/// <summary>
/// Gets the entry defined for the content item based on a content key, without taking ancestor entries into account.
/// </summary>
/// <param name="key"></param>
/// <returns>Returns null if no entry is found</returns>
/// <remarks>
/// This method does not support inheritance. Use <see cref="GetEntryByContentKeyAsync"/> to include ancestor entries (if any).
/// </remarks>
Task<Attempt<PublicAccessEntry?, PublicAccessOperationStatus>> GetEntryByContentKeyWithoutAncestorsAsync(Guid key)
=> Task.FromResult(Attempt.SucceedWithStatus<PublicAccessEntry?, PublicAccessOperationStatus>(PublicAccessOperationStatus.EntryNotFound, null));

/// <summary>
/// Deletes the entry and all associated rules for a given key.
/// </summary>
Expand Down
41 changes: 41 additions & 0 deletions src/Umbraco.Core/Services/PublicAccessService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Globalization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
Expand All @@ -16,19 +18,41 @@ internal class PublicAccessService : RepositoryService, IPublicAccessService
private readonly IPublicAccessRepository _publicAccessRepository;
private readonly IEntityService _entityService;
private readonly IContentService _contentService;
private readonly IIdKeyMap _idKeyMap;

[Obsolete("Please use the constructor that accepts all parameter. Will be removed in V16.")]
public PublicAccessService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IPublicAccessRepository publicAccessRepository,
IEntityService entityService,
IContentService contentService)
: this(
provider,
loggerFactory,
eventMessagesFactory,
publicAccessRepository,
entityService,
contentService,
StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>())
{
}

public PublicAccessService(
ICoreScopeProvider provider,
ILoggerFactory loggerFactory,
IEventMessagesFactory eventMessagesFactory,
IPublicAccessRepository publicAccessRepository,
IEntityService entityService,
IContentService contentService,
IIdKeyMap idKeyMap)
: base(provider, loggerFactory, eventMessagesFactory)
{
_publicAccessRepository = publicAccessRepository;
_entityService = entityService;
_contentService = contentService;
_idKeyMap = idKeyMap;
}

/// <summary>
Expand Down Expand Up @@ -381,6 +405,23 @@ private Attempt<PublicAccessNodesValidationResult, PublicAccessOperationStatus>
return Task.FromResult(Attempt.SucceedWithStatus<PublicAccessEntry?, PublicAccessOperationStatus>(PublicAccessOperationStatus.Success, entry));
}

public async Task<Attempt<PublicAccessEntry?, PublicAccessOperationStatus>> GetEntryByContentKeyWithoutAncestorsAsync(Guid key)
{
Attempt<PublicAccessEntry?, PublicAccessOperationStatus> result = await GetEntryByContentKeyAsync(key);
if (result.Success is false || result.Result is null)
{
return result;
}

Attempt<Guid> idToKeyAttempt = _idKeyMap.GetKeyForId(result.Result.ProtectedNodeId, UmbracoObjectTypes.Document);
if (idToKeyAttempt.Success is false || idToKeyAttempt.Result != key)
{
return Attempt.SucceedWithStatus<PublicAccessEntry?, PublicAccessOperationStatus>(PublicAccessOperationStatus.EntryNotFound, null);
}

return result;
}

public async Task<Attempt<PublicAccessOperationStatus>> DeleteAsync(Guid key)
{
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
Expand Down
Loading

0 comments on commit cd25c9a

Please sign in to comment.