How to migrate existing content into Kentico 12 MVC widgets
Technical | Kentico
Dragoljub Ilic

By Dragoljub Ilic | Software Developer 20.06.2019.

How to migrate existing content into Kentico 12 MVC widgets

Kentico 12 was released last November and with it there have been a lot of challenges related to the widgets and content migration. It is a really big effort to upgrade a Portal engine site to MVC -- in some cases it can be nearly impossible -- but if your site is already using the MVC development model then there won’t be a problem to migrate content to the new version. With the exception of widgets that is.

Before we start with the upgrade and migration, it’s necessary to determine if page builder is really needed. Page builder provides a user-friendly interface where non-technical users can manage content using configurable widgets. However, these widgets need to be first prepared by the developers in advance. Page builder then enables content editors to experiment with different layouts and immediately see the results in a drag-and-drop interface. For more information about making correct determination about the use of Page Builder (pros/cons) check this link.

In our example, we chose an approach which we have used on several MVC based sites where we needed to combine different components (e.g. building blocks) on one page. In our experience, it was much better to give editors defined components which can be combined on the page and place them directly in the content tree (see image 1 bellow) rather than allowing inline widgets for this purpose. We found that allowing inline widgets became messy and difficult for our customers to use, because the editors cannot see a visual representation with nested widgets – they can only see the results when previewing the page.

Image 1. Building blocks structure of single blog page Image 1. Building blocks structure of single blog page

From image above, you can see the hierarchy of the components used on the blog page. In this case, there is only one level of the pages, but this can be extended with sections that add additional levels of flexibility/complexity in the organization of the content. On the other hand, however, this would enable editors to see nested ‘widgets’ on the site.

All of the problems that we’ve discussed so far, are addressed by a new feature called page builder from Kentico 12. It now gives us the opportunity to control the order of widgets, style them, change layout and sections and much more without first creating and organizing the content into a content tree. That looks really nice if you are working on the site from scratch, but what about sites that already exist and require content migration to support page builder? Well this is what we set out to solve!

One of the perquisites is to upgrade project (Kentico and MVC) to latest hotfix release of Kentico 12 and set up page builder (check documentation for more information).

After we successfully complete the upgrade, we need to create MVC widgets for which content editors can use to add widgets in page builder. Once the widgets are ready, then we can proceed with the migration.

If we look closely at the changes, you may notice that a new column called DocumentPageBuilderWidgets was added in table CMS_Documents. This new field is used to store all new data related to page builder for each specific page. The data is stored in JSON format (see image 2 below) with valid JSON property types being used, such as: strings, numbers and arrays of the two.

Image 2. Json example of widgets structure in page builder field Image 2. Json example of widgets structure in page builder field

Now using the example above, we will design a new (C#) class model that represent 1-to-1 model with the JSON data model for our widget to use (see image 3 below).

Image 3. Class model which correspond to JSON example Image 3. Class model which correspond to JSON example

Next, we need to map the content from our pages, in the Kentico CMS, into widget fields – ensuring that we preserve same structure as is in JSON file. Using this basic example as a reference, we would need to get all pages from the components folder with the Kentico API. Then, we can dynamically return the fully instantiated model back by using some custom bindings based on the class name.

To help with this process, we have introduced a simple interface below that allows us do that (see code snippet 1 below).

using CMS.DocumentEngine;
using System;

namespace Custom.Kentico.Widgets.Mappers
{
    public interface IMapper
    {
        dynamic Map(TreeNode node);

        String GetWidgetType();
    }
}
Code snippet 1. Mapper interface which is implemented by every custom mapper

GetWidgetType is used to return widget type name, a string that specifically identifies the type of widget each implementation is. Then the Fields in the page type, responsible for content, can be mapped to widget properties fields defined for each widget. All of the mapping logic for the fields is placed in the Map method.

After we created our interface, we can now create the RichTextMapper class (see code snippet 2 below) which implements the IMapper interface and adds the custom logic to map the content properly for the specific Rich Text widget example.

using CMS.DocumentEngine;
using Custom.Kentico.Widgets.Models;
using Custom.Kentico.Widgets.Controllers;
using System.Dynamic;

namespace Custom.Kentico.Widgets.Mappers.Implementation
{
    public class RichTextMapper : IMapper
    {
        public string GetWidgetType()
        {
            return RichTextController.Identifier; // Rich text widget identifier
        }

        public dynamic Map(TreeNode node)
        {
            dynamic widget = new ExpandoObject();
            widget.text = node.GetValue(nameof(BlogRichText.Text)); // Page type ‘text’ field
	
            return widget;
        }
    }
}
Code snippet 2. Example of custom mapper for rich text widget

Finally we’ve gotten to the fun part . . . When we repeat this for each component (page type) and generate json for each property, we can then simply serialize the object into json and update DocumentPageBuilderWidgets field.

Even if the content is organized differently in your current solution, it shouldn’t present a big problem because the only thing that would need to be changed is how you map fields from Kentico pages to the widget’s properties. Whether you have all fields on one page type, or have inline widgets in rich text, you will still retrieve them and parse in the same way. The difference being that the final widget that is returned would be mapped on new widget properties.

There are still a few other things you might need to consider on a case-by-case basis. Such as when retrieving content from page, you may need to check for workflow or if is a page checked-out by someone -- which poses a risk of losing content after you update it from the Kentico API.

But, this approach makes everything pretty simple, right? In the end, you can wrap up all of the migration logic and run it as a scheduled task, custom module, etc...

Of course, this example is simplified for using variants per widget and multiple sections/editable areas on the page, but I hope that we’ve been able to provide a clear starting point for how you could potentially work with widgets more effectively in Kentico 12 MVC.

Dragoljub Ilic

By Dragoljub Ilic | Software Developer 20.06.2019.