Authors often tend to create new pages by duplicating and modifying existing one. When you use components extensively this could have side effects. Authors must understand, that datasources are the same on duplicated components and changing context in rendering of duplicate item will change the content on the original page, because it is the same item underneath the component.

This is why it is useful to provide users with duplicate feature that not only duplicates the item, but it also duplicates all datasources specified in renderings.

To achieve the duplication with components you can adjust sitecore uiDuplicateItem pipeline.

First we create a new Command that starts the new pipeline uiDuplicateItemWithComponents.

[Serializable]
public class DuplicateItemWithComponents : Duplicate
{
	/// <summary>
	/// Instance loggeru.
	/// </summary>


	private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

	public override void Execute(CommandContext context)
	{
		if (context.Items.Length == 1 && context.Items[0] != null)
		{
			var item = context.Items[0];
			Assert.ArgumentNotNull(item, "item");
			Start("uiDuplicateItemWithComponents", item);
		}
	}
}

The we configure the command in the sitecore configuration like this.

<command name="duplicateitemwithcomponents" type="IP.Sc.Global.Core.Global.Commands.DuplicateItemWithComponents, IP.Sc.Global"/>

And duplicate the item “/sitecore/content/Applications/Content Editor/Menues/Duplicate/Duplicate Item” in the core database.

dupticate_item_with_components

The uiDuplicateItemWithComponents pipeline is similar to original sitecore uiDuplicateItem pipeline, but the last processor that duplicates item is our own customized processor.

<uiDuplicateItemWithComponents>
    <processor mode="on" type="Sitecore.Shell.Framework.Pipelines.DuplicateItem,Sitecore.Kernel" method="CheckPermissions" />
    <processor mode="on" type="Sitecore.Shell.Framework.Pipelines.DuplicateItem,Sitecore.Kernel" method="GetName" />
    <processor mode="on" type="IP.Sc.Global.Core.Global.Pipelines.UiDuplicateItemWithComponents.DuplicateItemWithComponents, IP.Sc.Global" method="Execute" />
</uiDuplicateItemWithComponents>

The Execute method differs from the original on the line where DuplicateItem method is invoked. The original code does not store the duplicated item, but we need it for following step of duplicating the components. Our change looks like this.

var duplicate = Context.Workflow.DuplicateItem(item, args.Parameters["name"]);
ProcessLayout(duplicate);
ProcessChildren(duplicate, duplicate.Name);

The ProcessLayout method is called on the duplicate item and it processes its shared and final layout.

private void ProcessLayout(Item item, string rootName = null)
{
	if (item == null)
	{
		return;
	}

	var sharedLayout = GetLayoutDefinition(item, FieldIDs.LayoutField);
	var finalLayout = GetLayoutDefinition(item, FieldIDs.FinalLayoutField);

	DuplicateComponents(item, sharedLayout, rootName);
	DuplicateComponents(item, finalLayout, rootName);

	ItemUtil.SetLayoutDetails(item, sharedLayout.ToXml(), finalLayout.ToXml());
}

The GetLayoutDefinition method extracts the LayoutDefinition object from the layout field:

private LayoutDefinition GetLayoutDefinition(Item item, ID fieldId)
{
	return LayoutDefinition.Parse(LayoutField.GetFieldValue(item.Fields[fieldId]));
}

And we duplicate each datasource item in the layout definition using this method:

private void DuplicateComponents(Item item, LayoutDefinition layout, string rootName = null)
{
	foreach (DeviceDefinition device in layout.Devices)
	{
		foreach (RenderingDefinition rendering in device.Renderings)
		{
			if (!string.IsNullOrEmpty(rendering.Datasource))
			{
				var dataSourceItem = item.Database.GetItem(rendering.Datasource);
				if (dataSourceItem != null)
				{
					var newDataSourceItem = Context.Workflow.DuplicateItem(dataSourceItem, (string.IsNullOrEmpty(rootName) ? "" : rootName + "-") + item.Name);
					if (newDataSourceItem != null)
					{
						rendering.Datasource = newDataSourceItem.ID.ToString();
					}
				}
			}
		}
	}
}

The updated datasources are saved using call to

ItemUtil.SetLayoutDetails

in the ProcessLayout method.

The ProcessChildren method just goes through all duplicate child items recursively and calls the ProcessLayout method on each of them.

private void ProcessChildren(Item item, string rootName)
{
	foreach (Item child in item.GetChildren())
	{
		ProcessLayout(child, rootName);
		ProcessChildren(child, rootName);
	}
}

This is because sitecore duplicate pipeline duplicates item and all its children. It uses Context.Workflow.DuplicateItem method that executes Duplicate method in Item. Which calls CopyTo method with parameter deep set to true.
You can certainly adjust this behavior by writing your own Duplicate method, that will set the deep parameter to false.