Branch Template Personalisation

Branch Template Personalisation

Sitecore Branch templates are a cool feature and “Sitecore Sobar’s Sitecore blogs” added a nice article to enhance it to make it even easier to relink the datasource of Rendering items to the newly created item. this custom pipeline does cover the data source updates on the personalisation rules configured in the branch template.

In this post, I have further customized the implementation of Relink Datasources from Branch Template article in Sitecore Sobar’s Sitecore blogs to include relinking of personalisation rules Datasource from Branch Template.

Code changes

using Sitecore;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.ItemProvider.AddFromTemplate;
using System;
using Sitecore.Data.Fields;
using Sitecore.Layouts;
using Sitecore.Security.Accounts;
using System.Xml.Linq;
using System.Linq;

namespace SitecoreExtensions.Extensions
{
    public class RelinkBranchTemplateRulesDataSource : AddFromTemplateProcessor

    {

        public override void Process(AddFromTemplateArgs args)
        {
            Assert.ArgumentNotNull(args, nameof(args));
            if (args.Destination.Database.Name != "master")
            {
                return;
            }

            var templateItem = args.Destination.Database.GetItem(args.TemplateId);
            Assert.IsNotNull(templateItem, "RelinkBranchTemplateRulesDataSources.Process:Template does not exists" + args.TemplateId);
            if (templateItem.TemplateID != TemplateIDs.BranchTemplate)
            {
                return;
            }
            Assert.HasAccess(args.Destination.Access.CanCreate(), $"RelinkBranchTemplateRulesDataSources.Process:Inadequate access fro (destination :{args.Destination.ID}),template:{args.TemplateId}");

            var newItem = args.Destination.Database.Engines.DataEngine.AddFromTemplate(args.ItemName, args.TemplateId, args.Destination, args.NewId);
            RewriteBranchRenderingDataSources(newItem, templateItem);
            args.Result = newItem;
        }


        private static void RewriteBranchRenderingDataSources(Item item, BranchItem branchTemplateItem)
        {
            var branchBasePath = branchTemplateItem.InnerItem.Paths.FullPath;
            ApplyActiontoAllRenderings(item, FieldIDs.LayoutField, branchBasePath);
            ApplyActiontoAllRenderings(item, FieldIDs.FinalLayoutField, branchBasePath);
        }

        private static void ApplyActiontoAllRenderings(Item item, ID fieldId, string branchBasePath)
        {
            var currentLayoutXml = LayoutField.GetFieldValue(item.Fields[fieldId]);
            if (string.IsNullOrEmpty((currentLayoutXml)))
                return;

            var newXml = ApplyActionToLayoutXml(item, currentLayoutXml, branchBasePath);
            if (string.IsNullOrEmpty((newXml)))
                return;


            if (User.Exists("sitecore\admin"))
            {
                User scUser = User.FromName("sitecore\admin", false);
                using (new UserSwitcher(scUser))
                {
                    using (new EditContext(item))
                    {
                        LayoutField.SetFieldValue(item.Fields[fieldId], newXml);
                    }
                }
            }
        }


        private static string ApplyActionToLayoutXml(Item item, string xml, string branchBasePath)
        {
            var layout = LayoutDefinition.Parse(xml);
            xml = layout.ToXml();
            for (var deviceIndex = layout.Devices.Count - 1; deviceIndex >= 0; deviceIndex--)
            {
                var device = layout.Devices[deviceIndex] as DeviceDefinition;
                if (device == null)
                    continue;

                for (var renderingIndex = device.Renderings.Count - 1; renderingIndex >= 0; renderingIndex--)
                {
                    var rendering = device.Renderings[renderingIndex] as RenderingDefinition;
                    if (rendering == null)
                        continue;

                    if (rendering.Rules != null)
                    {
                        XElement ruleset = rendering.Rules;
                        var rules = ruleset.Elements("rule").ToList();
                        foreach (var rule in rules)
                        {
                            var actions = rule.Elements("actions").Elements("action").ToList();
                            foreach (var action in actions)
                            {
                                var branchTemplateActionDataSource = action.Attribute("DataSource")?.Value;
                                var newDataSourceItem = GetTargetDatasourceId(item, branchTemplateActionDataSource, branchBasePath);
                                if (!string.IsNullOrEmpty(newDataSourceItem))
                                    action.Attribute("DataSource").Value = newDataSourceItem;
                            }
                        }
                        rendering.Rules = ruleset;
                    }
                    RelinkRederingDatasource(item, rendering, branchBasePath);
                }
            }

            var layoutXml = layout.ToXml();
            return layoutXml != xml ? layoutXml : null;
        }

        private static string GetTargetDatasourceId(Item item, string branchTemplateActionDataSource, string branchBasePath)
        {
            var renderingTargetItem = item.Database.GetItem(branchTemplateActionDataSource);
            if (renderingTargetItem == null)
                Log.Warn($"Error while expanding branch template rendering Datasources:data source {branchTemplateActionDataSource} was not resolved", "RelinkBranchTemplateRulesDataSources.RelinkRederingDatasource");

            if (renderingTargetItem == null || !renderingTargetItem.Paths.FullPath.StartsWith(branchBasePath, StringComparison.OrdinalIgnoreCase))
                return string.Empty;

            var relativeRenderingPath = renderingTargetItem.Paths.FullPath.Substring(branchBasePath.Length).TrimStart('/');
            relativeRenderingPath = relativeRenderingPath.Substring(relativeRenderingPath.IndexOf('/'));

            var newTargetPath = item.Paths.FullPath + relativeRenderingPath;
            var newTargetItem = item.Database.GetItem(newTargetPath);

            if (newTargetItem == null)
                return string.Empty;

            return newTargetItem.ID.ToString();
        }


        private static void RelinkRederingDatasource(Item item, RenderingDefinition rendering, string branchBasePath)
        {
            if (string.IsNullOrWhiteSpace(rendering.Datasource))
                return;

            var newDataSourceItem = GetTargetDatasourceId(item, rendering.Datasource, branchBasePath);

            if (string.IsNullOrEmpty(newDataSourceItem))
                return;
            
            rendering.Datasource = newDataSourceItem;
        }
    }
}

Add a Patch config

Add a patch config file to update Sitecore to use the custom class for processing

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
  <sitecore>
    <pipelines>
      <group name="itemProvider" groupName="itemProvider">
        <pipelines>
          <addFromTemplate>
            <processor type="SitecoreExtensions.Extensions.RelinkBranchTemplateRulesDataSource ,SitecoreExtensions" />
          </addFromTemplate>
        </pipelines>
      </group>
    </pipelines>
  </sitecore>
</configuration>

This change will update the personalisation rules set in the branch template to local datasource items.

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *