Fix invalid dynamic placeholders after upgrading to Sitecore 9

Sitecore 9 supports Dynamic Placeholders OOTB and does no longer require the popular Dynamic Placeholder third party module. The format used by Sitecore for the dynamic placeholders is different than the format used by the Dynamic Placeholder module:

Old dynamic placeholder pattern: placeholderName_renderingId. Example:

content_acda6718-0907-4a6c-8c6b-b157f5d708ac

Sitecore placeholder pattern: {placeholder key}-{rendering unique suffix}-{unique suffix within rendering}. Example:

content-{acda6718-0907-4a6c-8c6b-b157f5d708ac}-0

You will have to update your presentation details for these differences otherwise the renderings will not be shown on the page.

Richard Seal already wrote a great blog post about how to upgrade this using SPE. This works well and also does a great job of explaining other tasks that need to be performed to move to Sitecore’s dynamic placeholders. However it doesn’t cover below 2 scenario’s:

  • Nested Dynamic placeholder: a rendering with a dynamic placeholder can contain another rendering with a dynamic placeholder.
  • Multilingual: a item can have dynamic placeholders in multiple language versions of the final layout.

Approach

I started with Martin Miles’ code which iterates through all the renderings and updates placeholders. From here the logic is updated to loop through each language version of every item. Below code updates every match of an old style dynamic placeholder so nested placeholders are taken care of appropriately:

bool requiresUpdate = false;

foreach (RenderingDefinition rendering in device.Renderings)
{
    if (!string.IsNullOrWhiteSpace(rendering.Placeholder))
    {
        var newPlaceholder = rendering.Placeholder;
        foreach (Match match in Regex.Matches(newPlaceholder, PlaceholderRegex, RegexOptions.IgnoreCase))
        {
            var renderingId = match.Value;
            var newRenderingId = "-{" + renderingId.ToUpper().Substring(1) + "}-0";

            newPlaceholder = newPlaceholder.Replace(match.Value, newRenderingId);

            requiresUpdate = true;
        }

        result.Add(new KeyValuePair<string, string>(rendering.Placeholder, newPlaceholder));
        rendering.Placeholder = newPlaceholder;
    }
}

if (requiresUpdate)
{
    string newXml = details.ToXml();

    using (new EditContext(currentItem))
    {
        LayoutField.SetFieldValue(field, newXml);
    }
}

Working Solution

The final working solution is uploaded to my Github and can be found here A few notes on solution:

  • Admin page is added at <sitecoreurl>/sitecore/admin/custom/migratedp.aspx
  • All content under /sitecore/content is updated, template standard values are not
  • Make sure to back up content before running this

 

Advertisements