Skip to content

Latest commit

 

History

History
137 lines (103 loc) · 6.74 KB

File metadata and controls

137 lines (103 loc) · 6.74 KB

Perplex.ContentBlocksToBlockList

Migrates Perplex.ContentBlocks data types and property data to Umbraco.BlockList.

Installation

dotnet add package Perplex.ContentBlocksToBlockList

Usage

This package contains a migration that will run automatically on startup. There is nothing you need to do other than run the website once, and all Perplex.ContentBlocks data types and property data will be migrated to Umbraco.BlockList format.

When the migration is completed, Perplex.ContentBlocks will no longer be used in your website.

NOTE: You do not need to have Perplex.ContentBlocks installed to run this package; it does not depend on it. In fact, it is recommended to uninstall Perplex.ContentBlocks, since there is no reason to keep it installed.

IPropertyValueMigrator

If you need to customize how individual property values within blocks are migrated, implement IPropertyValueMigrator. This is the easiest way to hook into the migration. It is called per property with the original value, property type, and content type, allowing you to return a custom migrated value for specific properties.

public class MyPropertyValueMigratorComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.Services.AddSingleton<IPropertyValueMigrator, MyPropertyValueMigrator>();
    }
}

public class MyPropertyValueMigrator : IPropertyValueMigrator
{
    public bool MigratePropertyValue(JsonNode? originalValue, IPropertyType propertyType, IContentType contentType, out JsonNode? migratedValue)
    {
        // Only handle a specific property editor or alias
        if (propertyType.PropertyEditorAlias != "My.CustomEditor")
        {
            migratedValue = null;
            return false;
        }

        // Perform your custom migration
        migratedValue = TransformValue(originalValue);
        return true;
    }

    private JsonNode? TransformValue(JsonNode? originalValue)
    {
        // Your custom migration logic here
        return originalValue?.DeepClone();
    }
}

Multiple migrators can be registered. They run in order after the default migration, and the last one returning true wins.

Note: this hook only applies to the Nested Content (v1-v3) migration path. Version 4 data has already been migrated by ContentBlocks itself, which had its own IContentBlocksPropertyValueMigrator hook.

NotificationHandler

For more advanced scenarios where you need to modify the entire migrated BlockList value (not just individual properties), you can handle the MigratedContentBlocksPropertyValueNotification. This is a lower-level hook that is sent after a full Perplex.ContentBlocks property value has been migrated to Umbraco.BlockList. The notification contains the raw JsonNode values of both the original ContentBlocks value and the migrated BlockList value. It is sent before the value is persisted to the database so you can update it directly.

Note this is quite a low-level notification giving you the raw JsonNode instances so you will need to perform some parsing yourself. See the example below:

public class MigratedContentBlocksPropertyValueNotificationHandlerComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        builder.AddNotificationHandler<MigratedContentBlocksPropertyValueNotification, MigratedContentBlocksPropertyValueNotificationHandler>();
    }
}

public class MigratedContentBlocksPropertyValueNotificationHandler : INotificationHandler<MigratedContentBlocksPropertyValueNotification>
{
    public class MigratedContentBlocksPropertyValueNotificationHandler : INotificationHandler<MigratedContentBlocksPropertyValueNotification>
{
    public void Handle(MigratedContentBlocksPropertyValueNotification notification)
    {
        // Add your custom migration logic here
        // Updates to notification.BlockListValue will be persisted to the migrated BlockList property value.

        // Basic sample:

        var contentData = notification.BlockListValue["contentData"]?.AsArray();
        var settingsData = notification.BlockListValue["settingsData"]?.AsArray();

        if (contentData is null || contentData.Count == 0)
        {
            return;
        }

        JsonNode?[] contentBlocks =
        [
            notification.ContentBlocksValue["header"]?.AsObject(),
            .. notification.ContentBlocksValue["blocks"]?.AsArray() ?? []
        ];

        if (contentBlocks is null || contentBlocks.Length == 0)
        {
            return;
        }

        foreach (var block in contentBlocks)
        {
            if (block is null) continue;

            var definitionId = block["definitionId"]?.GetValue<Guid>();
            var layoutId = block["layoutId"]?.GetValue<Guid>();
            var isDisabled = block["isDisabled"]?.GetValue<bool>();

            // The original NestedContent key
            var key = block["content"]?.AsArray()?.FirstOrDefault()?["key"]?.GetValue<Guid>();
            if (key is null) continue;

            var blockListBlock = contentData.FirstOrDefault(x => x?["key"]?.GetValue<Guid>() == key);
            if (blockListBlock is null)
            {
                continue;
            }

            // Update blockListBlock as needed or update settingsData for this block
        }
    }
}

Limitations

The package has a few known limitations, and some manual actions are required. There are no plans to resolve these issues, as the required manual actions are limited to the Umbraco.BlockList data type configuration and require minimal time to complete. This can be done locally and synced with, for example, uSync to other environments.

  1. Only blocks that are used in the property data are added to the Available Blocks in the Block List data types.
    • The migration is based only on the property data that is present in the database. If a certain ContentBlock is never used in the content, we will not know about its existence, and it will not be set as an available block in the Umbraco.BlockList data types that are created.
    • Fix: Manually check the created Umbraco.BlockList data types and ensure all available blocks are listed.
  2. Block name templates like {{title}} are not migrated to Umbraco.BlockList.
    • Fix: Manually configure the new name template in the Umbraco.BlockList configuration under Block appearance > Label. Make sure to use the new Umbraco Flavored Markdown; for example, {{title}} would become {umbValue: title} or {= title}.
  3. Features unique to Perplex.ContentBlocks that are not present in Umbraco.BlockList like layouts or the ability to hide blocks are not migrated. You can implement a notification handler for MigratedContentBlocksPropertyValueNotification to migrate these in a way that suits your use case.