Home » Customization » Who Just Published That? Log Publishing Statistics in the Sitecore Client

Who Just Published That? Log Publishing Statistics in the Sitecore Client

Sitecore Technology MVP 2016
Sitecore MVP 2015
Sitecore MVP 2014

Enter your email address to follow this blog and receive notifications of new posts by email.


Time and time again, I keep hearing about content in Sitecore being published prematurely, and this usually occurs by accident — perhaps a cat walks across the malefactor’s keyboard — although I have a feeling something else might driving these erroneous publishes.

However, in some instances, the malefactor will not fess up to invoking said publish.

Seeking out who had published a Sitecore item is beyond the know how of most end users, and usually requires an advanced user or developer to fish around in the Sitecore log to ascertain who published what and when.

Here’s an example of publishing information in my local sandbox’s instance of a publish I had done earlier this evening:


Given that I keep hearing about this happening, I decided it would be a great exercise to develop a feature to bring publishing information into the Sitecore client. We can accomplish this by building a custom PublishItemProcessor pipeline to log statistics into publishing fields.

First, I created a template containing fields to hold publishing information.


These fields will only keep track of who published an item last, similar to how the fields in the Statistics section in the Standard Template.

Next, I set the title of my fields to not show underscores:


I then added my template to the Standard Template:


Now that my publishing fields are in Sitecore, It’s time to build a custom PublishItemProcessor (Sitecore.Publishing.Pipelines.PublishItem.PublishItemProcessor):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Data.Fields;
using Sitecore.Diagnostics;
using Sitecore.Publishing.Pipelines.PublishItem;
using Sitecore.SecurityModel;

namespace Sitecore.Sandbox.Pipelines.Publishing
    public class UpdatePublishingStatistics : PublishItemProcessor
        private const string PublishedFieldName = "__Published";
        private const string PublishedByFieldName = "__Published By";

        public override void Process(PublishItemContext context)

        private void SetPublishingStatisticsFields(PublishItemContext context)
            Assert.ArgumentNotNull(context, "context");
            Assert.ArgumentNotNull(context.PublishOptions, "context.PublishOptions");
            Assert.ArgumentNotNull(context.PublishOptions.SourceDatabase, "context.PublishOptions.SourceDatabase");
            Assert.ArgumentNotNull(context.PublishOptions.TargetDatabase, "context.PublishOptions.TargetDatabase");
            Assert.ArgumentCondition(!ID.IsNullOrEmpty(context.ItemId), "context.ItemId", "context.ItemId must be set!");
            Assert.ArgumentNotNull(context.User, "context.User");
            SetPublishingStatisticsFields(context.PublishOptions.SourceDatabase, context.ItemId, context.User.Name);
            SetPublishingStatisticsFields(context.PublishOptions.TargetDatabase, context.ItemId, context.User.Name);

        private void SetPublishingStatisticsFields(Database database, ID itemId, string userName)
            Assert.ArgumentNotNull(database, "database");
            Item item = TryGetItem(database, itemId);

            if (HasPublishingStatisticsFields(item))
                SetPublishingStatisticsFields(item, DateUtil.IsoNow, userName);

        private void SetPublishingStatisticsFields(Item item, string isoDateTime, string userName)
            Assert.ArgumentNotNull(item, "item");
            Assert.ArgumentNotNullOrEmpty(isoDateTime, "isoDateTime");
            Assert.ArgumentNotNullOrEmpty(userName, "userName");

            using (new SecurityDisabler())
                item.Fields[PublishedFieldName].Value = DateUtil.IsoNow;
                item.Fields[PublishedByFieldName].Value = userName;

        private Item TryGetItem(Database database, ID itemId)
                return database.Items[itemId];
            catch (Exception ex)
                Log.Error(this.ToString(), ex, this);

            return null;

        private static bool HasPublishingStatisticsFields(Item item)
            Assert.ArgumentNotNull(item, "item");
            return item.Fields[PublishedFieldName] != null
                    && item.Fields[PublishedByFieldName] != null;

My PublishItemProcessor sets the publishing statistics on my newly added fields on the item in both the source and target databases, and only when the publishing fields exist on the published item.

I then added my PublishItemProcessor after the UpdateStatistics PublishItemProcessor in a new patch config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
        <processor type="Sitecore.Sandbox.Pipelines.Publishing.UpdatePublishingStatistics, Sitecore.Sandbox"
					patch:after="processor[@type='Sitecore.Publishing.Pipelines.PublishItem.UpdateStatistics, Sitecore.Kernel']" />

I created a new page for testing, put some dummy data in some fields and then published:


Beware — you can no longer hide after publishing when you shouldn’t! 🙂



  1. Tuan V. Le says:

    Great stuff, Mike!!!

  2. […] これをお客さんに見せれば納得するはずです。ただ、一般のお客さんにログファイルを検索させるわけいけません。これについて、同僚のマイケルと話をしたら、彼はカスタムフィールドを定義し、カスタムPublishItemProcessor を追加すりことでこのパブリシュの情報をサイトコアにて見ることができるようにできます。詳細およびコードはここで掲載されています。 […]

  3. tyshun says:

    This can slow down publishing quite a bit when content editors publish related Items since this has to update every single item that get’s published

  4. csuwannarat says:

    Hey Mike…
    By updating “published date” and “published by fields”, are updated date and updated by changed as well? I assume this is going to create rows in the EventQueue table, right?

  5. […] by whom, published where, and when.  While there may be Sitecore Marketplace modules and other solutions to this sort of challenge, they require customizations or at least package installations by the customer — and none of […]


Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: