Redaktoři často vytvářejí stránky zkopírováním existující a modifikací kopie. Při používání komponent může takovým postupem docházet k nechtěným vedlejším účinkům. Redaktoři musí mít na paměti, že zdroje dat komponent použité na zkopírované stránce jsou stejné jako na originále a změna obsahu komponenty na zkopírované stránce změní obsah také na stránce původní.

Z tohoto důvodu je užitečné poskytnout uživatelům nástroj pro kopírování item včetně komponent. To znamená, že v průběhu duplikace item jsou zkopírované také všechny zdroje dat použité v jejich komponentách.

Pro kopírování item včetně komponent je třeba použít modifikovanou pipeline uiDuplicateItem.

Nejdříve vytvoříme příkaz, který spustí 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);
            }
        }
    }

Pak příkaz přidáme do sitecore konfigurace:

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

Zduplikuje item „/sitecore/content/Applications/Content Editor/Menues/Duplicate/Duplicate Item“ v core databázi.

dupticate_item_with_components

Pipeline uiDuplicateItemWithComponents je téměř totožná s pipeline uiDuplicateItem, jen poslední procesor je náš vlastní.

<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>

Metoda Execute se liší od originálu na řádku, kde probíhá volání samotné duplikace pomocí metody DuplicateItem. Původní kíd neukládá návratovou hodnotu duplikovaného itemu, ale my jej potřebujeme pro následující krok duplikace jeho komponent. Naše úprava metody DuplicateItem vypadá takto:

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

Metoda ProcessLayout je volána s parametrem zkopírovaného itemu a procesuje shared i 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());
}

Metoda GetLayoutDefinition extrahuje objekt LayoutDefinition pole layout:

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

A poté duplikuje každý zdroj dat v definici layoutu pomocí této metody:

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();
                    }
                }
            }
        }
    }
}

Změněné zdroje dat jsou poté uloženy voláním

ItemUtil.SetLayoutDetails

v metodě ProcessLayout.

Metoda ProcessChildren projde všechny zduplikované potomky rekursivně a volá nad nimi metodu ProcessLayout.

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

Důvodem je, že duplikace itemy v sitecore publikuje item včetně jeho potomků. K duplikaci se používá metoda Context.Workflow.DuplicateItem, která volá metodu Duplicate nad objektem Item. Metoda Duplicate itemu volá metodu CopyTo s parametrem deep nastaveným na true.
Toto chování, lze samozřejmě také upravit pomocí vlastní metody Duplicate, která bude provádět kopírování s parametrem deep nastaveným na false.