EPiserver & Powerpaste postprocessing

How to run additional content cleanup after pasting rich content from MS Word.

EPiserver & Powerpaste postprocessing

One of the most important pieces of EPiserver for content editing is the support for rich text. EPiserver integrates with one of the most popular and well known rich text editors for web browsers - TinyMCE. Out of the box TinyMCE is packed with features and configuration options and its ecosystem is packed with plugins (both free and paid), that can greatly expand its capabilities.

I have recently been asked to add support for PowerPaste plugin for the TinyMCE editor in EPiserver. When editors are building the pages, they often copy some of the content of their existing MS Word documents. However there was an issue when pasting the content from MS Word and keeping the formatting in TinyMCE.

TinyMCE PowerPaste plugin primer

Following the official documentation, I have configured the plugin to prompt users when they are pasting MS Word content.

Paste Formatting Options dialog

When editors are pasting the content into the TinyMCE from MS Word, they can choose whether the formatting should be kept or removed. If the remove formatting was chosen, PowerPaste deletes all the styling information from the content - custom font styles, bolds, italics etc. All the formatting is preserved in (with some exceptions) in case the editor has chosen to keep it.

The problem

The problem I faced was due to the fact that the markup generated by MS Word that was getting pasted into TinyMCE contained some inline styles. When choosing the Keep Formatting option, all those styles are being preserved, thus not meeting the formatting and styling guidelines set by the design team.

I started looking around for a way to remove the inline styles from the content being pasted into the editor.

The solution

Luckily enough, PowerPaste exposes two callbacks that are executed when content is pasted:

  • paste_preprocess - allows to run custom filtering on the content being pasted from the clipboard before it is run through PowerPaste’s filters.
  • paste_postprocess - allows to run custom filtering on the content being pasted from the clipboard after it is run through PowerPaste’s filters.

After some debugging and a bit more of trial & error, I decided that paste_postprocess callback suits me better:

  • paste_postprocess operates on the HTML node(s) allowing to recursively go through the elements
  • paste_preprocess operates on the plain HTML/text string being pasted.

Hooking into paste_postprocess callback

In order to hook into the callback we need to update the TinyMCE settings object that is used to initialize editor instances during editing interface rendering. It’s quite simple - we can do this by creating an initialization script for TinyMCE. I figured this out thanks to an answer from Grzegorz Wiecheć posted on the EPi Server World forum in this thread.

First, we have to create a script under the ClientResources/Scripts folder (for example ClientResources/Scripts/TinyMCE/PowerPaste-PostProcess.js). In my case, the content of the file was:

define([], function () {

    // Remove all in-line styles from the node and child nodes
    function removeStyles(el) {
        if (el.nodeType === 1) { // process only element nodes
            el.removeAttribute('style');
        }

        for (let child in el.childNodes) {
            removeStyles(el.childNodes[child]);
        }
    }   
    
    return function (settings) {
        return Object.assign(settings, {
            // tap into TinyMCE callback/event
            paste_postprocess: function (pluginApi, data) {
                // remove any inline styles from child nodes                
                var childNodes = data.node.childNodes;
                for (let child in childNodes) {
                    removeStyles(childNodes[child]);     
                }
            }
        });
    }
});

Registering initlization script for TinyMCE

In order to execute the script while TinyMCE editor is initialized we have to configure the PowerPaste-PostProcess.js file as an initialization script in EPi Server initialization module. Below you can see an example on this can be achieved.

context.Services.Configure<TinyMceConfiguration>(configuration =>
{		
  configuration
    .Default()
    .RemovePlugin("paste") // PowerPaste is not compatibile with OOTB paste plugin
    .AddExternalPlugin("powerpaste", "/powerpaste/powerpaste/plugin.min.js")
    .InitializationScript("alloy/TinyMCE/PowerPaste-PostProcess");			
});

… and that’s it! From now on everytime content is pasted into TinyMCE all inline styles are being removed from the pasted nodes.

Summary

This post was to show you how to extend the PowerPaste plugin via paste_postprocess callback that can be used if the standard configuration does not fulfill all the requirements. What I described is just the tip of the iceberg in terms of TinyMCE extensibility. Plugins and robust configuration make the possibilities almost endless. I encourage you to visit EPiserver documentation for TinyMCE integration as well as TinyMCE documentation to find out more cool stuff you can do.