From 8f8b27f32c7e2ced7b3332d52ac252fab02e43fd Mon Sep 17 00:00:00 2001 From: MiguelGuedelha Date: Sun, 28 Jun 2026 15:40:30 +0100 Subject: [PATCH] ContentDeletingNotification handles both individual deletes from the Bin and global Empty Bin operations --- .../Core/IReadingTimeService.cs | 1 + .../Core/ReadingTimeNotificationHandler.cs | 30 ++++++++++++++++--- .../Persistence/IReadingTimeRepository.cs | 1 + .../Persistence/ReadingTimeRepository.cs | 16 ++++++++++ .../Infrastructure/ReadingTimeService.cs | 6 ++++ 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/jcdcdev.Umbraco.ReadingTime/Core/IReadingTimeService.cs b/src/jcdcdev.Umbraco.ReadingTime/Core/IReadingTimeService.cs index d920fd9..b91097f 100644 --- a/src/jcdcdev.Umbraco.ReadingTime/Core/IReadingTimeService.cs +++ b/src/jcdcdev.Umbraco.ReadingTime/Core/IReadingTimeService.cs @@ -9,6 +9,7 @@ public interface IReadingTimeService Task ScanAll(); Task Process(IContent item); Task DeleteAsync(Guid key); + Task DeleteAsync(IEnumerable keys); Task GetAsync(Guid key, Guid dataTypeKey); Task GetAsync(Guid key, int dataTypeId); } diff --git a/src/jcdcdev.Umbraco.ReadingTime/Core/ReadingTimeNotificationHandler.cs b/src/jcdcdev.Umbraco.ReadingTime/Core/ReadingTimeNotificationHandler.cs index 64b311c..cdc0d20 100644 --- a/src/jcdcdev.Umbraco.ReadingTime/Core/ReadingTimeNotificationHandler.cs +++ b/src/jcdcdev.Umbraco.ReadingTime/Core/ReadingTimeNotificationHandler.cs @@ -4,7 +4,6 @@ using jcdcdev.Umbraco.ReadingTime.Core.PropertyEditors; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; @@ -18,18 +17,41 @@ public class ReadingTimeNotificationHandler : { private readonly ILocalizedTextService _localizedTextService; private readonly IReadingTimeService _readingTimeService; + private readonly IContentService _contentService; - public ReadingTimeNotificationHandler(IReadingTimeService readingTimeService, ILocalizedTextService localizedTextService) + private const int ChildrenPageSize = 1000; + + public ReadingTimeNotificationHandler(IReadingTimeService readingTimeService, ILocalizedTextService localizedTextService, IContentService contentService) { _readingTimeService = readingTimeService; _localizedTextService = localizedTextService; + _contentService = contentService; } + // Both ContentDeletingNotification and ContentEmptyingRecycleBinNotification are called during emptying a global Recycle Bin empty operation + // Since individual deletes from within the Bin trigger just the ContentDeletingNotification and not the latter, this should cover both use cases public async Task HandleAsync(ContentDeletingNotification notification, CancellationToken cancellationToken) { - foreach (var content in notification.DeletedEntities) + foreach (var item in notification.DeletedEntities) { - await _readingTimeService.DeleteAsync(content.Key); + var toDelete = new List { item.Key }; + var pageIndex = 0; + + List pagedDescendants; + + do + { + pagedDescendants = _contentService + .GetPagedDescendants(item.Id, pageIndex, ChildrenPageSize, out _) + .Select(x => x.Key) + .ToList(); + + toDelete.AddRange(pagedDescendants); + + pageIndex++; + } while (pagedDescendants.Count == ChildrenPageSize); + + await _readingTimeService.DeleteAsync(toDelete); } } diff --git a/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/IReadingTimeRepository.cs b/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/IReadingTimeRepository.cs index 0495ef9..001d856 100644 --- a/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/IReadingTimeRepository.cs +++ b/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/IReadingTimeRepository.cs @@ -6,6 +6,7 @@ namespace jcdcdev.Umbraco.ReadingTime.Infrastructure.Persistence; public interface IReadingTimeRepository { Task DeleteAsync(Guid key); + Task DeleteAsync(IEnumerable key); Task GetOrCreate(Guid key, IDataType dataType); Task PersistAsync(ReadingTimeDto dto); Task Get(Guid key, int dataTypeId); diff --git a/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/ReadingTimeRepository.cs b/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/ReadingTimeRepository.cs index 3704ff4..84a28a3 100644 --- a/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/ReadingTimeRepository.cs +++ b/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/Persistence/ReadingTimeRepository.cs @@ -31,6 +31,22 @@ public Task DeleteAsync(Guid key) return Task.FromResult(data); } + public Task DeleteAsync(IEnumerable keys) + { + using var scope = _scopeProvider.CreateScope(); + + var sql = scope.SqlContext + .Sql() + .Delete() + .WhereIn(x => x.Key, keys); + + var data = scope.Database.Execute(sql); + + scope.Complete(); + + return Task.FromResult(data); + } + public async Task GetOrCreate(Guid key, IDataType dataType) { var dto = await Get(key, dataType.Id); diff --git a/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/ReadingTimeService.cs b/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/ReadingTimeService.cs index 890596c..040da83 100644 --- a/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/ReadingTimeService.cs +++ b/src/jcdcdev.Umbraco.ReadingTime/Infrastructure/ReadingTimeService.cs @@ -42,6 +42,12 @@ public async Task DeleteAsync(Guid key) return await _readingTimeRepository.DeleteAsync(key); } + public async Task DeleteAsync(IEnumerable keys) + { + _logger.LogDebug("Deleting reading time for {Keys}", keys); + return await _readingTimeRepository.DeleteAsync(keys); + } + public async Task ScanTree(int homeId) { var content = _contentService.GetById(homeId);