Compare Sitecore contents between two environments

Sometimes there is need for comparing the contents between two environments. It can be local to QA, QA to Stage or Stage to Production. So I thought of building a simple page, by which we can compare items between two environments.


There is already some popular tools available in market e.g Razl and each of these tools have their own niche role.

Here I tried to make it very simple for specific goals. When I thought of developing it, the first thing came to my mind that I don’t need to compare all the items in Sitecore, but I need to compare only the recently modified items.

So below are the steps.

  • Need a list of recently modified items of the current environment.
  • Need the current item details from DB.
  • Need the same item from Target environment
  • Compare between two items
  • Create package from selected items from current environment.

So let’s do some coding.

Get Modified items between two dates

public JsonResult GetModifiedItems(string startDate, string endDate, string rootPath)public JsonResult GetModifiedItems(string startDate, string endDate, string rootPath)
{
    var sitecoreItems = new List<SitecoreItem>();
    DateTime dtStartDate = Utility.GetDate(startDate);
    DateTime dtEndDate = Utility.GetDate(endDate);
    dtEndDate = dtEndDate.AddDays(1);
    endDate = dtEndDate.ToString("yyyyMMdd");
    startDate = dtStartDate.ToString("yyyyMMdd");
    rootPath = rootPath.Trim('/');
    rootPath = "/" + rootPath + "/";
    string sitecoreQuery = string.Format("fast:" + rootPath + "/*[@__Updated >= '{0}T000000' and @__Updated < '{1}T000000']", startDate, endDate);     var result = Utility.GetItems(sitecoreQuery);                   if (result.Count > 0)
    {
        int serialNo = 1;
        foreach (var item in result)
        {
            sitecoreItems.Add(new SitecoreItem
            {
                Id = item.ID.Guid.ToString().ToUpper(),
                Path = item.Paths.FullPath,
                SerialNo = serialNo,
                CreatedDate = Utility.GetIsoDate(item.Fields["__Created"].Value),
                UpdateDate = Utility.GetIsoDate(item.Fields["__Updated"].Value),
                UpdatedBy = Utility.GetUserName(item.Fields["__Updated by"].Value)
             });
             serialNo++;
        }
    }
    return Json(sitecoreItems, JsonRequestBehavior.AllowGet);
}

Get All field values of an Item

        public JsonResult GetItem(string id, string database, string lang)
        {
            var fieldValues = new List<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span><FieldValue><span 				data-mce-type="bookmark" 				id="mce_SELREST_end" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>()

            var db = Factory.GetDatabase(database);
            var siecoreId = new Sitecore.Data.ID(new Guid(id));

            //Check the language
            if (string.IsNullOrEmpty(lang))
            {
                lang = "en";
            }

            var language = Sitecore.Globalization.Language.Parse(lang);
            var item = db.GetItem(siecoreId, language);

            if (item != null)
            {
                item.Fields.ReadAll();
                FieldCollection fieldCollection = item.Fields;
                foreach (Field field in fieldCollection)
                {
                    //Use the following check if you do not want
                    //the Sitecore Standard Fields
                    if (!field.Name.StartsWith("__"))
                    {
                        fieldValues.Add(new FieldValue
                        {
                            Name = field.Name,
                            Value = HttpUtility.HtmlEncode(field.Value)
                        });
                    }
                    else if (field.Name == "__Renderings")
                    {
                        fieldValues.Add(new FieldValue
                        {
                            Name = field.Name,
                            Value = HttpUtility.HtmlEncode(field.Value)
                        });
                    }
                }
            }

            return Json(fieldValues, JsonRequestBehavior.AllowGet);
        }

Get Item Difference

        public JsonResult GetDifference(string id, string targetUrl, string lang)
        {
            var itemDifference = new ItemDifference { ErrorMessage = "", Items = new List<ItemDifferenceItem>() };

            if (string.IsNullOrEmpty(lang))
            {
                lang = "en";
            }

            var localApiPath = apiPath.Replace("//", "/");

            targetUrl = targetUrl.TrimEnd('/') + localApiPath;
            targetUrl = targetUrl.Replace("{id}", id);
            targetUrl = targetUrl.Replace("{lang}", lang);

            string currentUrl = Utility.GetDomainUrl(Request.Url.ToString());
            currentUrl = currentUrl.TrimEnd('/') + localApiPath;
            currentUrl = currentUrl.Replace("{id}", id);
            currentUrl = currentUrl.Replace("{lang}", lang);

            string message = "";

            try
            {
                string currentValue = "";
                string targetValue = "";

                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

                try
                {
                    using (var client = new WebClient())
                    {
                        currentValue = client.DownloadString(currentUrl);
                    }
                }
                catch
                {
                    message = "Couldn't retrieve the difference. Error occurred in api." + "" + currentUrl;
                }

                try
                {
                    using (var client = new WebClient())
                    {
                        targetValue = client.DownloadString(targetUrl);
                    }

                }
                catch
                {
                    if (string.IsNullOrEmpty(message))
                    {
                        message = "Couldn't retrieve the difference. Error occured in api." + "" + targetUrl;
                    }
                }

                if (string.IsNullOrEmpty(currentValue) || string.IsNullOrEmpty(targetValue))
                {
                    itemDifference.ErrorMessage = message;
                    return Json(itemDifference, JsonRequestBehavior.AllowGet);
                }

                var targetFieldValues = Utility.GetFieldAndValuesNew(targetValue);
                var currentFieldValues = Utility.GetFieldAndValuesNew(currentValue);

                if (targetFieldValues == null || targetFieldValues.Count == 0)
                {
                    itemDifference.ErrorMessage = "The content doesn't exist in target environment.";
                    return Json(itemDifference, JsonRequestBehavior.AllowGet);
                }

                itemDifference.Items = Utility.GetItemDifference(currentFieldValues, targetFieldValues);

                if (itemDifference.Items.Count == 0)
                {
                    itemDifference.ErrorMessage = "No difference found.";
                }

            }
            catch (Exception ex)
            {
                itemDifference.ErrorMessage = message + ex.Message + "" + ex.StackTrace + "" + currentUrl + "" + targetUrl;
            }

            return Json(itemDifference, JsonRequestBehavior.AllowGet);
        }

Create Package of selected Items

        [HttpPost]
        public JsonResult CreatePackage(string ids)
        {
            var response = new Package { Name = "" };
            try
            {
                using (new Sitecore.SecurityModel.SecurityDisabler())
                {
                    Sitecore.Data.Database db = Factory.GetDatabase("master");
                    PackageProject document = new PackageProject();

                    document.Metadata.PackageName = "Item Package";
                    document.Metadata.Author = "Admin";

                    //Create source – source should be based on BaseSource
                    ExplicitItemSource source = new ExplicitItemSource();
                    source.Name = "Sitecore";

                    string[] arrItemId = ids.Split('|');

                    foreach(string id in arrItemId)
                    {
                        var siecoreId = new Sitecore.Data.ID(new Guid(id));
                        var item = db.GetItem(siecoreId);

                        source.Entries.Add(new ItemReference(item.Uri, false).ToString());
                    }

                    document.Sources.Add(source);
                    document.SaveProject = true;

                    string packageName = "SitecoreItem_" + DateTime.Now.Ticks.ToString() + ".zip";
                    string folder = Settings.DataFolder + "\\packages\\";

                    response.Name = folder;

                    //path where the zip file package is saved
                    using (Sitecore.Install.Zip.PackageWriter writer = new Sitecore.Install.Zip.PackageWriter(folder + packageName))
                    {
                        Sitecore.Context.SetActiveSite("shell");

                        writer.Initialize(Installer.CreateInstallationContext());

                        PackageGenerator.GeneratePackage(document, writer);

                        Sitecore.Context.SetActiveSite("website");
                    }

                    response.IsSuccess = true;
                    response.Name = packageName;
                }
            }
            catch (Exception ex)
            {
                response.Name += ex.Message + "" + ex.StackTrace;
            }

            return Json(response);
        }

The source code is below
Controller

using SC.ItemCompare.Helper;
using SC.ItemCompare.Model;
using Sitecore.Collections;
using Sitecore.Configuration;
using Sitecore.Data.Fields;
using Sitecore.Install;
using Sitecore.Install.Items;
using System;
using System.Collections.Generic;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace SC.ItemCompare
{
    public class ItemCompareServiceController : Controller
    {
        private const string apiPath = "//api//sitecore//ItemCompareService//GetItem?id={id}&database=master&lang={lang}";

        public JsonResult GetModifiedItems(string startDate, string endDate, string rootPath)
        {
            var sitecoreItems = new List<SitecoreItem>();

            DateTime dtStartDate = Utility.GetDate(startDate);
            DateTime dtEndDate = Utility.GetDate(endDate);
            dtEndDate = dtEndDate.AddDays(1);

            endDate = dtEndDate.ToString("yyyyMMdd");
            startDate = dtStartDate.ToString("yyyyMMdd");

            rootPath = rootPath.Trim('/');
            rootPath = "/" + rootPath + "/";

            string sitecoreQuery = string.Format("fast:" + rootPath + "/*[@__Updated >= '{0}T000000' and @__Updated < '{1}T000000']", startDate, endDate);

            var result = Utility.GetItems(sitecoreQuery);

            if (result.Count > 0)
            {
                int serialNo = 1;
                foreach (var item in result)
                {
                    sitecoreItems.Add(new SitecoreItem
                    {
                        Id = item.ID.Guid.ToString().ToUpper(),
                        Path = item.Paths.FullPath,
                        SerialNo = serialNo,
                        CreatedDate = Utility.GetIsoDate(item.Fields["__Created"].Value),
                        UpdateDate = Utility.GetIsoDate(item.Fields["__Updated"].Value),
                        UpdatedBy = Utility.GetUserName(item.Fields["__Updated by"].Value)

                    });
                    serialNo++;
                }
            }

            return Json(sitecoreItems, JsonRequestBehavior.AllowGet);
        }

        public JsonResult GetItem(string id, string database, string lang)
        {
            var fieldValues = new List<FieldValue>();

            var db = Factory.GetDatabase(database);
            var siecoreId = new Sitecore.Data.ID(new Guid(id));

            //Check the language
            if (string.IsNullOrEmpty(lang))
            {
                lang = "en";
            }

            var language = Sitecore.Globalization.Language.Parse(lang);
            var item = db.GetItem(siecoreId, language);

            if (item != null)
            {
                item.Fields.ReadAll();
                FieldCollection fieldCollection = item.Fields;
                foreach (Field field in fieldCollection)
                {
                    //Use the following check if you do not want 
                    //the Sitecore Standard Fields 
                    if (!field.Name.StartsWith("__"))
                    {
                        fieldValues.Add(new FieldValue
                        {
                            Name = field.Name,
                            Value = HttpUtility.HtmlEncode(field.Value)
                        });
                    }
                    else if (field.Name == "__Renderings")
                    {
                        fieldValues.Add(new FieldValue
                        {
                            Name = field.Name,
                            Value = HttpUtility.HtmlEncode(field.Value)
                        });
                    }
                }
            }

            return Json(fieldValues, JsonRequestBehavior.AllowGet);
        }

        public JsonResult GetDifference(string id, string targetUrl, string lang)
        {
            var itemDifference = new ItemDifference { ErrorMessage = "", Items = new List<ItemDifferenceItem>() };

            if (string.IsNullOrEmpty(lang))
            {
                lang = "en";
            }

            var localApiPath = apiPath.Replace("//", "/");

            targetUrl = targetUrl.TrimEnd('/') + localApiPath;
            targetUrl = targetUrl.Replace("{id}", id);
            targetUrl = targetUrl.Replace("{lang}", lang);

            string currentUrl = Utility.GetDomainUrl(Request.Url.ToString());
            currentUrl = currentUrl.TrimEnd('/') + localApiPath;
            currentUrl = currentUrl.Replace("{id}", id);
            currentUrl = currentUrl.Replace("{lang}", lang);

            string message = "";

            try
            {
                string currentValue = "";
                string targetValue = "";

                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

                try
                {
                    using (var client = new WebClient())
                    {
                        currentValue = client.DownloadString(currentUrl);
                    }
                }
                catch
                {
                    message = "Couldn't retrieve the difference. Error occured in api." +
                                                  "\n\n" + currentUrl;
                }

                try
                {
                    using (var client = new WebClient())
                    {
                        targetValue = client.DownloadString(targetUrl);
                    }

                }
                catch
                {
                    if (string.IsNullOrEmpty(message))
                    {
                        message = "Couldn't retrieve the difference. Error occured in api." +
                                                  "\n\n" + targetUrl;
                    }
                }

                if (string.IsNullOrEmpty(currentValue) || string.IsNullOrEmpty(targetValue))
                {
                    itemDifference.ErrorMessage = message;
                    return Json(itemDifference, JsonRequestBehavior.AllowGet);
                }


                var targetFieldValues = Utility.GetFieldAndValuesNew(targetValue);
                var currentFieldValues = Utility.GetFieldAndValuesNew(currentValue);

                if (targetFieldValues == null || targetFieldValues.Count == 0)
                {
                    itemDifference.ErrorMessage = "The content doesn't exist in target environment.";
                    return Json(itemDifference, JsonRequestBehavior.AllowGet);
                }

                itemDifference.Items = Utility.GetItemDifference(currentFieldValues, targetFieldValues);

                if (itemDifference.Items.Count == 0)
                {
                    itemDifference.ErrorMessage = "No difference found.";
                }

            }
            catch (Exception ex)
            {
                itemDifference.ErrorMessage = message + ex.Message + "\n\n" + ex.StackTrace + "\n\n" + currentUrl + "\n\n" + targetUrl;
            }

            return Json(itemDifference, JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public JsonResult CreatePackage(string ids)
        {
            var response = new Package { Name = "" };
            try
            {
                using (new Sitecore.SecurityModel.SecurityDisabler())
                {
                    Sitecore.Data.Database db = Factory.GetDatabase("master");
                    PackageProject document = new PackageProject();

                    document.Metadata.PackageName = "AN Item Package";
                    document.Metadata.Author = "Admin";

                    //Create source – source should be based on BaseSource  
                    ExplicitItemSource source = new ExplicitItemSource();
                    source.Name = "Sitecore";

                    string[] arrItemId = ids.Split('|');

                    foreach(string id in arrItemId)
                    {
                        var siecoreId = new Sitecore.Data.ID(new Guid(id));
                        var item = db.GetItem(siecoreId);

                        source.Entries.Add(new ItemReference(item.Uri, false).ToString());
                    }
                    

                    document.Sources.Add(source);
                    document.SaveProject = true;

                    string packageName = "SitecoreItem_" + DateTime.Now.Ticks.ToString() + ".zip";
                    string folder = Settings.DataFolder + "\\packages\\";

                    response.Name = folder;

                    //path where the zip file package is saved  
                    using (Sitecore.Install.Zip.PackageWriter writer = new Sitecore.Install.Zip.PackageWriter(folder + packageName))
                    {
                        Sitecore.Context.SetActiveSite("shell");

                        writer.Initialize(Installer.CreateInstallationContext());

                        PackageGenerator.GeneratePackage(document, writer);

                        Sitecore.Context.SetActiveSite("website");
                    }

                    response.IsSuccess = true;
                    response.Name = packageName;
                }
            }
            catch (Exception ex)
            {
                response.Name += ex.Message + "\n\n" + ex.StackTrace;
            }

            return Json(response);
        }

        public JsonResult IsFileExists(string fileName)
        {
            string folder = Settings.DataFolder + "\\packages\\";
            string filePath = folder + fileName;

            if (System.IO.File.Exists(filePath))
            {
                return Json(true, JsonRequestBehavior.AllowGet);
            }
            else
            {
                return Json(false, JsonRequestBehavior.AllowGet);
            }
        }

        public ActionResult Download()
        {
            string fileName = Request.QueryString["fileName"];
            string folder = Settings.DataFolder + "\\packages\\";
            string filePath = folder + fileName;

            if (System.IO.File.Exists(filePath))
            {
                return File(filePath, System.Net.Mime.MediaTypeNames.Application.Zip, fileName);
            }
            else
            {
                return Content(fileName + " not found in directory. Please try after some time.");
            }
            
        }
    }
}

Utility class

using Sitecore.Data.Items;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Sitecore.Configuration;
using SC.ItemCompare.Model;
using System.Web.Script.Serialization;

namespace SC.ItemCompare.Helper
{
    public static class Utility
    {
        public static DateTime GetDate(string date)
        {
            return DateTime.ParseExact(date, "dd MMMM yyyy", CultureInfo.InvariantCulture);
        }

        public static List<Item> GetItems(string sitecoreQuery)
        {
            var database = Factory.GetDatabase("master");
            var contentItems = database.SelectItems(sitecoreQuery);
            var orderedContentItems = contentItems.OrderByDescending(x => x[Sitecore.FieldIDs.Updated]);

            var result = new List<Item>();

            if (orderedContentItems != null && orderedContentItems.Any())
            {
                result = orderedContentItems.ToList();
            }

            return result;
        }

        public static string GetDomainUrl(string url)
        {
            Uri uri = new Uri(url);
            return uri.GetLeftPart(UriPartial.Authority);
        }

        public static List<FieldValue> GetFieldAndValues(string response)
        {
            var fields = new List<FieldValue>();

            if (response.IndexOf("{", StringComparison.OrdinalIgnoreCase) >= 0 && response.IndexOf("}", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                var values = new JavaScriptSerializer().Deserialize<Dictionary<string, string>>(response);
                foreach (KeyValuePair<string, string> entry in values)
                {
                    fields.Add(new FieldValue { Name = entry.Key, Value = entry.Value });
                }
            }

            return fields;

        }

        public static List<FieldValue> GetFieldAndValuesNew(string response)
        {
            List<FieldValue> fields;
            fields = new JavaScriptSerializer().Deserialize<List<FieldValue>>(response);
            return fields;
        }

        public static List<ItemDifferenceItem> GetItemDifference(List<FieldValue> currentFieldValues, List<FieldValue> targetFieldValues)
        {
            var ItemDifferenceItems = new List<ItemDifferenceItem>();
            foreach (var fieldValue in currentFieldValues)
            {
                if (!targetFieldValues.Exists(fv => fv.Name == fieldValue.Name))
                {
                    ItemDifferenceItems.Add(new ItemDifferenceItem
                    {
                        FieldName = fieldValue.Name,
                        CurrentEnvironmentValue = fieldValue.Value,
                        TargetEnvironmentValue = ""

                    });
                }
                else
                {
                    var targetFieldValue = targetFieldValues.FirstOrDefault(fv => fv.Name == fieldValue.Name);
                    if (fieldValue.Value != targetFieldValue.Value)
                    {
                        ItemDifferenceItems.Add(new ItemDifferenceItem
                        {
                            FieldName = fieldValue.Name,
                            CurrentEnvironmentValue = fieldValue.Value,
                            TargetEnvironmentValue = targetFieldValue.Value

                        });
                    }
                }
            }

            return ItemDifferenceItems;
        }

        public static string GetUserName(string name)
        {
            if (!string.IsNullOrEmpty(name) && name.IndexOf("\\", StringComparison.OrdinalIgnoreCase) >= 0)
            {
                return name.Substring(name.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase)).Replace("\\", "");
            }

            return name;
        }

        public static string GetIsoDate(string isoDate)
        {
            DateTime d = Sitecore.DateUtil.IsoDateToDateTime(isoDate);
            return d.ToString("dd-MMM-yyyy hh:mm tt");
        }
    }
}

Models

public class ItemDifferenceItem
    {
        public string FieldName { get; set; }

        public string CurrentEnvironmentValue { get; set; }

        public string TargetEnvironmentValue { get; set; }
    }

 public class FieldValue
    {
        public string Name { get; set; }

        public string Value { get; set; }
    }
 public class ItemDifference
    {
        public string ErrorMessage { get; set; }

        public List<ItemDifferenceItem> Items { get; set; }
    }
public class Package
    {
        public string Name { get; set; }

        public bool IsSuccess { get; set; }
    }
 public class SitecoreItem
    {
        public int SerialNo { get; set; }

        public string Id { get; set; }

        public string Path { get; set; }

        public string CreatedDate { get; set; }

        public string UpdateDate { get; set; }

        public string UpdatedBy { get; set; }
    }

The .aspx page

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CompareItems.aspx.cs" Inherits="ProcessUpdatePackage.SC.Admin.CompareItems" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <link rel="shortcut icon" href="/favicon.ico" type="image/vnd.microsoft.icon" />
    <title>Compare Sitecore Items</title>
    <link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
    <link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css" />
</head>
<body>
    <form id="form1" runat="server">
        <div class="jumbotron text-center">
            <p>Compare Sitecore Items</p>
        </div>
        <div class="container-fluid">
            <div class="row">
                <div class="col-lg-12">
                    <div class="form-inline text-center">
                        <div class="form-group">
                            <label>Start:</label>
                            <input type="text" readonly="true" style="cursor: pointer;" class="form-control" id="txtStartDate" />
                        </div>
                        <div class="form-group">
                            <label>End:</label>
                            <input type="text" readonly="true" style="cursor: pointer;" class="form-control" id="txtEndDate" />
                        </div>
                        <div class="form-group">
                            <label>Root:</label>
                            <input type="text" class="form-control" id="txtRootPath" style="width: 375px;" value="/sitecore/content/" />
                        </div>
                        <button type="button" id="btnSearch" class="btn btn-default">Search</button>
                    </div>
                </div>
            </div>

            <div id="dvContent" class="row" style="margin-top: 40px; display: none;">
                <div class="col-lg-12">
                    <div class="form-inline" style="margin-bottom: 20px;">
                       
                        <div class="form-group">
                            <button type="button" id="btnCreatePackage" class="btn btn-default">Create Package</button>
                        </div>
                        <div class="form-group pull-right" style="margin-right: 20px;">
                            <label id="lblTotalRecords"></label>
                        </div>
                    </div>

                    <table class="table table-bordered">
                        <thead class="bg-info">
                            <tr>
                                <th colspan="8" style="text-align:right; background-color:white;">
                                    <button type="button" id="btnCompareAll" class="btn btn-default">Compare All</button>
                                </th>
                            </tr>
                            <tr>
                                <th>
                                    <input id="chkSelectAll" type="checkbox" value="" /></th>
                                <th style="width: 320px;">Id</th>
                                <th style="width: 450px">Path</th>
                                <th>Created Date (UTC)</th>
                                <th>Updated Date (UTC)</th>
                                <th>Updated By</th>
                                <th>Status</th>
                                <th><span class="glyphicon glyphicon-th-list" style="font-size: 24px; cursor: pointer" data-toggle="modal" data-target="#settingsModal" data-backdrop="static" data-keyboard="false"></span></th>
                            </tr>
                        </thead>
                        <tbody id="searchBody">
                        </tbody>
                    </table>
                </div>
            </div>

            <div id="dvLoader" class="row" style="display: none;">
                <div class="col-lg-12">
                    <div class="text-center">
                        <img src="/sitecore/admin/images/Loader.gif" style="width: 200px;" />
                    </div>
                </div>
            </div>
        </div>

        <%--Modal Item Difference--%>
        <div id="myModal" class="modal fade" role="dialog">
            <div class="modal-dialog modal-lg">

                <!-- Modal content-->
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal">&times;</button>
                        <h4 class="modal-title">Item Difference</h4>
                    </div>
                    <div class="modal-body">
                        <table id="tblDifference" class="table" style="display: none;">
                            <thead>
                                <tr>
                                    <th>Field</th>
                                    <th>Current Environment Value</th>
                                    <th>Target Environment Value</th>
                                </tr>
                            </thead>
                            <tbody id="modalBody">
                            </tbody>
                        </table>
                        <p id="errorMessage" style="display: none;"></p>
                        <div id="dvModalLoader" class="text-center">
                            <img src="/sitecore/admin/images/Loader.gif" style="width: 200px;" />
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                    </div>
                </div>

            </div>
        </div>

        <%--Modal Settings--%>
        <div id="settingsModal" class="modal fade" role="dialog">
            <div class="modal-dialog modal-lg">

                <!-- Modal content-->
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" id="btnCloseSettings">&times;</button>
                        <h4 class="modal-title">Settings</h4>
                    </div>
                    <div class="modal-body">
                        <div class="form-horizontal">
                            <div class="form-group">
                                <label class="control-label col-sm-2">Compare with:</label>
                                <div class="col-sm-10">
                                    <input type="text" class="form-control" id="txtEnvironmentUrl" value="http://auth2.site.com" />
                                </div>
                            </div>
                            <div class="form-group">
                                <label class="control-label col-sm-2">Language:</label>
                                <div class="col-sm-10">
                                    <input type="text" class="form-control" id="txtLanguage" value="en" maxlength="6" />
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">OK</button>
                    </div>
                </div>

            </div>
        </div>

        <%--Download Package Modal--%>
         <div id="downloadPackageModal" class="modal fade" role="dialog">
            <div class="modal-dialog modal-lg">

                <!-- Modal content-->
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal">&times;</button>
                        <h4 class="modal-title">Download Package</h4>
                    </div>
                    <div class="modal-body">
                        <p id="errorMessageDownloadPackage" style="display: none;"></p>
                        <div id="dvModalLoaderDownloadPackage" class="text-center">
                            <img src="/sitecore/admin/images/Loader.gif" style="width: 200px;" />
                        </div>
                        <div id="dvDownloadPackage" class="text-center">
                            <span id="spnDownloadPackage" class="glyphicon glyphicon-download-alt" style="font-size: 28px; cursor: pointer"></span>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                    </div>
                </div>

            </div>
        </div>

        <script type="text/template" data-template="listitem">
            <tr>
                <td>
                    <input type="checkbox" data-id="${Id}" class="chkSelect" /></td>
                <td>${Id}</td>
                <td>${Path}</td>
                <td>${CreatedDate}</td>
                <td>${UpdateDate}</td>
                <td>${UpdatedBy}</td>
                <td><span class="badge"></span></td>
                <td>
                    <button type="button" data-id="${Id}" class="btn btn-default btnCompare" data-toggle="modal" data-target="#myModal">Compare</button></td>
            </tr>
        </script>
        <script type="text/template" data-template="listitemDifference">
            <tr>
                <td>${FieldName}</td>
                <td>${CurrentEnvironmentValue}</td>
                <td>${TargetEnvironmentValue}</td>
            </tr>
        </script>
        <script type="text/javascript" src="//code.jquery.com/jquery-2.2.4.min.js"></script>
        <script type="text/javascript" src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
        <script type="text/javascript" src="//code.jquery.com/ui/1.12.0/jquery-ui.min.js"></script>
        <script type="text/javascript" src="/sitecore/admin/js/itemCompare.js"></script>
    </form>
</body>
</html>

The aspx.cs page

public partial class CompareItems : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            CheckSecurity();
        }

        protected void CheckSecurity()
        {
            if (Sitecore.Context.User.IsAdministrator)
            {
                return;
            }

            var site = Sitecore.Context.Site;

            string loginPage = (site != null ? site.LoginPage : "");

            if (loginPage.Length > 0)
            {
                string url = "/sitecore/admin/login.aspx?returnUrl=/sitecore/admin/compareItems.aspx";
                Response.Redirect(url, true);
            }
        }
    }

The JS Script

function trimText(x) {
    return x.replace(/^\s+|\s+$/gm, '');
}


function addDays(date, days) {
    var result = new Date(date);
    result.setDate(result.getDate() + days);
    return result;
}

function render(props) {
    return function (tok, i) {
        return (i % 2) ? props[tok] : tok;
    };
}

function isUrlValid(userInput) {
    var res = userInput.match(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
    return res != null;
}

function populateSearchData(items) {
    var itemTpl = $('script[data-template="listitem"]').text().split(/\$\{(.+?)\}/g);

    $("#searchBody").html("");

    if (items.length > 0) {
        $("#searchBody").append(items.map(function (item) {
            return itemTpl.map(render(item)).join('');
        }));
    } else {
        $("#searchBody").html("<tr><td colspan='7' align='center'>No results found.</td></tr>");
    }

    $("#lblTotalRecords").html("Total Records:" + items.length);
}

function populateDifference(items) {
    var itemTpl = $('script[data-template="listitemDifference"]').text().split(/\$\{(.+?)\}/g);

    $("#modalBody").html("");

    $("#modalBody").append(items.map(function (item) {
        return itemTpl.map(render(item)).join('');
    }));
}

function populateTargetUrl() {
    var environments = [];
    environments.push({ current: "local.site.com", target: "auth5.site.com" });
    environments.push({ current: "auth5.site.com", target: "auth2.site.com" });
    environments.push({ current: "auth2.site.com", target: "auth1.site.com" });
    environments.push({ current: "auth1.site.com", target: "auth.site.com" });

    var currentHost = document.location.host;
    var protocol = "http://";

    for (var count = 0; count < environments.length; count++) {
        if (environments[count].current === currentHost) {
            var targetEnvUrl = protocol + environments[count].target + "/";
            $("#txtEnvironmentUrl").val(targetEnvUrl);
            break;
        }
    }
}

function PopulateDatePicker() {
    $("#txtStartDate").datepicker({
        dateFormat: "dd MM yy",
        maxDate: 0,
        minDate: '-24m'
    });
    $("#txtStartDate").datepicker().datepicker("setDate", addDays(new Date(), -30));
    $("#txtEndDate").datepicker({
        dateFormat: "dd MM yy",
        maxDate: 0,
        minDate: '-3m'
    });
    $("#txtEndDate").datepicker().datepicker("setDate", new Date());
}

function searchButtonClickEvent() {
    $("#btnSearch").click(function () {

        var startDate = $("#txtStartDate").val();
        var endDate = $("#txtEndDate").val();
        var rootPath = $("#txtRootPath").val();
        rootPath = trimText(rootPath);
        $("#txtRootPath").parent().removeClass("has-error");

        if (rootPath.indexOf("/sitecore/") < 0) {
            $("#txtRootPath").parent().addClass("has-error");
            return;
        }

        $("#dvContent").hide();
        $("#dvLoader").show();
        $("#lblTotalRecords").html("");

        $.ajax({
            type: "GET",
            url: "/api/sitecore/ItemCompareService/GetModifiedItems?startDate=" + startDate + "&endDate=" + endDate + "&rootPath=" + rootPath,
            contentType: "application/json; charset=utf-8",

            success: function (response) {
                var items = response;
                populateSearchData(items);
                $("#dvContent").show();
                $("#dvLoader").hide();
            },
            failure: function () {
                alert(response.d);
                $("#dvLoader").hide();
            }
        });
    });
}

function compareButtonClickEvent() {
    $(document).on("click", ".btnCompare", function () {
        $("#modalBody").html("");
        $("#tblDifference").hide();
        $("#errorMessage").hide();
        $("#errorMessage").html("");
        $("#dvModalLoader").show();

        var id = $(this).attr("data-id");
        var targetUrl = $("#txtEnvironmentUrl").val();

        $.ajax({
            type: "GET",
            url: "/api/sitecore/ItemCompareService/GetDifference?id=" + id + "&targetUrl=" + targetUrl + "&lang=en",
            contentType: "application/json; charset=utf-8",
            success: function (response) {
                var res = response;
                $("#dvModalLoader").hide();
                if (res.ErrorMessage !== "") {
                    $("#errorMessage").html(res.ErrorMessage);
                    $("#errorMessage").show();
                } else {
                    populateDifference(res.Items);
                    $("#tblDifference").show();
                }
            },
            failure: function () {

            }
        });


    });
}

function compareAllbuttonClickEvent() {
    $(document).on("click", "#btnCompareAll", function () {
        var compareButtons = $(".btnCompare");
        var badges = $(".badge");
        for (var count = 0; count < compareButtons.length; count++) {
            var id = $(compareButtons[count]).attr("data-id");
            var targetUrl = $("#txtEnvironmentUrl").val();

            $.ajax({
                type: "GET",
                url: "/api/sitecore/ItemCompareService/GetDifference?id=" + id + "&targetUrl=" + targetUrl + "&lang=en",
                contentType: "application/json; charset=utf-8",
                indexValue: count,
                success: function (response) {
                    var res = response;
                    if (res.ErrorMessage === "") {
                        $(badges[this.indexValue]).html("DIFFERENT");
                        $(badges[this.indexValue]).css("background-color", "red");
                    } else if (res.ErrorMessage === "No difference found.") {
                        $(badges[this.indexValue]).html("NO DIFFERENCE");
                        $(badges[this.indexValue]).css("background-color", "green");
                    } else {
                        $(badges[this.indexValue]).html("NEW");
                        $(badges[this.indexValue]).css("background-color", "orange");
                    }
                },
                failure: function () {

                }
            });
        }

    });
}

function closeSettingsModalClickEvent() {
    $("#btnCloseSettings").click(function () {
        var url = trimText($("#txtEnvironmentUrl").val());
        var language = trimText($("#txtLanguage").val());
        var isValid = true;

        $("#txtLanguage").parent().parent().removeClass("has-error");
        $("#txtEnvironmentUrl").parent().parent().removeClass("has-error");

        if (language === "") {
            $("#txtLanguage").parent().parent().addClass("has-error");
            isValid = false;
        }

        if (url === "" || !isUrlValid(url)) {
            $("#txtEnvironmentUrl").parent().parent().addClass("has-error");
            isValid = false;
        }

        if (isValid) {
            $("#settingsModal").modal("hide");
        }

    });
}

function createPackageButtonClickEvent() {
    $("#btnCreatePackage").click(function () {
        var itemIds = "";
        $(".chkSelect").each(function () {
            if ($(this).is(":checked")) {
                var id = $(this).attr("data-id");
                if (itemIds === "") {
                    itemIds = id;
                } else {
                    itemIds += "|" + id;
                }
            }
        });

        if (itemIds === "") {
            alert("Please select at least one item.");
            return;
        }

        $("#errorMessageDownloadPackage").hide();
        $("#dvModalLoaderDownloadPackage").show();
        $("#dvDownloadPackage").hide();

        $("#downloadPackageModal").modal("show");


        $.ajax({
            type: "POST",
            url: "/api/sitecore/ItemCompareService/CreatePackage",
            data: "ids=" + itemIds,
            success: function (response) {
                $("#dvModalLoaderDownloadPackage").hide();
                if (!response.IsSuccess) {
                    $("#errorMessageDownloadPackage").show();
                    $("#errorMessageDownloadPackage").html(response.Name);
                } else {
                    $("#dvDownloadPackage").show();
                    $("#spnDownloadPackage").attr("data-name", response.Name);
                }
            }
        });
    });
}

$(function () {

    populateTargetUrl();
    PopulateDatePicker();

    //Search Button click event
    searchButtonClickEvent();

    //Compare button click event
    compareButtonClickEvent();

    compareAllbuttonClickEvent();

    //Close settings modal click event
    closeSettingsModalClickEvent();

    //Select all check box select event
    $("#chkSelectAll").change(function () {
        $(".chkSelect").prop("checked", $(this).is(":checked"));
    });

    //Create package click event
    createPackageButtonClickEvent();

    //Download Package click event
    $("#spnDownloadPackage").click(function () {
        var fileName = $(this).attr("data-name");
        var url = window.location.protocol.replace(/:/g, '') + "://" + window.location.hostname + "//api/sitecore/ItemCompareService/Download?fileName=" + fileName;
        window.open(url);
    });
});


One thought on “Compare Sitecore contents between two environments

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 )

Google+ photo

You are commenting using your Google+ 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