Is there a way in Umbraco 17 to stop Unpublish an Item if it linked to another page

Hi,

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.

Hi @darrenhunterEmerald

Do you mean that you don’t want a page to be unpublished if it is linked from another page? If not, I’m not sure I understand your question?

Justin

Yes if the page is linked, we don’t want it been unpublished.

Hi @darrenhunterEmerald

Something like this may work (courtesy of AI)

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.

Justin

ContentService Notifications Example | CMS | Umbraco Documentation

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…

Hi @mistyn8

I did think it wouldn’t be a bullet-proof example, but it may suit @darrenhunterEmerald needs.

Justin