Content Editor Warning to Display Publishing Status

For the current edition of this blog post, see:

http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2010/11/Content-Editor-Warning-to-Display-Publishing-Status.aspx

At dreamcore 2010, I was prepared to show how to add a warning to the Content Editor in the Sitecore ASP.NET CMS, but I ran out of time. I based that demo preparation on the requirements described in this thread on the Sitecore Developer Network forums. Coincidentally, someone approached me after my presentation and asked about another issue for which I thought a Content Editor warning was appropriate. This post describes their issue and provides a Content Editor warning prototype to meet those requirements.

You can add any number of warnings in the Content Editor to alert the CMS user of various conditions. For example, you might want to alert the user if the selected item does not exist in a publishing target database, to indicate that they might want to publish that item. Or you might want to indicate if the latest version does not exist in a publishing target database. Or you might want to indicate that the latest revision of the item does not exist in a publishing target database (a revision is like a version of a version – each time you change a version of an item, Sitecore assigns a globally unique revision identifier to that version).

To add a warning to the content editor, implement a pipeline processor that contains a method named Process that accepts a parameter of type Sitecore.Pipelines.GetContentEditorWarnings.GetContentEditorWarningsArgs. Within the method, call the Add() method on the GetContentEditorWarningsArgs object to add a warning, and then set the Title and Text properties of the Sitecore.Pipelines.GetContentEditorWarnings.GetContentEditorWarningsArgs.ContentEditorWarning object that that method returns. Then, add that processor to the getContentEditorWarnings pipeline defined in the web.config file.

For example, in web.config:

<getContentEditorWarnings>

  <processor type="Sitecore.Sharedsource.Pipelines.GetContentEditorWarnings.PublishingStatus, dreamcore">
    <sourceDatabase>master</sourceDatabase>
  </processor>
  …

The order of the processor definitions in web.config controls the order in which corresponding warnings appear in the Content Editor.

My prototype of a Content Editor warning to meet the define requirements follows.

namespace Sitecore.Sharedsource.Pipelines.GetContentEditorWarnings
{
  using System;

  public class PublishingStatus
  {
    public string SourceDatabase
    {
      get;
      set;
    }

    public void Process(
      Sitecore.Pipelines.GetContentEditorWarnings.GetContentEditorWarningsArgs args)
    {
      Sitecore.Diagnostics.Assert.IsNotNullOrEmpty(this.SourceDatabase, "SourceDatabase");
      Sitecore.Diagnostics.Assert.IsNotNull(args, "args");
      Sitecore.Data.Items.Item sourceItem = args.Item;
      Sitecore.Diagnostics.Assert.IsNotNull(sourceItem, "args.Item");

      if (String.Compare(sourceItem.Database.Name, this.SourceDatabase, false) != 0)
      {
        return;
      }

      if (!this.IsLatest(sourceItem))
      {
        return;
      }

      string message = this.GetMessage(sourceItem);

      if (!String.IsNullOrEmpty(message))
      {
        this.AddWarning(message, sourceItem, args);
      }
    }

    protected bool IsLatest(Sitecore.Data.Items.Item item)
    {
      Sitecore.Data.Items.Item latest = item.Database.GetItem(
        item.ID,
        item.Language,
        Sitecore.Data.Version.Latest);

      if (latest.Version.Number != item.Version.Number)
      {
        return false;
      }

      return true;
    }

    protected string GetMessage(Sitecore.Data.Items.Item item)
    {
      Sitecore.Data.Items.Item targetsRoot =
        item.Database.GetItem("/sitecore/system/publishing targets");
      Sitecore.Diagnostics.Assert.IsNotNull(targetsRoot, "/sitecore/system/publishing targets");
      string message = String.Empty;

      foreach (Sitecore.Data.Items.Item target in targetsRoot.Children)
      {
        if (!this.PublishingTargetApplies(item, target.ID))
        {
          continue;
        }

        string dbName = target[Sitecore.FieldIDs.PublishingTargetDatabase];
        Sitecore.Diagnostics.Assert.IsNotNullOrEmpty(dbName, target.Paths.FullPath);
        Sitecore.Data.Database db = Sitecore.Configuration.Factory.GetDatabase(dbName);
        Sitecore.Diagnostics.Assert.IsNotNull(db, dbName);
        Sitecore.Data.Items.Item targetItem = db.GetItem(item.ID, item.Language);

        if (targetItem == null)
        {
          message += "You have not published this item to the " + dbName + " database.<br />";
        }
        else if (item.Version.Number != targetItem.Version.Number)
        {
          message += "You have not published this version to the " + dbName + " database.<br />";
        }
        else if (item[Sitecore.FieldIDs.Revision] != targetItem[Sitecore.FieldIDs.Revision])
        {
          message += "You have not published this revision to the "
            + dbName
            + " database.<br />";
        }
      }

      return message;
    }

    protected bool PublishingTargetApplies(
      Sitecore.Data.Items.Item item,
      Sitecore.Data.ID publishingTarget)
    {
      while (item != null)
      {
        string restricted = item[Sitecore.FieldIDs.PublishingTargets];

        if (!(String.IsNullOrEmpty(restricted) || restricted.Contains(item.ID.ToString())))
        {
          return false;
        }

        item = item.Parent;
      }

      return true;
    }

    protected void AddWarning(
      string message,
      Sitecore.Data.Items.Item item,
      Sitecore.Pipelines.GetContentEditorWarnings.GetContentEditorWarningsArgs args)
    {
      Sitecore.Pipelines.GetContentEditorWarnings.GetContentEditorWarningsArgs.ContentEditorWarning warning
        = args.Add();
      warning.Title = "Publishing Status";
      warning.Text = message;
      string command = String.Format(
        "item:load(id={0},language={1},version={2})",
        item.ID,
        item.Language,
        item.Version.Number);
      warning.AddOption("Refresh", command);
    }
  }
}

That’s a lot of code, so let me try to explain. The processor ignores items in databases other than that specified by the SourceDatabase property defined within the processor definition in the web.config file. It also ignores versions other than the latest. Then it iterates through each of the publishing target definition items, and ignores any to which Sitecore should not publish the item according to publishing target restrictions on the item or any of its ancestors. Then it retrieves the item in the publishing target database that corresponds to the item in the source database. If that item does not exist in the source database, or if the target database contains a different version or revision, then the processor generates a warning. Finally, it adds a command to the warning to refresh the current item (alternatively, the user could simply click on the item in the content tree). This was easier for me to implement than defining a new command that would publish the item/version/revision and refresh the content tree.

Here is what you should see in the Content Editor if you select an item that you have not published:

Here is what you should see if you select an item for which you have not published the latest version:

Here is what you should see if you select an item for which you have made changes to the latest version without publishing:

After you publish, click the Refresh command to eliminate the warning.

You can set the Icon property of the ContentEditorWarning object to cause an icon other than the default to appear with the warning. You can set the HideFields property of the warning if your warning should prevent the user from updating the item. You can set the IsExclusive property of the warning to cause only this warning to appear, in which case you should probably also call the AbortPipeline() method of the GetContentEditorWarningsArgs to prevent Sitecore from running additional processors. You can also set the IsFullScreen property of the warning to cause the warning to consume the entire editing pane in the Content Editor.

Remember that because each processor runs each time a user selects an item in the Content Editor, the performance of getContentEditorWarnings pipeline processors is important.

Feel free to refactor, locate defects, and comment below…

This entry was posted in Obsolete. Bookmark the permalink.

Leave a Reply

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 )

Connecting to %s