Migrating Episerver Dynamic Content to Episerver Blocks
Apr 11, 2018
Update
The official Episerver documentation has been updated to include some of the content in this post. More information: https://world.episerver.com/documentation/developer-guides/CMS/dynamic-content/migrating-dynamic-content-to-blocks/
Episerver Dynamic Content was a tool that allowed developers to programmatically insert content into XHTML property values and was introduced in Episerver around 2010.
My blog runs Episerver 11 and I’ve used Dynamic Content in the past for embedded code snippets (though I normally just embed straight from Gist these days). So I decided it was long overdue that I move away from using Dynamic content as it’s a obsolete piece of tech, isn’t compatible with the new TinyMCE editor, doesn’t work with the new Episerver Headless API and sooner or later will go out of support completely.
This is the approach I took when migrating from Episerver Dynamic Content to Episerver Blocks.
Approach
- Identify and iterate the items that use dynamic content
- Get the original content saved by the dynamic content control and extract the content for migration
- Create a new block to render the Dynamic Content control
- Create a block to replace the Dynamic Content control at the page level
- Update the XHTML string property
1. Identify and Iterate the items that use dynamic content
The following query to find all content IDs where Dynamic Content is used:
SELECT [fkContentID] FROM [dbo].[tblContentProperty] WHERE LongString LIKE \'%data-dynamicclass%\' ORDER BY [fkContentID]
I don’t normally recommend using SQL against the Episerver database but as this is a one off for migration and also isn’t manipulating data it is safe.
2. Get the original content saved by the dynamic content control and extract the content for migration
Fortunately when loading up the content its possible integrate the fragments of the XHTML content. The fragment can be tested to check if it's dynamic content. The data/parameters that Dynamic Content used to display are stored as text in-line with the XHTML property so it's possible to use the original dynamic control to deserialise and get the original content:
private void MigrateDyanmicContent(int contentId) { var originalPage = _contentRepository.Get<BlogPostPage>(new ContentReference(contentId)).CreateWritableClone() as BlogPostPage; var updatedXhtmlString = originalPage.BodyText.ToInternalString(); var updateXhtml = false; foreach (var bodyTextFragment in originalPage.BodyText.Fragments) { if (bodyTextFragment.GetType() == typeof(DynamicContentFragment)) { // Get the original dynamic content values using the orignal DynamicContent control // -- In this scenario we only CodeBrowserView is used so this is safe var originalDyanamicContnet = (CodeBrowserView)((DynamicContentFragment) bodyTextFragment).DynamicContent; var codeType = originalDyanamicContnet.Properties["Brush"].Value.ToString(); var codeSnippet = originalDyanamicContnet.Properties["CodeSnippet"].Value.ToString(); // Create a new block var codeBlock = CreatePageLevelBlock(contentId, codeType, codeSnippet); // Further migration code } } if (updateXhtml) { originalPage.BodyText = new XhtmlString(updatedXhtmlString); _contentRepository.Publish(originalPage, AccessLevel.NoAccess); } }
3. Create a new block to represent the Dynamic Content control
Create a standard Episerver block with the same properties as were defined for the dynamic content control and the appropriate view so it can be used to render the content. This is just a standard block that will be used in the rich text editor.
4. Create instances of the block created above to replace the Dynamic Content control
Next up a number of blocks need to be created to store the migrated Dynamic content. A basic skeleton of this code can be seen below:
private IContent CreatePageLevelBlock(int contentId, string codeType, string codeSnippetText) { ContentAssetFolder folder = _contentAssetHelper.GetOrCreateAssetFolder(new ContentReference(contentId)); var codeSnippet = _contentRepository.GetDefault<CodeSnippet>(folder.ContentLink); codeSnippet.CodeType = codeType; codeSnippet.Code = codeSnippetText; (codeSnippet as IContent).Name = contentId + "_Code"; _contentRepository.Publish(codeSnippet as IContent, AccessLevel.NoAccess); return codeSnippet as IContent; }
It adds the newly created block in the "For this page" section of the block tree.
5. Update the XHTML string property
Finally the XHTML string property needs to be updated to replace the original Dynamic Content control:
private void MigrateDyanmicContent(int contentId) { foreach (var bodyTextFragment in originalPage.BodyText.Fragments) { if (bodyTextFragment.GetType() == typeof(DynamicContentFragment)) { // Previous migration code // Update the XHTML string to embed the block and remove the dynamic content reference var embeddedBlock = "" + ((IContent) codeBlock).Name + ""; updatedXhtmlString = updatedXhtmlString.Replace(bodyTextFragment.InternalFormat, embeddedBlock); updateXhtml = true; } } if (updateXhtml) { originalPage.BodyText = new XhtmlString(updatedXhtmlString); _contentRepository.Publish(originalPage, AccessLevel.NoAccess); } }
The full source code which is executed as a scheduled job is available over at Gist.
Conclusion
Episerver Dynamic Content has its place in the past but I suspect there still may be instances of it in use today even on the latest versions of Episerver. If you haven’t already started thinking about migrating off Dynamic Content then I would advise you do this now as part of regular BAU.