Extending CKEditor in Liferay

Perhaps you encountered this problem too: you have a website in LifeRay, with WYSIWYG (What You See Is What You Get) editor but you need a custom tag / functionality in your editor to avoid always tipping HTML / JavaScript code in the text editor tab (for a WordPress example: source code). For this the best solution would be a custom button in the editor.

In this article I’ll show you how to add this custom functionality to CKEditor, one of the usable editors in Liferay.

Getting Liferay

If you do not have a Liferay instance I suggest you to install one to follow along with the article.

Installation is easy: download an archived package (ZIP / RAR / TAR) available. It comes mostly with an integrated runtime environment (application server). I’ve chosen the package with JBoss.

At the time creating this article the Liferay version was 6.2 and it was shipped with a JBoss 7.1.1 application server.

Starting the JBoss (and deploying Liferay) is easy: just go into the folder where your Liferay is, enter the JBoss folder and execute

 bin/standalone.sh 

if you are on a Unix / Linux environment. For windows you have to execute the standalone.bat file in the same folder.

The variable JAVA_HOME should be set. And it should be a Java 7 (or 6) instance — JBoss does not start with a JDK 8. And unfortunately you do not get any error message in the JBoss console visibl when you start the application. I had to wait and wait but nothing happened. Then I switched back to a JDK 7 and the server started and deployed a Liferay instance. This was it.

Now let’s look at the CKEditor extension.

Creating a CKEditor plugin

The plugin we will create adds a custom download link to the site. Let’s call the plugin download.

It will add a content element to the site which displays a custom download box and enables a file download. We do not want any restriction so we enable all file types you can select. Naturally you can change this if you want to.

To have this plugin and to add it to CKEditor we need a folder with our plugin name to created. Then a plugin.js file which is the main plugin file, containing the plugin’s definition. The icons of the plugin will be located at the icons folder, the dialog (the popup window when you click on the button in the toolbar) is going into the dialog folder and is called download.js — just as the plugin itself, however you can name it as you like it is configured in the plugin.js.

Alternatively you can skip the dialog folder and the separate JS file: you can add everything up in the plugin.js. However I recommend to have a separate dialog JS file because it makes your code a bit cleaner. And so you can easily change it, you do not need to hack something in one huge file. Naturally your dialog file can get huge too.

The plugin.js file does the configuration of the plugin and the button: you can add here the icon, and define the init function which adds the command to the toolbar (and the button to hang the command on) and the editor to display when firing the command.

To select the file and add the title and description we create a dialog and place it in the dialogs folder with the name download.js.

The dialog should look like this:

Download link dialog screenshot

The dialog we should select the downloadable file and add some title and description.

The configuration of the dialog’s layout goes as for other CKEditor dialogs so I won’t go into detail.

The only thing I have to mention is the filebrowser entry where we have to set the name. The name of the file is another text field which is hidden and should contain the name of the file as it is in Liferay:

{
   type: 'text',
   id: 'filename',
   hidden: !0
},

And to set this filename parameter we have to change the normal

filebrowser: "txtUrl"

to the following, a bit more complex code:

filebrowser:
{
   action: 'Browse',
   onSelect: function (fileUrl, fileName)
   {
      var dialog = this.getDialog();
      dialog.getContentElement('txtUrl').setValue(fileUrl);
      dialog.getContentElement('filename').setValue(fileName);
      // Do not call the built-in onSelect command
      return false;
   }
}

However this method with two parameters does not exists currently. But there is a way to call it and get the file name.

Getting the file name

A big problem in the requirement is that we need the name of the file to include into the download content. If you look at CKEditor’s examples there is something: the OnSelect method gets some parameters: file URL and data which contains the meta-data of the file. However if you try the example you’ll see that Liferay does not return this data. For this we need some extension in Liferay’s file browser.

This can be done with a hook too: we need to change the frmresourceslist.html file. Because Liferay’s hooks overwrite the whole file we need to copy this file (directory structure is in the “Installing the plugin” section). The only modification is:

 window.top.opener.CKEDITOR.tools.callFunction(funcNum, fileUrl, fileName); 

This is in the OpenFile method. As you can see, Liferay’s file browser gets the information of the file name but it does not return it to the caller dialog. We fixed this little problem.

But there is one small drawback of this solution is that from now on every file browsing has to be adapted like above. So in this case we have to modify the image selection and other plugins which use the filebrowser. This needs to be done because there comes an alert with the name of the file and this comes directly from the CKEditor (creditor.js). The main problem is, that this file is obfuscated. It took me a bunch of time to figure out where the alert message comes from. This editor works as same as the underlying filebrowser: it treats the second parameter of the onSelect function as an error message and displays it.

For this I’ve added a copy of this file where I removed this alert. If an error message comes it will be handled by the integrated file browser so no need to worry about it — and no need to shock the users. The big problem with this is that I had do copy and replace a whole obfuscated JavaScript file for a specific version. So if you want to us a later version of CKEditor please do not use the hook completely. Eventually you can give me feedback on the workaround: does it work for you or if not, what’s the problem.

Enabling the plugin

The plugin won’t be enabled by default. For this you have to enable it manually in the ckconfig.jsp file. You have to modify two sections:

1) first you have to add the download plugin to the custom plugins used by the editor:

CKEDITOR.config.extraPlugins = 'ajaxsave,media,restore,scayt,wsc,download’;

2) you have to add the button to the toolbar:

I show my CKEditor toolbar for liferayArticle but you have to remember: You must include your button to every toolbar where you want it to show up.

CKEDITOR.config.toolbar_liferayArticle = [
  ['Styles', '-'],
  ['Bold', 'Italic', 'Underline'],
  ['Subscript', 'Superscript'],
  ['Download'], // custom plugins' buttons
  '/',
  ['Undo', 'Redo', '-', 'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'SelectAll', 'RemoveFormat'],
  ['Find', 'Replace', 'SpellChecker', 'Scayt'],
  ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
  ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
  '/',
  ['Source'],
  ['Link', 'Unlink', 'Anchor'],
  ['Image', 'Flash', 'Table', '-', 'SpecialChar', 'LiferayPageBreak']
];

And it looks like this when calling the editor:

LiferayArticle toolbar with custom download icon

LiferayArticle toolbar with custom download icon

Result

The result should look as follows if you add it to a Web Content Article in Liferay:

Web content with custom download

Web content with custom download

FontAwesome toolbar icons

As you could see, icons are needed to display your custom plugin’s button in the editor. Messing around with icon files (download.png) can be a bit confusing and overwhelming if you are not a designer and cannot use image-manipulation tools properly — as it is for me. And if you peek at the icon (a floppy disc), it does not resemble the feature we include: a download link into a Liferay article.

Fortunately there is FontAwesome. A library where you have fonts which display as pictures. And those you can use like icons in your CKEditor toolbar too.

For this you need to modify the editor.css file in the skins folder. I changed it for the moono style, so the CSS file is located at …../ckeditor/skins/moono/editor.css.

If you look in the browser at the source code for a toolbar button you’ll see it displays the icons as background-image CSS tags. So first we have to disable those background images, after this we can add FontAwesome.

The easiest way to disable the icons is to remove the icon definition from the plugin.js file.

.cke_button__download_icon:before
{  
   padding-left: 1px;
   font-size: 1.1rem;
   font-family: FontAwesome;
   text-align: center !important;
   content: "\f019";
}

As you can see above the CSS class name is created from the plugin name: .cke_button__download_icon. The :before enables to add a custom text (content) to the tag having this class. We can format this text as you can see, we use FontAwesome as font-family. The content describes the text we want to display. In this case it is the same download icon as for the representation in the post itself. I’ve added the editor.css to the hook too — however I’ve commented out the overriding CSS because if it stays enabled you get the content over your icon image — because the icon is used as a background-image.

Naturally you have to have a theme which contains FontAwesome. Else you get only some weird figure instead of a custom font-icon.

And if you have FontAwesome already in your Liferay and you want to (and have time to) improve this download link you can pimp up the download link and make it a custom “widget” which looks like this for example:

Pimped download box

Pimped download box

Installing the plugin

Installing a plugin to CKEditor or Liferay is not easy: you have to copy the folder containing your plugin into the CKEditor folder in your Liferay installation.

Or this is the only thing you’ll probably find if you search the internet about a how-to.

Fortunately I’m here, stuck with the same problem and I found a more elegant solution to this question: hooks.

Liferay has hooks to enable configuration of Liferay without touching the code. The popularity of the other posts (copy the files you’ve modified or added to CKEditor in Liferay) are created prior the version 6 of Liferay. Since then there are the hooks which enable the customization of Liferay itself. And we’ll use a hook to add our plugin to CKEditor.

For this we need to add our plugin to a Hook project. I’ll create it as a maven project. You can create it with ant or do something manually — it depends on you. The Liferay guide describes how to do it.

I create the following directory structure to have a maven project as my hook:

  • ckeditor-hook/
    • src/main/resources/
    • webapp/
      • META-INF/
        • custom_jsps/html/js/editor/ckeditor/
          • ckconfig.jsp
          • ckeditor.js
          • editor/filemanager/browser/liferay/
            • frmresourceslist.html
          • plugins/download/
            • plugin.js
            • dialogs/
              • download.js
          • skins/moono/
            • editor.css
      • WEB-INF/
        • lib/
        • liferay-hook.xml
        • liferay-plugin-package.properties
        • web.xml
    • pom.xml

In the pom.xml you add WAR packaging and this was it. And as you can see I removed the icon because with FontAwesome there is no need for it.

Package the project together and place the resulting WAR file into the <liferay installation folder>/deploy folder. This results in deploying the application through Liferay automatically. Perhaps it does something parallel (clearing the cache or just removing the replaced files from the cache).

However I recommend that after deployment you clear your browser’s cache and clear the main caches of the Liferay server too. Sometimes the JavaScripts get cached and you see only the old version of your code.

Conclusion and sources

Sometimes it is good to extend some tools with the usage of CSS and JavaScript. When it comes to Liferay, hooks are the best way to temporarily modify files deployed with the system.

And it is not impossible to change the regular behavior of your plugins: rewrite already installed parts of CKEditor or just simply add features. And you do not need to mess with icons and color schemes: FontAwesome can help in this case, and you can override the default button-background behavior of CKEditor.

Sources are available at my repository. As for this tool it is at my Bitbucket because it was done with my other company and the credits belong there. Naturally: the example code is free to use if you stick to the terms in the license.

The link points to a ZIP file because I’m working on the next article where I extend the browser window to enable sorting.

If you have any questions do not hesitate to ask here as a comment or per email. Happy coding!

Advertisements

3 thoughts on “Extending CKEditor in Liferay

  1. Pingback: Ordering files in the CKEditor file browser with Liferay | JaPy Software
  2. Pingback: Extending CKEditor in Liferay | Dinesh Ram Kali.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s