How to create a CKeditor plugin for Drupal 7

My installation settings:

  • Drupal 7
  • WYSIWYG Version 7.x-2.2+73-dev
  • ckeditor 4.5.1

Client requirements.

I created a simple CKeditor plugin that allows non-technical writers to change the text of a URL only by selecting (partially) the link and editing the content in a prompt.

This small plugin allows the end users to avoid typos or deleting the URL by mistake.

In order to make this plugin work in Drupal, I needed to create a plugin for the WYSIWYG Drupal module (the Drupal way).

Plugin demo:

The Drupal 7 Module:

Drupal 7 module structure:

 - ckeditor_linkchange/ <- Drupal 7 Module folder.
    - plugins/
        - linkchange/ <- Actual ckeditor plugin.
    - ckeditor_linkchange.info <- Drupal 7 info file.
    - ckeditor_linkchange.module <- Drupal 7 module file.

ckeditor_linkchange.info

This file declares our module and its dependencies:

name = "Ckeditor linkchange plugin"
description = "Adds the ability to change the text or a url with the linkchange button (CKEditor)"
package = Custom
dependencies[] = wysiwyg
core = 7.x

ckeditor_linkchange.module

The `hook_wysiwyg_plugin()` lets the WYSIWYG module know that we have a new plugin available.

 <?php
    
/**
 * Implements hook_wysiwyg_plugin().
 *
 */
function ckeditor_linkchange_wysiwyg_plugin($editor, $version) {
    $plugins = array();
    switch ($editor) {
        case 'ckeditor':
            if ($version > 4) {
                $plugins['linkchange'] = array(
                    'path' => drupal_get_path('module', 'ckeditor_linkchange') . '/plugins/linkchange',
                    'filename' => 'plugin.js',
                    'load' => TRUE,
                    'buttons' => array(
                        'linkchange' => t('Linkchange'),
                    ),
                );
            }
            break;
    }
    return $plugins;
}

In my case I checked that the ckeditor version is superior to version number 4.

the array $plugins['linkchange'] contains the path to the plugin and the buttons parameter which allows this plugin to be enabled from the WYSIWYG UI (admin/config/content/wysiwyg/).

The CKeditor plugin:

 - linkchange/
    - dialogs/
        - linkchange.js
    - icons/
        - linkchange.png    
    - plugin.js

plugin.js

 (function($) {
    CKEDITOR.plugins.add( 'linkchange', {
        icons: 'linkchange',
        init: function( editor ) {
            editor.addCommand( 'linkchange', new CKEDITOR.dialogCommand( 'linkchangeDialog' ) );
            editor.ui.addButton( 'linkchange', {
                label: 'Link text change',
                command: 'linkchange',
                toolbar: 'linkchange'
    
            });
            if ( editor.contextMenu ) {
                editor.addMenuGroup( 'linkchangeGroup' );
                editor.addMenuItem( 'linkchangeItem', {
                    label: 'Change link',
                    icon: this.path + 'icons/linkchange.png',
                    command: 'linkchange',
                    group: 'linkchangeGroup'
                });
                editor.contextMenu.addListener( function( element ) {
                    if ( element.getAscendant( 'linkchange', true ) ) {
                        return { abbrItem: CKEDITOR.TRISTATE_OFF };
                    }
                });
            }
            CKEDITOR.dialog.add( 'linkchangeDialog', this.path + 'dialogs/linkchange.js' );
        }
    });
})(jQuery)

dialogs/linkchange.js

 CKEDITOR.dialog.add( 'linkchangeDialog', function( editor ) {
    return {
        title: 'Text link change',
        minWidth: 400,
        minHeight: 200,
        contents: [
            {
                id: 'tab-basic',
                label: 'Basic Settings',
                elements: [
                    {
                        type: 'text',
                        id: 'txtchng',
                        label: 'Text update',
                        setup: function( element ) {
                            this.setValue( element.getText() );
                        },
                        commit: function( element ) {
                            element.setText( this.getValue() );
                        }
                    }
                ]
            }
        ],
        onShow: function() {
            var selection = editor.getSelection();
            var element = selection.getStartElement();
            this.element = element;
            if(element.$.nodeName === "A") { // Only if it's and anchor.
                this.setupContent( this.element );
            } else {
                this.hide();
            }
        },
        onOk: function() {
            var txt = this.element;
            this.commitContent( txt );
            if ( this.insertMode )
                editor.insertElement( txt );
        }
    };
});

The complete module can be found here you can take it as reference to implement yours.