CKEditor plugins - Bloomreach Experience - Headless Digital Experience Platform

This article covers a Hippo CMS version 12. There's an updated version available that covers our most recent release.

01-11-2018

# CKEditor plugins

The core of CKEditor can be extended with many plugins. Each plugin has a unique all-lowercase name that is used to reference the plugin in the CKEditor configuration.

## Available plugins

The following CKEditor plugins ship with Bloomreach Experience Manager by default:

Plugin Description Used by default in Formatted Text Fields in Used by default in Rich Text Fields in Available since release
Content Channels Content Channels
a11yhelp Accessibility Help No No Yes Yes 10.0
autogrow Auto Grow No Yes [2] No Yes [2] 12.0
balloonpanel Balloon Panel No No No No 12.0
balloontoolbar Balloon Toolbar No No No No 12.5
basicstyles Basic Styles Yes Yes Yes Yes 10.0
bidi BiDi (Text Direction) No No No No 10.0
blockquote Blockquote No No No No 10.0
button UI Button Yes Yes Yes Yes 10.0
clipboard Clipboard Yes Yes Yes Yes 10.0
codemirror CodeMirror (Source) Syntax Highlighting No No Yes Yes 10.0
codesnippet Code Snippet No No No No 10.0
colorbutton Color Button No No No No 10.0
colordialog Color Dialog No No No No 10.0
copyformatting Copy Formatting Yes Yes Yes Yes 12.0
dialog Dialog Yes Yes Yes Yes 10.0
dialogui Dialog User Interface Yes Yes Yes Yes 10.0
div Div Container Manager No No No No 10.0
divarea Div Editing Area Yes Yes Yes Yes 10.0
elementspath Elements Path No No Yes Yes 10.0
enterkey Enter Key Yes Yes Yes Yes 10.0
entities Escape HTML Entities Yes Yes Yes Yes 10.0
fakeobjects Fake Objects No No No No 10.0
filebrowser File Browser No No No No 10.0
find Find / Replace No No No No 10.0
floatingspace Floating Space Yes Yes Yes Yes 10.0
floatpanel Floating Panel Yes Yes Yes Yes 10.0
font Font Size and Family No No No No 10.0
format Format No No No No 10.0
forms [1] Form Elements No No No No 10.0
hippoautosave Bloomreach Experience Manager auto save Yes Yes Yes Yes 10.0
hippopicker Bloomreach Experience Manager pickers for images and internal links No No Yes Yes 10.0
horizontalrule Horizontal Rule No No No No 10.0
htmlwriter HTML Output Writer Yes Yes Yes Yes 10.0
iframe IFrame Dialog No No No No 10.0
image Image No No No No 10.0
image2 Enhanced Image No No No No 10.0
indent Indent / Outdent No No Yes Yes 10.0
indentblock Indent Block No No Yes Yes 10.0
indentlist Indent List No No Yes Yes 10.0
justify Justify No No Yes Yes 10.0
language Language No No No No 10.0
list List No No Yes Yes 10.0
listblock List Block Yes Yes Yes Yes 10.0
liststyle List Style No No Yes Yes 10.0
magicline Magic Line Yes Yes Yes Yes 10.0
mathjax Mathematical Formulas No No No No 10.0
maximize Maximize No No [3] Yes No [3] 10.0
pagebreak Page Break No No No No 10.0
panel Panel Yes Yes Yes Yes 10.0
panelbutton Panel Button Yes Yes Yes Yes 10.0
pastefromword Paste from Word No No Yes Yes 10.0
pastetext Paste as Plain Text No No Yes Yes 10.0
placeholder Placeholder No No No No 10.0
popup Popup No No Yes Yes 10.0
removeformat Remove Format Yes Yes Yes Yes 10.0
resize Editor Resize No No [3] Yes No [3] 10.0
richcombo Rich Combo Yes Yes Yes Yes 10.0
selectall Select All No No No No 10.0
sharedspace Shared Space No Yes [2] No Yes [2] 12.0
showblocks Show Blocks No No Yes Yes 10.0
showborders Show Table Borders No No Yes Yes 10.0
smiley Insert Smiley No No No No 10.0
sourcearea Source Editing Area No No [3] No No [3] 10.0
sourcedialog Source Dialog No Yes [2] No Yes [2] 12.0
specialchar Special Characters No No Yes Yes 10.0
stylescombo Styles Combo Yes Yes Yes Yes 10.0
tab Tab Key Handling Yes Yes Yes Yes 10.0
table Table No No Yes Yes 10.0
tableresize Table Resize No No Yes Yes 10.0
tableselection Table Selection No No Yes Yes 12.0
tabletools Table Tools No No Yes Yes 10.0
templates Content Templates No No No No 10.0
textselection Keep TextSelection No No Yes Yes 10.0
toolbar Editor Toolbar Yes Yes Yes Yes 10.0
undo Undo Yes Yes Yes Yes 10.0
widget Widget No No No No 10.0
wordcount Word Count and Character Count No No No No 10.0
wysiwygarea IFrame Editing Area No No No No 10.0

1 The 'forms' plugin requires the CKEditor instance to run in an iframe instead of a div, please see the example configuration on how to set this up correctly.

2 Explicitly enabled by the system

3 Explicitly disabled by the system

To summarize: all plugins work in both Content and Channels, except for a few plugins that are explicitly disabled in Channels:
• The maximize plugin is replaced by the "full width toggle" in the right side-panel.
• The sourcearea plugin does not work with the inline version of CKEditor, so Channels uses the sourcedialog plugin instead.
• The resize plugin does not work with the inline version of CKEditor, so Channels uses the autogrow plugin instead.

## Runtime debugging

The names of all available plugins can also be retrieved at runtime:

1. Login to Bloomreach Experience Manager using Chrome or Firefox
2. Edit a document type that contains an HTML field
3. Open the Chrome dev tools or Firebug
4. Execute the following JavaScript one-liner:
Object.keys(CKEDITOR.plugins.registered).sort()

Similarly, it is possible to retrieve the list of plugins used by a specific editor instance:

1. Print the map of all CKEditor instances to find the name of the editor instance to inspect:
CKEDITOR.instances
2. List all plugins in a specific instance, for example 'editor123':
Object.keys(CKEDITOR.instances.editor123.plugins).sort()

## Add an existing CKEditor plugin

To add a CKEditor plugin that is already available in Bloomreach Experience Manager, use the CKEditor configuration property ' extraPlugins'. For example, to add the 'About' plugin:

{
}


A plugin may add one or more buttons to toolbar groups that are not shown by default. In that case, also add these toolbar groups. For example, to show the 'About' button in rich text fields, also add the following configuration:

{
toolbarGroups: [
]
}


## Remove an existing CKEditor plugin

To remove a CKEditor plugin that is included by default, use the CKEditor configuration property ' removePlugins'. For example, to remove the 'styles' combo box, add the following configuration:

{
removePlugins: 'stylescombo'
}


## Add a custom CKEditor plugin

Custom CKEditor plugins can be located in any .jar file that is packaged with the 'cms' application in your project. The easiest way to include them is to add them to the 'cms' artifact itself.

The source code of all custom CKEditor plugins must be placed under:

src/main/resources/ckeditor/plugins/

Each plugin has its own sub-directory. The name of the sub-directory is the name by which the plugin is referred to in the CKEditor configuration. Each plugin consist of main 'plugin.js' file that is loaded by CKEditor, and optionally more files and icons.

In production, the CMS uses an 'optimized' version of the CKEditor sources (i.e. minified, concatenated, one big sprint with all icons, etc.). These optimized sources are loaded from the directory src/main/resources/ckeditor/optimized. Custom plugins should therefore also generate a version of their source code that is put in the subdirectory 'optimized' and used together with the optimized CKEditor sources. Optimizing a few small custom plugins usually does not gain much performance, so the easiest way is to simply copy the sources with the Maven resources plugin (see the example below).

In Wicket development mode, the CMS uses the plain sources of CKEditor and custom plugins. In Wicket production mode, the 'optimized' sources are used. Developing a custom CKEditor plugin is therefore best done in Wicket development mode together with something like JRebel to hot-deploy you changes
In Bloomreach Experience Manager 11 and up, the CKEditor sources have to be included explicitly when building your project. The sources are included with the system property include.ckeditor.sources:
mvn clean verify -Dinclude.ckeditor.sources
This mechanism ensures that the CKEditor sources are only included when explicitly asked for, which saves about 6MB of unused sources in production war files.
To learn how to develop a CKEditor plugin, read the CKEditor plugin SDK documention. If your plugin adds HTML elements to the content, don't forget to adjust the HTML cleaning configuration: take care of the advanced content filter integration and add the element/attributes to the server-side HTML cleaner configuration if needed.

### Example: timestamp plugin

The CKEditor developer's guide describes an example CKEditor plugin called 'timestamp'. Perform the following steps to add this timestamp plugin to the archetype-created project:

1. Download the source code and copy the tutorial-timestamp/timestamp in the directory cms/src/main/resources/ckeditor/plugins. The final file structure should be as follows:
cms/
src/
main/
resources/
ckeditor/
plugins/
timestamp/
icons/
timestamp.png
plugin.js
2. To use a copy of the sources as an optimized version, add the following to the build/plugins section in the file cms/pom.xml:
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>create-optimized-resources</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/classes/ckeditor/optimized</outputDirectory> <resources> <resource> <directory>${basedir}/src/main/resources/ckeditor</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
3. Build the project and start it:
mvn clean verify
mvn -Pcargo.run
4. Login to the CMS as 'admin' and edit the 'newsdocument' type in the document type editor (To The Documents > Configuration > myhippoproject > newsdocument > Edit)

5. Select the 'Body' field and add the 'timestamp' plugin to the overlayed CKEditor configuration:

{
extraPlugins: 'timestamp'
}


The 'timestamp' icon should now be visible in toolbar of the editor for the 'Body' field.

## CKEditor plugin that opens a Wicket dialog

A custom CKEditor plugin can also open a Wicket dialog in Bloomreach Experience Manager. Such a plugin requires two things:

1. a CKEditor plugin for the client-side logic and UI.
2. a custom CKEditorNodePlugin Java class that creates a CKEditorPanel class with an additional CKEditorPanelExtension.

A good example of such a plugin is the 'hippopicker' plugin in the CMS. This plugin adds the 'image' and 'internal link' buttons that open the Bloomreach Experience Manager image picker and Bloomreach Experience Manager document picker, respectively (which are both Wicket dialogs). Each CKEditor field is rendered by the CKEditorPanel class. The server-side Wicket behavior for the hippopicker plugin is added by default to the CKEditorPanel by the CKEditorNodePlugin. To get your own server-side behavior in there too, create your own class that extends CKEditorNodePlugin. First add the following dependency to your cms pom.xml (or the pom.xml of another artifact that will be included in the cms war file):

<dependency>
<groupId>org.onehippo.cms7</groupId>
<artifactId>hippo-cms-richtext-ckeditor-frontend</artifactId>
</dependency>


The custom plugin class should override the createEditPanel method and add an extra CKEditorPanelExtension to the returned CKEditorPanel. For example:

public class MyCKEditorNodePlugin extends CKEditorNodePlugin {

@Override
protected CKEditorPanel createEditPanel(String id, String editorConfigJson) {
CKEditorPanel panel = super.createEditPanel(id, editorConfigJson);
return panel;
}
}


The MyCKEditorPanelExtension can then add the necessary Wicket behaviors that open your picker, and add the Ajax URL to open this picker to the editorConfig JSON object that will be provided to the client-side code. The client-side can then POST the Ajax URL to open the server-side picker. Vice-versa, the server-side can render some JavaScript that invokes a CKEditor command when the picker is closed (see the existing code for details).

The last thing to do is to use your own plugin class instead of the default one:

/hippo:namespaces/hippostd/html/editor:templates/_default_/root
- plugin.class = org.example.MyCKEditorNodePlugin


### Visual Editor

A plugin that opens a Wicket dialog cannot be used in HTML fields in the Visual Editor. The plugin code should therefore only initialize itself when the editor is loaded in a Wicket context:

CKEDITOR.plugins.add('mywicketbasedplugin', {
init: function (editor) {
if (window.Wicket) {
// initialize plugin
}
}
}


Another option is to not use Wicket at all. For example, let your plugin use CKEditor UI widgets only and let it call a custom REST API by creating a repository JAX-RS service.
{"extraction_info": {"found_math": true, "script_math_tex": 0, "script_math_asciimath": 0, "math_annotations": 0, "math_alttext": 0, "mathml": 0, "mathjax_tag": 0, "mathjax_inline_tex": 1, "mathjax_display_tex": 0, "mathjax_asciimath": 1, "img_math": 0, "codecogs_latex": 0, "wp_latex": 0, "mimetex.cgi": 0, "/images/math/codecogs": 0, "mathtex.cgi": 0, "katex": 0, "math-container": 0, "wp-katex-eq": 0, "align": 0, "equation": 0, "x-ck12": 0, "texerror": 0, "math_score": 0.21100199222564697, "perplexity": 11348.653392655684}, "config": {"markdown_headings": true, "markdown_code": true, "boilerplate_config": {"ratio_threshold": 0.18, "absolute_threshold": 10, "end_threshold": 15, "enable": true}, "remove_buttons": true, "remove_image_figures": true, "remove_link_clusters": true, "table_config": {"min_rows": 2, "min_cols": 3, "format": "plain"}, "remove_chinese": true, "remove_edit_buttons": true, "extract_latex": true}, "warc_path": "s3://commoncrawl/crawl-data/CC-MAIN-2021-25/segments/1623487626122.27/warc/CC-MAIN-20210616220531-20210617010531-00095.warc.gz"}