Tracking history of item fields changes in Sitecore

29 December 2013
Marek Musielak
Frink_Cognifide_2016_HeaderImages_0117

sitecore track author who changed item fields

Sitecore tracks the information about the last time and by whom an item was updated by storing this information in __Updated and __Updated by fields. This information however is overwritten with every consecutive update. And in fact we're not able to tell what has changed. Sometimes we need to be able to tell more about the history of the item and about the changes performed by editors. This post explains how we can easily track more details about item updates.

The easiest way of accessing the information about changed fields of the item is to plug in our event handler into Sitecore <event name="item:saving"> event:

<sitecore>
  <events>
    <event name="item:saving">
      <handler type="My.Assembly.Namespace.CreateHistoryEntryHandler,
        My.Assembly" method="OnItemSaving" />
    </event>
  </events>
</sitecore>

Using this approach we have easy access to both original item and the newly saved item.
The next step is to retrieve the differences between the original item and the newly saved item. I'm looping through all the fields and checking if their values are equal with what was in the original item:
private static List<string> FindDifferences(Item newItem, Item originalItem)
{
	newItem.Fields.ReadAll();

	IEnumerable<string> fieldNames = newItem.Fields.Select(f => f.Name);

	return fieldNames
		.Where(fieldName => newItem[fieldName] != originalItem[fieldName])
		.ToList();
}
Now we need to save the information about the differences. In my approach I'll save only the information for the items which have defined workflow and I will use HistoryStore to store this data. This will allow your users to see the saved information using built-in functionality of Sitecore Content Editor. My handler checks if GetWorkflowInfo(newItem) is not null but you can create a more specific condition than I did to save only e.g. items which use specified Item Template, changes performed by chosen roles, etc and store the information in any place which is convenient for you. The whole code of my event handler is:
using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.Data.Items;
using Sitecore.Events;
using Sitecore.Workflows;
using Sitecore.Workflows.Simple;

namespace My.Assembly.Namespace
{
  public class CreateHistoryEntryHandler
  {
    protected void OnItemSaving(object sender, EventArgs args)
    {
      Item newItem = Event.ExtractParameter(args, 0) as Item;

      if (newItem == null || newItem.Database.DataManager.GetWorkflowInfo(newItem) == null)
      {
        return;
      }

      Item originalItem = 
        newItem.Database.GetItem(newItem.ID, newItem.Language, newItem.Version);

      var differences = FindDifferences(newItem, originalItem);

      if (differences.Any())
      {
        string message = String.Format("Item content changed [{0}]", 
          String.Join(", ", differences));

        AddHistoryEntry(newItem, message);
      }
    }

    private static List<string> FindDifferences(Item newItem, Item originalItem)
    {
      newItem.Fields.ReadAll();

      IEnumerable<string> fieldNames = newItem.Fields.Select(f => f.Name);

      return fieldNames
        .Where(fieldName => newItem[fieldName] != originalItem[fieldName])
        .ToList();
    }

    private static void AddHistoryEntry(Item item, string text)
    {
      WorkflowProvider workflowProvider = (item.Database.WorkflowProvider as WorkflowProvider);
      if (workflowProvider != null && workflowProvider.HistoryStore != null)
      {
        string workflowState = GetWorkflowState(item);
        workflowProvider.HistoryStore.AddHistory(item, workflowState, workflowState, text);
      }
    }

    private static string GetWorkflowState(Item item)
    {
      WorkflowInfo info = item.Database.DataManager.GetWorkflowInfo(item);
      return (info != null) ? info.StateID : String.Empty;
    }
  }
}

Now any item in workflow that has any changes tracked will show you a nice history of those in
Content Editor
when you click the History button on Review tab like this one:

sitecore track item change on save see history

This post is just an example and a startup point for storing the information about item updates. Most probably in your case you may want to add information about the new field values or store this data not in HistoryStore but in some place designated for this. I hope this article gives you an idea of how this can be achieved and lets you extend this solution to fit your needs easily. If you have any questions or comments, leave a note below or check out my other
Sitecore blog posts.