Sitecore Layout Comparer Extension Classes

February 21, 2011

So during a project where I was converting a site to new templates for a rebuild, I was automating the creation of the new items and needed to know if the current page had any layout overrides on the item. It was necessary for me to compare each page's layout definition against the layout definition assigned to its standard value. If you've never ventured into the standard fields let alone the raw values of an item you might find this useful. The layout fields are stored as xml and keep references to all devices, sublayouts and layouts assigned to it. I ended up writing a few classes that handles the comparisons. It's not something that's entirely common but occassionally you need to know how to deal with the layouts programmatically. Hopefully someone else will find it useful too.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Sitecore.Layouts;
using Sitecore.Data.Items;
using Sitecore.Data;

namespace YourSite.Extensions
{
	public static class LayoutDefinitionExtensions
	{
		/// <summary>
		/// Is Equal To - this will determine if a layout definition is equal to another layout definition
		/// </summary>
		public static bool IsEqualTo(this LayoutDefinition layout1, LayoutDefinition layout2) {

			//get device lists
			ArrayList dd1 = layout1.Devices;

			//compare the device count
			if (dd1.Count.Equals(layout2.Devices.Count)) {
				foreach (DeviceDefinition d1 in dd1) {
					DeviceDefinition d2 = layout2.GetDevice(d1.ID);
					if (d2 != null) {
						if (!d1.IsEqualTo(d2)) {
							return false;
						}
					} else {
						return false;
					}
				}
			} else {
				return false;
			}
			return true;
		}
	}

	public static class DeviceDefinitionExtensions
	{
		/// <summary>
		/// Is Equal To - this will determine if a device definition is equal to another device definition
		/// </summary>
		public static bool IsEqualTo(this DeviceDefinition device1, DeviceDefinition device2) {

			//compare the layout on the device
			if (!device1.Layout.Equals(device2.Layout)) {
				return false;
			}
			ArrayList rd1 = device1.Renderings;

			//compare the rendering count 
			if (rd1.Count.Equals(device2.Renderings.Count)) {
				foreach (RenderingDefinition r1 in rd1) {
					RenderingDefinition r2 = device2.GetRendering(r1.ItemID);
					if (r2 != null) {
						if (!r1.IsEqualTo(r2)) {
							return false;
						}
					} else {
						return false;
					}
				}
			} else {
				return false;
			}
			return true;
		}

		/// <summary>
		/// Inner Item - this will return an item reference of the device on a device definition
		/// </summary>
		public static Item InnerItem(this DeviceDefinition device) {
			return InnerItem(device, Sitecore.Context.Database);
		}

		public static Item InnerItem(this DeviceDefinition device, Database db) {
			return db.Items[device.ID];
		}

		/// <summary>
		/// Layout - this will return an item reference of the layout on a device definition
		/// </summary>
		public static Item Layout(this DeviceDefinition device) {
			return Layout(device, Sitecore.Context.Database);
		}

		public static Item Layout(this DeviceDefinition device, Database db) {
			return db.Items[device.Layout];
		}

		/// <summary>
		/// Sublayouts - this will return a list of rendering definitions on a device definition
		/// </summary>
		public static List<RenderingDefinition> Sublayouts(this DeviceDefinition device) {
			return Sublayouts(device, Sitecore.Context.Database);
		}

		public static List<RenderingDefinition> Sublayouts(this DeviceDefinition device, Database db) {
			List<RenderingDefinition> rdl = new List<RenderingDefinition>();
			ArrayList al = device.Renderings;
			foreach (RenderingDefinition rd in al) {
				Item rend = db.Items[rd.ItemID];
				if (rend.IsNotNull() && rend.TemplateName.Equals("Sublayout")) {
					rdl.Add(rd);
				}
			}
			return rdl;
		}

		/// <summary>
		/// XSL Renderings - this will return a list of Rendering Definitions on a Device Definition
		/// </summary>
		public static List<RenderingDefinition> XslRenderings(this DeviceDefinition device) {
			return XslRenderings(device, Sitecore.Context.Database);
		}

		public static List<RenderingDefinition> XslRenderings(this DeviceDefinition device, Database db) {
			List<RenderingDefinition> rdl = new List<RenderingDefinition>();
			ArrayList al = device.Renderings;
			foreach (RenderingDefinition rd in al) {
				Item rend = db.Items[rd.ItemID];
				if (rend.IsNotNull() && rend.TemplateName.Equals("Xsl Rendering")) {
					rdl.Add(rd);
				}
			}
			return rdl;
		}
	}

	public static class RenderingDefinitionExtensions
	{
		/// <summary>
		/// Is Equal To - this will determine if a Rendering Definition is equal to another Rendering Definition by comparing all placeholders 
		/// </summary>
		public static bool IsEqualTo(this RenderingDefinition r1, RenderingDefinition r2) {

			//compare item id 
			if (!r1.ItemID.Equals(r2.ItemID)) {
				return false;
			}
			//compare the placeholder
			if (!r1.Placeholder.Equals(r2.Placeholder)) {
				return false;
			}

			return true;
		}

		public static Item InnerItem(this RenderingDefinition r) {
			return InnerItem(r, Sitecore.Context.Database);
		}

		public static Item InnerItem(this RenderingDefinition r, Database db) {
			return db.GetItem(r.ItemID);
		}
	}
	
}

Here are also some Item extensions that work with the layouts/sublayouts

public static class ItemExtensions {
	#region LAYOUTS SUBLAYOUTS TEMPLATES

	/// <summary>
	/// Will return true if the item contains a sublayout path you've provided
	/// </summary>
	/// <param name="thisItem" />
	/// The item whose sublayouts are to be checked
	/// 
	/// <param name="sublayoutPath" />
	/// The path after "/sitecore/layout/Sublayouts/"
	/// 
	/// <returns></returns>
	public static bool HasSublayout(this Item thisItem, String sublayoutPath) {

		Sitecore.Data.Database db = Sitecore.Context.Database;

		// get the Renderings field of the item, which 
		// corresponds to the Layout field in the Content Editor
		string rend = thisItem.Fields["__renderings"].Value;

		// Use the LayoutDefinition to parse the field value
		Sitecore.Layouts.LayoutDefinition layout = new LayoutDefinition();

		layout = LayoutDefinition.Parse(rend);

		// Get the current device from the Context 
		Sitecore.Data.Items.DeviceItem dev = Sitecore.Context.Device;

		// Get the device definition for the current device from the
		// Test Item’s Layout field
		Sitecore.Layouts.DeviceDefinition device = layout.GetDevice(dev.ID.ToString());

		// we are looking for references to the Document rendering
		// so retrieve that rendering from the master database
		Item docRendering = db.Items["/sitecore/layout/Sublayouts/" + sublayoutPath];

		// Look for the document rendering in the definition of 
		// the current device in the Test Item’s layout field
		Sitecore.Layouts.RenderingDefinition rendering = device.GetRendering(docRendering.ID.ToString());

		// If the rendering is not null, then it is referenced
		// on the current device in the layout field for the
		// Test Item
		return (rendering != null) ? true : false;
	}

	/// <summary>
	/// This will return a Device Definition for the item you've provided or null if none is found
	/// </summary>
	public static DeviceDefinition DefaultDevice(this Item thisItem) {
		return DefaultDevice(thisItem, Sitecore.Context.Database);
	}

	public static DeviceDefinition DefaultDevice(this Item thisItem, Database db) {
		List<devicedefinition> al = thisItem.Devices();
		foreach (DeviceDefinition dd in al) {
			Item device = db.GetItem(dd.ID);
			if (((CheckboxField)device.Fields["Default"]).Checked) {
				return dd;
			}
		}
		return null;
	}

	/// <summary>
	/// This will return a list of Device Definitions on the provided item
	/// </summary>
	public static List<devicedefinition> Devices(this Item thisItem) {

		List<devicedefinition> list = new List<devicedefinition>();

		Sitecore.Layouts.LayoutDefinition layout = LayoutDefinition.Parse(thisItem["__Renderings"]);
		ArrayList al = layout.Devices;
		foreach (DeviceDefinition dd in al) {
			list.Add(dd);
		}

		return list;
	}

	/// <summary>
	/// Check to see if an item contain a layout and returns true if there is a match
	/// </summary>
	public static bool HasLayout(this Item thisItem) {

		return (!thisItem["__Renderings"].Equals("") && thisItem.Devices().Count > 0) ? true : false;
	}

	/// <summary>
	/// Return true if the placeholder key is set on a sublyout or xsl rendering applied to the presentation settings of this item
	/// </summary>
	/// <param name="thisItem" />
	/// The item being queried
	/// 
	/// <param name="PlaceholderKey" />
	/// The key of the sitecore placeholder to search for
	/// 
	/// <returns></returns>
	public static bool HasPlaceholderApplied(this Sitecore.Data.Items.Item thisItem, string PlaceholderKey) {

		string rend = thisItem.Fields["__renderings"].Value;
		Sitecore.Layouts.LayoutDefinition layout = LayoutDefinition.Parse(rend);
		Sitecore.Data.Items.DeviceItem dev = Sitecore.Context.Device;
		Sitecore.Layouts.DeviceDefinition device = layout.GetDevice(dev.ID.ToString());
		foreach (Sitecore.Layouts.RenderingDefinition rd in device.Renderings) {
			if (rd.Placeholder.Equals(PlaceholderKey)) {
				return true;
			}
		}
			return false;
	}

	/// <summary>
	/// this will return a list of placeholder key names that are applied to a device definition on an item. if you specify a placeholder this will return those whose key matches. if you provide ignoreSublayout it will return all but that key.
	/// </summary>
	public static List<string> Placeholders(this Item item, string placeholder, string ignoreSublayout) {
		List<string> s = new List<string>();

		Sitecore.Data.Items.DeviceItem dev = Sitecore.Context.Device;
		string rend = item.Fields["__renderings"].Value;
		Sitecore.Layouts.LayoutDefinition layout = LayoutDefinition.Parse(rend);
		Sitecore.Layouts.DeviceDefinition device = layout.GetDevice(dev.ID.ToString());
		foreach (Sitecore.Layouts.RenderingDefinition rd in device.Renderings) {
			bool addIt = true;

			if (!ignoreSublayout.Equals("")) {
				Item i = Sitecore.Context.Database.Items[rd.ItemID];
				if (i.DisplayName.Equals(ignoreSublayout)) {
					addIt = false;
				}
			}
			if (!placeholder.Equals("") && !rd.Placeholder.Equals(placeholder)) {
				addIt = false;
			}

			if (addIt) {
				s.Add(rd.Placeholder);
			}
		}
		return s;
	}

	/// <summary>
	/// Checks to see if the item has the base template specified
	/// </summary>
	/// <param name="thisItem" />
	/// The item whose base templates will be checked 
	/// 
	/// <param name="BaseTemplateName" />
	/// The base template name to check for
	/// 
	/// <returns></returns>
	public static bool HasBaseTemplate(this Sitecore.Data.Items.Item thisItem, string BaseTemplateName) {

		TemplateItem[] bases = thisItem.Template.BaseTemplates;
		bool found = false;
		foreach (TemplateItem temp in bases) {
			found = (temp.DisplayName.Equals(BaseTemplateName)) ? true : false;
		}

		return found;
	}

	#endregion	
}