From daace4b4a02d7a215fa23d00e5aebae37f352cc3 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 21 Oct 2025 12:05:10 +0200 Subject: [PATCH] Publishing: Resolve exceptions on publish branch (#20464) * Reduce log level of image cropper converter to avoid flooding logs with expected exceptions. * Don't run publish branch long running operation on a background thread such that UmbracoContext is available. * Revert to background thread and use EnsureUmbracoContext to ensure we can get an IUmbracoContext in the URL providers. * Updated tests. * Applied suggestion from code review. * Clarified comment. --- .../Services/ContentPublishingService.cs | 12 ++++++++++-- .../ValueConverters/ImageCropperValueConverter.cs | 7 ++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Services/ContentPublishingService.cs b/src/Umbraco.Core/Services/ContentPublishingService.cs index 698efbda91..b46608aa14 100644 --- a/src/Umbraco.Core/Services/ContentPublishingService.cs +++ b/src/Umbraco.Core/Services/ContentPublishingService.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Models.ContentPublishing; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services.OperationStatus; +using Umbraco.Cms.Core.Web; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Services; @@ -26,6 +27,7 @@ internal sealed class ContentPublishingService : IContentPublishingService private readonly IRelationService _relationService; private readonly ILogger _logger; private readonly ILongRunningOperationService _longRunningOperationService; + private readonly IUmbracoContextFactory _umbracoContextFactory; public ContentPublishingService( ICoreScopeProvider coreScopeProvider, @@ -37,7 +39,8 @@ internal sealed class ContentPublishingService : IContentPublishingService IOptionsMonitor optionsMonitor, IRelationService relationService, ILogger logger, - ILongRunningOperationService longRunningOperationService) + ILongRunningOperationService longRunningOperationService, + IUmbracoContextFactory umbracoContextFactory) { _coreScopeProvider = coreScopeProvider; _contentService = contentService; @@ -53,6 +56,7 @@ internal sealed class ContentPublishingService : IContentPublishingService { _contentSettings = contentSettings; }); + _umbracoContextFactory = umbracoContextFactory; } /// @@ -290,7 +294,7 @@ internal sealed class ContentPublishingService : IContentPublishingService return MapInternalPublishingAttempt(minimalAttempt); } - _logger.LogInformation("Starting async background thread for publishing branch."); + _logger.LogDebug("Starting long running operation for publishing branch {Key} on background thread.", key); Attempt enqueueAttempt = await _longRunningOperationService.RunAsync( PublishBranchOperationType, async _ => await PerformPublishBranchAsync(key, cultures, publishBranchFilter, userKey, returnContent: false), @@ -324,6 +328,10 @@ internal sealed class ContentPublishingService : IContentPublishingService Guid userKey, bool returnContent) { + // Ensure we have an UmbracoContext in case running on a background thread so operations that run in the published notification handlers + // have access to this (e.g. webhooks). + using UmbracoContextReference umbracoContextReference = _umbracoContextFactory.EnsureUmbracoContext(); + using ICoreScope scope = _coreScopeProvider.CreateCoreScope(); IContent? content = _contentService.GetById(key); if (content is null) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs index 9cb5d57474..0218a10b63 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs @@ -1,6 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System.Text.Json; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Models.DeliveryApi; using Umbraco.Cms.Core.Models.PublishedContent; @@ -53,10 +54,10 @@ public class ImageCropperValueConverter : PropertyValueConverterBase, IDeliveryA { value = _jsonSerializer.Deserialize(sourceString); } - catch (Exception ex) + catch (JsonException ex) { - // cannot deserialize, assume it may be a raw image URL - _logger.LogError(ex, "Could not deserialize string '{JsonString}' into an image cropper value.", sourceString); + // Cannot deserialize, assume it may be a raw image URL. + _logger.LogDebug(ex, "Could not deserialize string '{JsonString}' into an image cropper value.", sourceString); value = new ImageCropperValue { Src = sourceString }; }