Control Revision history for Sitefinity pages

This blog post comes as a solution for the long waited control of revision history for pages in Sitefinity as you may see in the Feature request here.

The full code can be downloaded from GitHub repository here

Most of you know that each change that you make on Sitefinity pages is kept in Revision history. However the more often you edit the pages, more versions are created. This could harm the performance of your Sitefinity project not only by the round trips to the database for each version in revision history but with load time for each page.

Solution video demo

Watch the video below to see how this blog post solves the problem:

Revision history settings for pages in Sitefinity

Solution plan

You can create an Advanced settings section in the backend with configuration options. The configuration will have two criterias to control the revision history:

  • by number of versions to keep
  • by time (not older than X days)

TIP: If both values are 0, revision history will be kept forever as by default in Sitefinity.

Solution

Follow the steps below to understand the solution.

1.Create the configuration

In order to create configuration for the advanced settings you must add new class that inherits the ConfigSection class. Next all property that you create will be displayed in the advanced section. You must add an ObjectInfo attribute to each property to define the title and description that will be displayed.

For example:

public class RevisionHistoryConfig : ConfigSection
{
    [ConfigurationProperty("NumberOfRevisionsToKeep")]
    [ObjectInfo(typeof(RevisionHistoryResources), Title = "NumberOfRevisionsToKeepTitle", Description = "NumberOfRevisionsToKeepDescription")]
    public string NumberOfRevisionsToKeep
    {
        get
        {
            return (string)this["NumberOfRevisionsToKeep"];
        }

        set
        {
            this["NumberOfRevisionsToKeep"] = value;
        }
    }
 . . .

Do not forget to add ConfigurationProperty attribute with the name of the property. This will be the key in the .config class that is going to be created.

2.Register the configuration class.

Normally you can register custom configurations in the Global application class as follows:

 Config.RegisterSection<RevisionHistoryConfig>();     

3.Handle the ConfigurationManager Executed event

You need to remove reduntant revision history versions once you save the configuration in the advanced settings. For that purpose, you must handle the Executed event of the Configuration manager:

private void ConfigManager_Executed(object sender, ExecutedEventArgs args)
    {
        if (args != null)
        {
            var commandArgs = args.CommandArguments as RevisionHistoryConfig;
            if (commandArgs != null)
            {
                int revisionsNumber = 0;
                int.TryParse(commandArgs.NumberOfRevisionsToKeep, out revisionsNumber);

                int daysToKeepHistory = 1;
                int.TryParse(commandArgs.NumberOfDaysToKeepHistory, out daysToKeepHistory);

                var manager = PageManager.GetManager();
                var pageDataList = manager.GetPageDataList();

                foreach (var data in pageDataList)
                {
                    if ((revisionsNumber > 0) && (daysToKeepHistory <= 1))
                    {
                        RevisionHistoryHelper.TruncateVersionsByNumber(revisionsNumber, data);
                    }
                    else if ((revisionsNumber == 0) && (daysToKeepHistory > 1))
                    {
                        RevisionHistoryHelper.TruncateVersionsByDaysToKeep(daysToKeepHistory, data);
                    }
                    else if ((revisionsNumber > 0) && (daysToKeepHistory > 1))
                    {
                        RevisionHistoryHelper.TruncateVersionsByDaysToKeepAndCount(daysToKeepHistory, revisionsNumber, data);
                    }
                }
            }
        }
    }

As the revision history is having a relation with the PageData object in Sitefinity pages, you must iterate through all page data list and make the needed checks according to the configuration values

4.Delete reduntant versions according to number and days to keep

Revision history and versions are maintained by the VersionManager class. You must use the overloads of the TruncateVersions method to remove versions. For example the following method removes all versions for a specific page up tp a specific date:

public static void TruncateVersionsByDaysToKeep(int daysToKeep, PageData pageData)
    {
        var versionManager = VersionManager.GetManager();

        var changes = versionManager.GetItemVersionHistory(pageData.Id);

        if (changes.Count > 0)
        {
            var lastChange = changes.OrderByDescending(c => c.Version).FirstOrDefault();
            if (lastChange != null)
            {
                var earliestDate = lastChange.LastModified.AddDays(daysToKeep * (-1));

                //Delete all the changes with dates older or equal to the specified date
                versionManager.TruncateVersions(pageData.Id, earliestDate);

                versionManager.SaveChanges();
            }
        }
    }

In the code above you must get the earliest date from which you must keep the versions. This date is a calculation of last modified date subtracted by the days to keep property from the configuration class:

var earliestDate = lastChange.LastModified.AddDays(daysToKeep * (-1));

5.Remove versions once you update the page

Except on saving the configuration class, you must take care to not pass the limit ot versions on each update of the pages. For that purpose you must handle the PageManager Executing event and do the same logic as in the previous step.

Here's an example of subscribing to the PageManager.Executing() event:

        PageManager.Executing += new EventHandler<Telerik.Sitefinity.Data.ExecutingEventArgs>(PageManager_Executing);

Next, in the handler you must get the config values from Advanced settings and delete versions for page data only when the page is updated:

 private void PageManager_Executing(object sender, ExecutingEventArgs e)
    {
        if (e.CommandName == "CommitTransaction" || e.CommandName == "FlushTransaction")
        {
            var configManager = ConfigManager.GetManager();
            var revisionHistoryConfig = configManager.GetSection<RevisionHistoryConfig>();

            if (revisionHistoryConfig != null)
            {
                int revisionsNumber = 0;
                int.TryParse(revisionHistoryConfig.NumberOfRevisionsToKeep, out revisionsNumber);

                int daysToKeepHistory = 1;
                int.TryParse(revisionHistoryConfig.NumberOfDaysToKeepHistory, out daysToKeepHistory);

                var provider = sender as PageDataProvider;
                var dirtyItems = provider.GetDirtyItems();
                var pageManager = PageManager.GetManager();
                if (dirtyItems.Count != 0)
                {
                    foreach (var item in dirtyItems)
                    {
                        SecurityConstants.TransactionActionType itemStatus = provider.GetDirtyItemStatus(item);
                        var pageData = item as PageData;
                        if (pageData != null)
                        {
                            //Versions are truncated only when updating a page
                            if (itemStatus == SecurityConstants.TransactionActionType.Updated)
                            {
                            . . . 

With this step you are ready to control your revision history.

Veronica Milcheva

About Veronica Milcheva

I am a passionate Sitefinity blogger, developer and consultant. In my spare time I enjoy running and listening to music. My personal quote: There's no tough problem, just not enough coffee :)

View Comments

comments powered by Disqus