Is there a way to stop a page from been Unpublished when Unpublished its linked to another page, I sort of added a fix to a site I am using, it just means if we any any other block list item or Block Grid Item then I need to apply a fix to it.
I been looking at ContentUnpublishingNotification but can’t seem to find a way to get the item been unpublished.
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Services;
namespace MySite.Notifications;
/// <summary>
/// Cancels an unpublish if any other published page links to the page being unpublished.
/// Uses the automatic "umbDocument" relations Umbraco maintains for reference-tracking
/// property editors (RTE, Multi URL Picker, Content Picker, block editors, etc.).
/// </summary>
public class BlockUnpublishIfLinkedHandler(
IRelationService relationService,
IContentService contentService,
ILogger<BlockUnpublishIfLinkedHandler> logger)
: INotificationHandler<ContentUnpublishingNotification>
{
// How many linking page names to show in the backoffice message.
private const int MaxNamesInMessage = 5;
public void Handle(ContentUnpublishingNotification notification)
{
// Pages being unpublished together in this operation shouldn't block each other.
var unpublishingIds = notification.UnpublishedEntities.Select(x => x.Id).ToHashSet();
foreach (IContent content in notification.UnpublishedEntities)
{
// Relations where this page is the child = pages that link TO this page.
var linkingIds = relationService
.GetByChildId(content.Id, Constants.Conventions.RelationTypes.RelatedDocumentAlias)
.Select(r => r.ParentId)
.Where(id => id != content.Id && !unpublishingIds.Contains(id))
.Distinct()
.ToArray();
if (linkingIds.Length == 0)
{
continue;
}
// Only care about linking pages that are live.
var publishedLinkers = contentService.GetByIds(linkingIds)
.Where(x => x.Published && !x.Trashed)
.ToList();
if (publishedLinkers.Count == 0)
{
continue;
}
var names = string.Join(", ", publishedLinkers.Take(MaxNamesInMessage).Select(x => $"'{x.Name}'"));
var more = publishedLinkers.Count > MaxNamesInMessage
? $" and {publishedLinkers.Count - MaxNamesInMessage} more"
: string.Empty;
logger.LogInformation(
"Blocked unpublish of {ContentName} ({ContentKey}): linked from {Count} published page(s).",
content.Name, content.Key, publishedLinkers.Count);
notification.CancelOperation(new EventMessage(
"Unpublish blocked",
$"'{content.Name}' is linked from {names}{more}. Remove those links before unpublishing.",
EventMessageType.Error));
return;
}
}
}
public class BlockUnpublishIfLinkedComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
=> builder.AddNotificationHandler<ContentUnpublishingNotification, BlockUnpublishIfLinkedHandler>();
}
It won’t pick up manual links in the RTE though - only pages linked via pickers in the backoffice.
there is a caveat though… if unpublishing a single language (eg variants) you don’t get the ContentUnpublishingNotification
and have revert to ContentPublishingNotification
with if (notification.IsUnpublishingCulture(node, {lang}))
Also using any custom property editors they might not have implemented the relationship tracking…