Help needed to understand a few properties inside ContentPublishingNotification

I am writting a custom handler that inherits from INotificationHandler<ContentPublishingNotification> so I can do some custom work to the document type when it is getting published but some of that work needs to know if the document was already published or not. To that end I need help understanding a few values of the PublsihedEntities class.

.Published is this true only if the document was already published before or do I need to look at PublishedState for that value?

.PublishedDateis this value for the very first time the document was ever published or the last time it was published?

It’s PublishDate (not PublishedDate).

Published = tells you whether there’s already a live published version. During ContentPublishingNotification, PublishedState will typically be Publishing because the publish pipeline has already started.

If you need “was this already published before this operation?”, you can use:

bool wasAlreadyPublished = content.HasIdentity && content.Published && content.PublishedVersionId > 0;

PublishDate= is not a “first ever published” timestamp - it’s effectively the last published date of the current published snapshot.

If you need a true first-published date, you’ll need to store it yourself.

For variant content:

content.GetPublishDate(culture)

Hi @Eaglef90 ,

Here is a quick setup you can try:

Step 1: Update your Document Type Add a new Date Picker property to your Document Type with the alias firstPublishedDate.

Step 2: Create the Handler Write a handler that checks if this property is empty, and stamps it if it is. (Note: Modern Umbraco strictly enforces UTC, so always use DateTime.UtcNow).

using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;

namespace MyQuickUmbracoSitetestsql10.Notifications
{
    public class TrackPublishStateHandler : INotificationHandler<ContentPublishingNotification>
    {
        private readonly ILogger<TrackPublishStateHandler> _logger;

        // Inject the logger
        public TrackPublishStateHandler(ILogger<TrackPublishStateHandler> logger)
        {
            _logger = logger;
        }

        public void Handle(ContentPublishingNotification notification)
        {
            foreach (var node in notification.PublishedEntities)
            {
                // Log that the event fired for this specific node
                _logger.LogInformation("==> Publishing event triggered for node: {NodeName} (ID: {NodeId})", node.Name, node.Id);

                // 1. Check for First Publish
                if (node.HasProperty("firstPublishedDate"))
                {
                    if (node.GetValue("firstPublishedDate") == null)
                    {
                        _logger.LogInformation("==> RESULT: Node '{NodeName}' is being published for the VERY FIRST TIME. Stamping UTC date.", node.Name);
                        node.SetValue("firstPublishedDate", DateTime.UtcNow);
                    }
                    else
                    {
                        _logger.LogInformation("==> RESULT: Node '{NodeName}' already has a first publish date: {Date}", node.Name, node.GetValue("firstPublishedDate"));
                    }
                }
                else
                {
                    _logger.LogWarning("==> WARNING: Node '{NodeName}' does not have a 'firstPublishedDate' property on its Document Type.", node.Name);
                }

                // 2. Check Previous Publish State
                if (node.Published)
                {
                    _logger.LogInformation("==> RESULT: Node '{NodeName}' was ALREADY LIVE. Previous publish date was: {Date}", node.Name, node.PublishDate);
                }
                else
                {
                    _logger.LogInformation("==> RESULT: Node '{NodeName}' is currently UNPUBLISHED (it is a new draft or was previously unpublished).", node.Name);
                }
            }
        }
    }
}

(If you are using Variant/Multilingual content, remember to loop through node.AvailableCultures and pass the culture into the SetValue method).

Step 3: Register in Program.cs Don’t forget to register the handler in your pipeline! In your Program.cs file, attach it to the Umbraco builder:

builder.CreateUmbracoBuilder()
    .AddBackOffice()
    .AddWebsite()
    .AddComposers()
    .AddNotificationHandler<ContentPublishingNotification, TrackPublishStateHandler>()
    .Build();

How to quickly test this: Save and run your project. Go to the Umbraco Backoffice, create a new page, and hit “Save and Publish”. Look at your custom firstPublishedDate property—you’ll see it automatically populated. If you unpublish, make a change, and publish again, that original date will remain intact.

Hope this helps!

Sorry about the typeo and thanks for the info. Question though, Why do I need HasIdentity and PublishedVersionId in addition to Publsihed?

@ShekharTarare Thanks for the AI generated code set but it does not answer my questions, though it would let me see the values I don’t think simply knowing the values answers the question either.

You don’t need all three. In Umbraco’s own ContentService, “was it already published before this publish?” is literally:

content.HasIdentity && content.Published

There’s no PublishedVersionId in that check.

HasIdentity - only applies to content that’s already saved in the database (so you’re not treating a brand‑new, unsaved item as “previously published”).

Published - whether it already had a published version.

So: Published alone is often fine if you know the node is always saved; HasIdentity && Published matches what core does. PublishedVersionId isn’t required for that same meaning I only mentioned it before as an optional extra, not as something Umbraco combines there.

Hi Sorry @Eaglef90 for misunderstanding,

I thought @BishalTimalsina12 covered most of the things. The code I shared which I tested 2 times, it was just for the below mentioned part.

Cool, thanks for the explanations.

@ShekharTarare No big. I did not understand that was what you where trying to do.

1 Like