From 87e9797a36ebb213eddc40e82af2e1307ec52627 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 10 Sep 2024 14:59:50 +0200 Subject: [PATCH] Temp commit, move to cache refreshers. --- .../Factories/DocumentUrlFactory.cs | 36 ++++++++++- .../Implement/ContentCacheRefresher.cs | 61 +++++++++++++++++++ .../Handlers/RoutingNotificationHandler.cs | 61 ++++++++++--------- .../Services/DocumentUrlService.cs | 47 ++++++++++++++ .../Services/IDocumentUrlService.cs | 6 ++ .../UmbracoBuilder.CoreServices.cs | 10 +-- .../Services/DocumentUrlServiceTest.cs | 10 +-- 7 files changed, 190 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs index 7ec5e937b4e1..cdcec3b173ab 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/DocumentUrlFactory.cs @@ -1,6 +1,8 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Api.Management.ViewModels.Content; using Umbraco.Cms.Api.Management.ViewModels.Document; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Routing; @@ -21,7 +23,9 @@ public class DocumentUrlFactory : IDocumentUrlFactory private readonly ILoggerFactory _loggerFactory; private readonly UriUtility _uriUtility; private readonly IPublishedUrlProvider _publishedUrlProvider; + private readonly IDocumentUrlService _documentUrlService; + [Obsolete("Use the non-obsolete constructor. This will be removed in Umbraco 16")] public DocumentUrlFactory( IPublishedRouter publishedRouter, IUmbracoContextAccessor umbracoContextAccessor, @@ -32,6 +36,33 @@ public DocumentUrlFactory( ILoggerFactory loggerFactory, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider) + :this( + publishedRouter, + umbracoContextAccessor, + languageService, + localizedTextService, + contentService, + variationContextAccessor, + loggerFactory, + uriUtility, + publishedUrlProvider, + StaticServiceProvider.Instance.GetRequiredService()) + { + + } + + + public DocumentUrlFactory( + IPublishedRouter publishedRouter, + IUmbracoContextAccessor umbracoContextAccessor, + ILanguageService languageService, + ILocalizedTextService localizedTextService, + IContentService contentService, + IVariationContextAccessor variationContextAccessor, + ILoggerFactory loggerFactory, + UriUtility uriUtility, + IPublishedUrlProvider publishedUrlProvider, + IDocumentUrlService documentUrlService) { _publishedRouter = publishedRouter; _umbracoContextAccessor = umbracoContextAccessor; @@ -42,12 +73,15 @@ public DocumentUrlFactory( _loggerFactory = loggerFactory; _uriUtility = uriUtility; _publishedUrlProvider = publishedUrlProvider; + _documentUrlService = documentUrlService; } public async Task> CreateUrlsAsync(IContent content) { IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); + // var urls = await _documentUrlService.ListUrlsAsync(content.Key); + //TODO replace with documentUrlService IEnumerable urlInfos = await content.GetContentUrlsAsync( _publishedRouter, diff --git a/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs b/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs index 779b22fe6888..2e53e5548c82 100644 --- a/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/Refreshers/Implement/ContentCacheRefresher.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Notifications; @@ -14,9 +16,11 @@ public sealed class ContentCacheRefresher : PayloadCacheRefresherBase { private readonly IDomainService _domainService; + private readonly IDocumentUrlService _documentUrlService; private readonly IIdKeyMap _idKeyMap; private readonly IPublishedSnapshotService _publishedSnapshotService; + [Obsolete("Use non-obsolete constructor. This will be removed in Umbraco 16")] public ContentCacheRefresher( AppCaches appCaches, IJsonSerializer serializer, @@ -25,11 +29,34 @@ public ContentCacheRefresher( IDomainService domainService, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory) + : this( + appCaches, + serializer, + publishedSnapshotService, + idKeyMap, + domainService, + eventAggregator, + factory, + StaticServiceProvider.Instance.GetRequiredService()) + { + + } + + public ContentCacheRefresher( + AppCaches appCaches, + IJsonSerializer serializer, + IPublishedSnapshotService publishedSnapshotService, + IIdKeyMap idKeyMap, + IDomainService domainService, + IEventAggregator eventAggregator, + ICacheRefresherNotificationFactory factory, + IDocumentUrlService documentUrlService) : base(appCaches, serializer, eventAggregator, factory) { _publishedSnapshotService = publishedSnapshotService; _idKeyMap = idKeyMap; _domainService = domainService; + _documentUrlService = documentUrlService; } #region Indirect @@ -75,6 +102,7 @@ public override void Refresh(JsonPayload[] payloads) // By GUID Key isolatedCache.Clear(RepositoryCacheKeys.GetKey(payload.Key)); + _idKeyMap.ClearCache(payload.Id); // remove those that are in the branch @@ -89,6 +117,10 @@ public override void Refresh(JsonPayload[] payloads) { idsRemoved.Add(payload.Id); } + + + HandleRouting(payload); + } if (idsRemoved.Count > 0) @@ -129,6 +161,35 @@ public override void Refresh(JsonPayload[] payloads) base.Refresh(payloads); } + private void HandleRouting(JsonPayload payload) + { + //TODO test at denne methode bliver kaldt ved save også. + + if(payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) + { + var key = payload.Key ?? _idKeyMap.GetKeyForId(payload.Id, UmbracoObjectTypes.Document).Result; + _documentUrlService.DeleteUrlsAndDescendantsAsync(key).GetAwaiter().GetResult(); + } + if(payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) + { + //TODO Force rebuilt instead + _documentUrlService.RebuildAllUrlsAsync().GetAwaiter().GetResult(); //TODO make async + } + + if(payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode)) + { + var key = payload.Key ?? _idKeyMap.GetKeyForId(payload.Id, UmbracoObjectTypes.Document).Result; + _documentUrlService.CreateOrUpdateUrlSegmentsAsync(key).GetAwaiter().GetResult(); + } + + if(payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) + { + var key = payload.Key ?? _idKeyMap.GetKeyForId(payload.Id, UmbracoObjectTypes.Document).Result; + _documentUrlService.CreateOrUpdateUrlSegmentsWithDescendantsAsync(key).GetAwaiter().GetResult(); + } + + } + // these events should never trigger // everything should be PAYLOAD/JSON public override void RefreshAll() => throw new NotSupportedException(); diff --git a/src/Umbraco.Core/Handlers/RoutingNotificationHandler.cs b/src/Umbraco.Core/Handlers/RoutingNotificationHandler.cs index fb8fb2482ba6..36e4dd51767c 100644 --- a/src/Umbraco.Core/Handlers/RoutingNotificationHandler.cs +++ b/src/Umbraco.Core/Handlers/RoutingNotificationHandler.cs @@ -1,30 +1,31 @@ -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Notifications; -using Umbraco.Cms.Core.Services; - -namespace Umbraco.Cms.Core.Handlers; - -public class RoutingNotificationHandler - : INotificationAsyncHandler, - INotificationAsyncHandler, - INotificationAsyncHandler, - INotificationAsyncHandler -{ - private readonly IDocumentUrlService _documentUrlService; - private readonly IContentService _contentService; - private readonly IIdKeyMap _idKeyMap; - - public RoutingNotificationHandler(IDocumentUrlService documentUrlService, IContentService contentService, IIdKeyMap idKeyMap) - { - _documentUrlService = documentUrlService; - _contentService = contentService; - _idKeyMap = idKeyMap; - } - - public async Task HandleAsync(ContentPublishedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.CreateOrUpdateUrlSegmentsAsync(notification.PublishedEntities); - public async Task HandleAsync(ContentDeletedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.DeleteUrlsAsync(notification.DeletedEntities); - public async Task HandleAsync(ContentUnpublishedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.DeleteUrlsAsync(notification.UnpublishedEntities); - - public async Task HandleAsync(ContentSavedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.CreateOrUpdateUrlSegmentsAsync(notification.SavedEntities); -} +// using Umbraco.Cms.Core.Events; +// using Umbraco.Cms.Core.Models; +// using Umbraco.Cms.Core.Notifications; +// using Umbraco.Cms.Core.Services; +// +// namespace Umbraco.Cms.Core.Handlers; +// +// public class RoutingNotificationHandler +// : INotificationAsyncHandler, +// INotificationAsyncHandler, +// INotificationAsyncHandler, +// INotificationAsyncHandler +// { +// private readonly IDocumentUrlService _documentUrlService; +// private readonly IContentService _contentService; +// private readonly IIdKeyMap _idKeyMap; +// +// public RoutingNotificationHandler(IDocumentUrlService documentUrlService, IContentService contentService, IIdKeyMap idKeyMap) +// { +// _documentUrlService = documentUrlService; +// _contentService = contentService; +// _idKeyMap = idKeyMap; +// } +// +// // TODO move to content refreshers +// public async Task HandleAsync(ContentPublishedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.CreateOrUpdateUrlSegmentsAsync(notification.PublishedEntities); +// public async Task HandleAsync(ContentDeletedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.DeleteUrlsAsync(notification.DeletedEntities); +// public async Task HandleAsync(ContentUnpublishedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.DeleteUrlsAsync(notification.UnpublishedEntities); +// +// public async Task HandleAsync(ContentSavedNotification notification, CancellationToken cancellationToken) => await _documentUrlService.CreateOrUpdateUrlSegmentsAsync(notification.SavedEntities); +// } diff --git a/src/Umbraco.Core/Services/DocumentUrlService.cs b/src/Umbraco.Core/Services/DocumentUrlService.cs index 4cd45781f8f0..cce5b011ad52 100644 --- a/src/Umbraco.Core/Services/DocumentUrlService.cs +++ b/src/Umbraco.Core/Services/DocumentUrlService.cs @@ -6,6 +6,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; +using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.Navigation; using Umbraco.Cms.Core.Strings; @@ -415,6 +416,52 @@ public async Task DeleteUrlsAsync(IEnumerable documents) return runnerKey; } +// public async Task> ListUrlsAsync(Guid contentKey) +// { +// if(_documentNavigationQueryService.TryGetAncestorsKeys(contentKey, out var ancestorsKeys)) +// { +// var languages = await _languageService.GetAllAsync(); +// var cultures = languages.Select(x=>x.IsoCode); +// +// foreach (Guid ancestorKey in ancestorsKeys) +// { +// TODO her skal vi lave skabe en full url med domainer. +// +// foreach (var culture in cultures) +// { +// if (_cache.TryGetValue(CreateCacheKey(ancestorKey, culture, false), out PublishedDocumentUrlSegment? urlSegment)) +// { +// //yield return new UrlInfo(urlSegment.UrlSegment, true, culture); +// } +// } +// } +// } +// } + + public async Task CreateOrUpdateUrlSegmentsWithDescendantsAsync(Guid key) + { + var id = _idKeyMap.GetIdForKey(key, UmbracoObjectTypes.Document).Result; + IEnumerable contents = _contentService.GetPagedDescendants(id, 0, int.MaxValue, out _); + await CreateOrUpdateUrlSegmentsAsync(contents); + } + + public async Task DeleteUrlsAndDescendantsAsync(Guid key) + { + var id = _idKeyMap.GetIdForKey(key, UmbracoObjectTypes.Document).Result; + IEnumerable contents = _contentService.GetPagedDescendants(id, 0, int.MaxValue, out _); + await DeleteUrlsAsync(contents); + } + + public async Task CreateOrUpdateUrlSegmentsAsync(Guid key) + { + IContent? content = _contentService.GetById(key); + + if (content is not null) + { + await CreateOrUpdateUrlSegmentsAsync(content.Yield()); + } + } + //TODO test cases: // - Find the root, when a domain is set // - Find a nested child, when a domain is set diff --git a/src/Umbraco.Core/Services/IDocumentUrlService.cs b/src/Umbraco.Core/Services/IDocumentUrlService.cs index 5609b4df77a8..90c04925e401 100644 --- a/src/Umbraco.Core/Services/IDocumentUrlService.cs +++ b/src/Umbraco.Core/Services/IDocumentUrlService.cs @@ -1,5 +1,6 @@ using Umbraco.Cms.Core.Media.EmbedProviders; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Routing; namespace Umbraco.Cms.Core.Services; @@ -13,6 +14,7 @@ public interface IDocumentUrlService /// Task InitAsync(bool forceEmpty, CancellationToken cancellationToken); + Task RebuildAllUrlsAsync(); /// /// Gets the Url from a document key, culture and segment. Preview urls are returned if isPreview is true. /// @@ -29,4 +31,8 @@ public interface IDocumentUrlService Task DeleteUrlsAsync(IEnumerable documents); Guid? GetDocumentKeyByRoute(string route, string? culture, int? documentStartNodeId, bool isDraft); + //Task> ListUrlsAsync(Guid contentKey); + Task CreateOrUpdateUrlSegmentsWithDescendantsAsync(Guid key); + Task DeleteUrlsAndDescendantsAsync(Guid key); + Task CreateOrUpdateUrlSegmentsAsync(Guid key); } diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 1005776a1f80..e89f50bb288b 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -416,11 +416,11 @@ public static IUmbracoBuilder AddCoreNotifications(this IUmbracoBuilder builder) .AddNotificationAsyncHandler(); // Handlers for routing - builder - .AddNotificationAsyncHandler() - .AddNotificationAsyncHandler() - .AddNotificationAsyncHandler() - .AddNotificationAsyncHandler(); + // builder + // .AddNotificationAsyncHandler() + // .AddNotificationAsyncHandler() + // .AddNotificationAsyncHandler() + // .AddNotificationAsyncHandler(); return builder; } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs index d2079ab0001f..82ab1073fe19 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Services/DocumentUrlServiceTest.cs @@ -20,11 +20,11 @@ public class DocumentUrlServiceTest : UmbracoIntegrationTestWithContent protected override void CustomTestSetup(IUmbracoBuilder builder) { - builder - .AddNotificationAsyncHandler() - .AddNotificationAsyncHandler() - .AddNotificationAsyncHandler() - .AddNotificationAsyncHandler(); + // builder + // .AddNotificationAsyncHandler() + // .AddNotificationAsyncHandler() + // .AddNotificationAsyncHandler() + // .AddNotificationAsyncHandler(); } //