Adding plugins to the Liferay Alloy Editor

I have been working on adding CKEditor plugins to the Alloy Editor in Liferay DXP and figured I would share what I found. Specifically I was trying to add the YouTube Plugin. There are some articles online at Liferay but they focus more on packaged or custom developed plugins. I got close streaming the files into the Dynamic-Include, but there is a better way (thanks to Chema Balsas for the help!): The trick is to create a OSGI module fragment.

First we create a new Liferay Module Project Fragment – this module won’t have much in it, its purpose is to add the plugin files to the ckeditor module Liferay has in it’s core modules.

To do this, we download and extract the plugin, rename it to a simpler name – in my case youtube, and copy it into the resources/META-INF/resources/ckeditor/plugins folder:

Screen Shot 2018-02-15 at 11.46.43 AM.png

Now all we need to do is edit the bnd.bnd file and inform the OSGI container of our awesome new plugin and that we would like to add it to the existing ckeditor module:

Bundle-Name: base22-alloyeditor-plugins
Bundle-SymbolicName: base22.alloyeditor.plugins
Bundle-Version: 1.0.0
Fragment-Host: com.liferay.frontend.editor.ckeditor.web

You can now test that it works by deploying our fragment and seeing if the js file is accessible. Go to http://localhost/o/frontend-editor-ckeditor-web/ckeditor/plugins/youtube/plugin.js and hopefully you see the JavaScript file.

As you can see we are accessing the same path as the standard editors plugins, we just added our own directory to it using the fragment. Powerful stuff.

Now that we have the plugin, we need to let the editor know we want to use it, and where. For this we need to create a module which implements the EditorConfigContributor – there is a good article at Liferay covering this as well.

We create our class and declare it as a component that is a EditorConfigContributor and tell it to configure the Alloy editor:

@Component(
        property = {
        "editor.name=alloyeditor",
        "service.ranking:Integer=1000"},
        service = EditorConfigContributor.class
)

Next we extend the BaseEditorConfigContributor and override the populateConfigJSONObject. This is where we change the JSON that configures the editor. The JSON object will get passed to us and we will adjust it to include our new button.

As a tip, it is really useful to set a breakpoint in the code and look at the overall configuration as you are working with the configuration.

First, we are going to get the extraPlugins section of the configuration JSON and add our youtube plugin. We also want to make sure the ae_uibridge and ae_buttonbridge are loaded to ensure the CK editor plugin is bridged into Alloy editor.

@Override
public void populateConfigJSONObject(
    JSONObject jsonObject, 
    Map<String, Object> inputEditorTaglibAttributes,
    ThemeDisplay themeDisplay,
    RequestBackedPortletURLFactory requestBackedPortletURLFactory) {

    String extraPlugins = jsonObject.getString("extraPlugins");

    if (extraPlugins == null) {
        extraPlugins = extraPlugins+ "ae_uibridge";
    }else{
        extraPlugins = extraPlugins+ ",ae_uibridge";
    }

    if(!extraPlugins.contains("ae_buttonbridge")){
        extraPlugins = extraPlugins+ ",ae_buttonbridge";
    }

    if(!extraPlugins.contains("youtube")){
        extraPlugins = extraPlugins+ ",youtube";
    }

    jsonObject.put("extraPlugins", extraPlugins);

Now that the editor knows about our plugin, we need to add the button to a toolbar. We pull the toolbar from the JSON and add our button to it:

JSONObject toolbarsJSONObject = jsonObject.getJSONObject("toolbars");

if (toolbarsJSONObject == null) {
    toolbarsJSONObject = JSONFactoryUtil.createJSONObject();
} 

JSONObject addJSONObject = toolbarsJSONObject.getJSONObject("add");

if (addJSONObject == null) {
    addJSONObject = JSONFactoryUtil.createJSONObject();
}

JSONArray buttons = addJSONObject.getJSONArray("buttons");
buttons.put("Youtube");

toolbarsJSONObject.put("add", addJSONObject);
jsonObject.put("toolbars", toolbarsJSONObject);

Nothing extraordinary is needed in the Gradle or BND files. Deploy your modules and go edit some web content. Your new button should show where you placed it.

This is another way to use OSGI fragments as mentioned by David Nebinger in his post on OSGI fragments.

Blogs
Hi Christian, I'm glad you got this all working! Thanks for sharing your results. I'm sure it will be very useful for other's trying to do the same customization.
Hi Christian, I am trying to enable template option in editor with my custom template and images. But i am unable to do so. Do you have any idea.. can you please guide me ?

Hi Christian,

Thank you for your great post.

Which type of module (template )I need to use to implement the EditorConfigContributor ?