Article

Como crear un plugin para CKEditor, paso a paso

Tech & innovation

Como crear un plugin para CKEditor, paso a paso

CKEditor es un editor de contenidos WYSIWYG (What You See Is What You Get) para navegadores y permite usar muchas de las funcionalidad de un editor de texto directamente en la web. Nosotros lo usamos habitualmente para permitir que nuestros clientes puedan editar fácilmente los contenidos de su web. Además de ser open source, CKEditor puede extender su funcionalidad mediante plugins que nos permiten añadir funcionalidades personalizadas. En este post crearemos un plugin de ejemplo, y ¿qué mejor ejemplo que empezar con un ¡Hola Mundo!?

Creando ¡Hola Mundo!

Para nuestro plugin, vamos a insertar un icono en la barra de herramientas del editor que, al pulsarlo, insertará el mensaje ‘Hola Mundo!’ en nuestro contenido. Después lo ampliaremos para que nos pida nuestro nombre mediante una ventana de diálogo e inserte un saludo personalizado. Lo primero que haremos es crear la carpeta donde vamos a guardar nuestro plugin, en nuestro caso, ya que estamos usando Symfony en el backend, lo haremos en ./web/ckeditor/plugins/hola-mundo. Dentro de esta carpeta crearemos un archivo javascript que contendrá el código de nuestro plugin y otra carpeta donde guardaremos el icono que usaremos en la barra de herramientas. La estructura de nuestra carpeta de plugins quedaría así:

Ahora editamos el archivo plugin.js para añadir nuestro plugin.

Esto lo hacemos llamando al método plugins.add de CKEditor, pasándole el nombre del plugin como primer parámetro seguido de un objeto javascript.

En el método init de este objeto escribiremos la lógica del plugin.

embed: <script src="https://gist.github.com/anonymous/347af24983b68a3293b6303f7c115add.js"></script>

CKEDITOR.plugins.add('hola-mundo', {

    init: function(editor) {

// lógica de nuestro plugin

    }

});

En la lógica del plugin vamos a hacer dos cosas, primero vamos a añadir un comando que será el que ejecute el código para insertar el saludo ‘Hola Mundo!’ en el contenido, y por otra parte vamos a añadir un nuevo botón en la barra de herramientas del editor para que podamos lanzar este comando con un solo click.

Empecemos por el comando, para añadirlo vamos a llamar al método addCommand del editor, pasándole como parámetros el nombre del comando y un objeto javascript que contiene el método exec con la funcionalidad que ejecutará el plugin.

Aquí llamaremos al método insertText que nos permite insertar texto en el contenido del editor.

embed: <script src="https://gist.github.com/anonymous/3429e3afb1fa083b1ff118f94e37cf33.js"></script>

        editor.addCommand('hola-mundo', {

            exec: function(editor) {

                editor.insertText('Hola Mundo!');

            }

        } );

Para crear el botón de la barra de herramientas, lo hacemos de forma similar con el método ui.addButton.

En este caso le pasamos el nombre del botón y un objeto javascript en el que le indicamos cuál será el label del botón, el comando que debe ejecutar y el icono que va a usar.

embed: <script src="https://gist.github.com/anonymous/26a6fda8717a36f98a0c96db40c8fc53.js"></script>

        editor.ui.addButton('hola-mundo', {

                label: 'Hola Mundo',

                command: 'hola-mundo’,

                icon: this.path + 'icons/hola-mundo.png'

         });

Por el momento el código completo de nuestro plugin quedaría así:

embed: <script src="https://gist.github.com/anonymous/2e9713339322755a1ff607fbf06580a5.js"></script>

CKEDITOR.plugins.add('hola-mundo', {

    init: function(editor) {

        editor.addCommand('hola-mundo', {

            exec: function(editor) {

                editor.insertText('Hola Mundo!');

            }

        });

        editor.ui.addButton('hola-mundo', {

                label: 'Hola Mundo',

                command: 'hola-mundo’,

                icon: this.path + 'icons/hola-mundo.png'

        });

    }

});

Activando el plugin.

Con el código en su sitio ya solo nos queda activar el plugin. En nuestro caso estamos usando CKEditor en Symfony a través del IvoryCKEditorBundle, así que para activar el plugin tenemos que editar su archivo de configuración en App/config/vendors/ivory_ck_editor.yml.

De estar usando CKEditor de forma manual tendrás que usar la configuración del editor para activar el plugin.

Editamos nuestro ivory_ck_editor.yml y lo primero que tenemos que hacer aquí es añadir nuestro plugin en extraPlugins

embed: <script src="https://gist.github.com/anonymous/cfafd8c16ce8f814e3b20567f2ebf201.js"></script>

extraPlugins: “hola-mundo”

Después vamos a añadirlo al toolbar, en la posición en la que queremos que aparezca dentro de la barra de herramientas:

embed: <script src="https://gist.github.com/anonymous/caf52bf2ad7ae53ad146de3670937c7e.js"></script>

            toolbar:

                - [Bold, Italic, Underline]

                - [Undo, Redo]

                - [Link, Unlink, Anchor]

                - [NumberedList, BulletedList, Blockquote]

                - [Table]

                - [TextColor]

                - [Image]

                - [Source]

                - [Maximize]

                - [hola-mundo]

Y finalmente le indicamos la ruta al javascript que contiene el plugin:

embed: <script src="https://gist.github.com/anonymous/2ad930f001dc4a6e00955e81d7e9affb.js"></script>

    plugins:

        hola-mundo: 

            path: "/ckeditor/plugins/hola-mundo/”

            filename: “plugin.js"

¡El plugin ya está activado!

Al cargar el editor deberíamos ver nuestro nuevo botón, y al pulsarlo veremos que se añade el texto ‘Hola Mundo!’ en el contenido.

Con este ejemplo ya hemos creado un plugin sencillo con el que podemos manipular el contenido del editor, a partir de aquí podemos modificarlo para adaptarlo a las necesidades que se nos presenten.

Ventanas de diálogo.

Siguiendo con nuestro ejemplo de ‘Hola Mundo!’, vamos a continuar con algunos cambios para que el editor nos pida el nombre de la persona a la que queremos saludar. Lo que queremos conseguir es que al pulsar el botón se abra un diálogo con un input donde escribir el nombre, este nombre se usará después para insertar en el contenido un saludo personalizado del tipo “Hola Runroom!”.

 

Lo primero que vamos a hacer es crear la ventana de diálogo, para ello necesitamos un nuevo archivo javascript que contendrá todo el código necesario para gestionar tanto la ventana como los datos del formulario que nos presenta.

Este archivo lo crearemos en una nueva carpeta en dialogs/ y le llamaremos hola-mundo-dialog.js.

Así, la estructura de directorio de nuestro plugin quedaría de esta forma:

El código que vamos a escribir en este nuevo archivo se encarga de configurar la ventana y de establecer los contenidos que se van a mostrar en ella.

embed: <script src="https://gist.github.com/anonymous/29a4a07fa7a06205c5fd2831c9220aaa.js"></script>

CKEDITOR.dialog.add(‘hola-mundo-dialog', function( editor ) {

    return {

        title: ‘Hola Mundo',

        minWidth: 400,

        minHeight: 100,

        contents: [

            {

                id: 'tab-basic',

                label: 'Basic Settings',

                elements: [

                    // elementos UI

                ]

            }

        ]

    };

});

Con dialog.add creamos la nueva ventana de diálogo pasándole como primer parámetro el nombre de la ventana y como segundo parámetro una función que debe devolver un objeto javascript dónde le indicamos el título y las dimensiones.

Además, en contents le especificamos los contenidos de esta ventana.

En nuestro caso solo añadimos un objeto con los elementos que necesitamos, de añadir más objetos la ventana constaría con diferentes pestañas.

Esta funcionalidad podría usarse para crear una pestaña de configuración básica junto con otra de configuración avanzada.

En cualquier caso, para nuestro plugin tenemos suficiente con una.

En este momento nuestro diálogo constaría tan sólo de una ventana vacía. Vamos a añadir el formulario para solicitar el nombre al usuario, para ello le indicamos a CKEditor los elementos que deben aparecer en el diálogo:

embed: <script src="https://gist.github.com/anonymous/43c6986099e900a89ed4eb35f63e7c6d.js"></script>

                elements: [

                    {

                        type: 'text',

                        id: 'name',

                        label: 'Nombre',

                        validate: CKEDITOR.dialog.validate.notEmpty( "This field cannot be empty." )

                    },

 

En este caso lo que hemos hecho es añadir un input de texto con id name, label Nombre y que se validará para que no esté vacío.

Llegados este punto vamos a crear el método onOk que se ejecutará cuando el usuario pulse el botón aceptar en el diálogo.

Lo que haremos primero es recoger el valor que el usuario haya escrito en el input y usarlo para crear nuestro texto personalizado.

embed: <script src="https://gist.github.com/anonymous/e7d947d998b186f54fd0fc436a0e7179.js"></script>

        onOk: function() {

            var name = this.getValueOf('tab-basic', 'name');

 

            editor.insertText('Hola ' + name + '!');

        }

El código completo de este diálogo quedaría así:      

embed: <script src="https://gist.github.com/anonymous/bd881f95b04817e99e74a71d37e6c0f9.js"></script>

CKEDITOR.dialog.add('hola-mundo-dialog', function(editor) {

    return {

        title: 'Hola Mundo',

        minWidth: 400,

        minHeight: 100,

        contents: [

            {

                id: 'tab-basic',

                label: 'Basic Settings',

                elements: [

                    {

                        type: 'text',

                        id: 'name',

                        label: 'Nombre',

                        validate: CKEDITOR.dialog.validate.notEmpty( "This field cannot be empty." )

                    },

                ]

            }

        ],

        onOk: function() {

            var name = this.getValueOf('tab-basic', 'name');

 

            editor.insertText('Hola ' + name + '!');

        }

    };

});

Aún nos queda una última modificación para poder ver nuestro diálogo en acción.

En el archivo del plugin, en plugin.js, vamos a incluir el diálogo y a hacer que se ejecute cuando pulsamos el botón de nuestro plugin.

embed: <script src="https://gist.github.com/anonymous/08920b2c67d7008e6483e1e62bd5ec26.js"></script>

        CKEDITOR.dialog.add('hola-mundo-dialog', this.path + 'dialogs/hola-mundo-dialog.js');

        editor.addCommand('hola-mundo', new CKEDITOR.dialogCommand('hola-mundo-dialog'));

Con esto ya deberíamos poder usar nuestra nueva ventana de diálogo, ahora al pulsar nuestro botón, el editor nos pregunta por nuestro nombre y, al aceptar, automáticamente se añadirá en el editor un saludo personalizado.

Abrir el diálogo en modo edición.

Ahora ya tenemos nuestro plugin y podemos añadir saludos personalizados a nuestro contenido, pero ¿qué pasa si queremos editar uno que ya hemos insertado?

A continuación vamos a ver cómo podemos abrir la ventana de diálogo en modo edición para modificar el contenido que insertemos con nuestro plugin.

Para esto tendremos que hacer algunos cambios en nuestra ventana de diálogo, primero necesitaremos identificar si el elemento en cuestión lo hemos añadido con nuestro plugin, si es así, tendremos que recuperar el nombre del usuario y mostrarlo en el input del diálogo para después actualizar el contenido.

Para todo esto usaremos el método onShow junto con las funciones de setup y commit que CKEditor pone a nuestra disposición.

Empezaremos editando el archivo hola-mundo-dialog.js:

embed: <script src="https://gist.github.com/anonymous/beeb620bb0999e5744bc3e422dd41abe.js"></script>

        onShow: function() {

            var selection = editor.getSelection();

            var element = selection.getStartElement();

            var pluginName = 'hola-mundo';

            var dataAttribute = 'data-plugin';

 

            if (!element || element.getAttribute(dataAttribute) != pluginName) {

                this.insertMode = true;

                element = editor.document.createElement('p');

                element.setAttribute(dataAttribute, pluginName);    

            }

 

            this.element = element;

        }

 

Con getSelection obtenemos la selección del usuario, ya sea porque ha seleccionado parte del contenido o porque simplemente tienen el cursor situado en algún elemento del contenido. Con getStartElement obtenemos el elemento donde empieza la selección.

Después miramos si tenemos algún elemento seleccionado y si éste tiene el atributo que lo identifica como uno de nuestros elementos.

En el caso de estar creando un nuevo saludo estas condiciones no se cumplen, así que procedemos a crear un nuevo elemento al que le ponemos el atributo identificativo.

Además, dejamos la variable insertMode a true para más adelante saber que estamos insertando un elemento nuevo.

Ahora vamos a modificar el método onOk que hemos añadido anteriormente para que use las funciones de commit:

embed: <script src="https://gist.github.com/anonymous/384fcd91e2493c48bb7c50fdf0405223.js"></script>

    onOk: function() {

            var name = this.getValueOf('tab-basic', 'name');

 

            this.commitContent(this.element);

 

            if (this.insertMode) {

                editor.insertElement(this.element);

            }

        }

 

Lo que hacemos aquí es obtener el valor del input y llamar a commitContent, que ejecutará las funciones commit de todos los elementos, después, si estamos en modo inserción, añadimos el elemento al contenido. Así que ahora lo que nos falta es crear la función commit en nuestro elemento.

Para que al editar podamos recuperar de forma sencilla el nombre del usuario vamos a guardarlo en un atributo, esto lo haremos con element.setAttribute, después establecemos el HTML del elemento con nuestro saludo personalizado.

embed: <script src="https://gist.github.com/anonymous/d2eb6969f9e7b4238cc695555d8cb03b.js"></script>

                        commit: function (element) {

                            element.setAttribute('data-name', this.getValue());

                            element.setHtml('Hola ' + this.getValue() + '!');

                        }

En este punto nuestro plugin ya es capaz de editar el contenido, aunque el input del diálogo siempre aparece en blanco en lugar de mostrarnos el nombre del usuario.

Veamos como solucionar esto.

Volvemos a nuestro método onShow y lo cambiamos para que llame a setupContent si estamos en modo edición.

Esta llamada ejecutará las funciones setup de todos los elementos.

embed: <script src="https://gist.github.com/anonymous/73c77df560f9ccf56603d299ccd69ef8.js"></script>

        onShow: function() {

            var selection = editor.getSelection();

            var element = selection.getStartElement();

            var pluginName = 'hola-mundo';

            var dataAttribute = 'data-plugin';

 

            if (!element || element.getAttribute(dataAttribute) != pluginName) {

                this.insertMode = true;

                element = editor.document.createElement('p');

                element.setAttribute(dataAttribute, pluginName);    

            } else {

                this.insertMode = false;

                this.setupContent(element);

            }

 

            this.element = element;

        }

Ya solo nos queda crear la función setup en nuestro elemento, en esta función necesitamos decirle al elemento que valor debe usar en el input.

Como hemos guardado el nombre de nuestro usuario en un atributo podremos recuperarlo sin muchas complicaciones.

embed: <script src="https://gist.github.com/anonymous/d4a0b8556406c219476bf07505775d84.js"></script>

 

                        setup: function(element) {

                            this.setValue(element.getAttribute('data-name'));

                        },

 

Ahora cuando editemos el contenido deberíamos ver el nombre del usuario en el input, gracias a la función setup. Si lo cambiamos y aceptamos el diálogo, nuestro plugin llamará a las funciones commit desde el método onOk, por lo que se actualizará el contenido.

El código completo de nuestra ventana de diálogo quedaría así:

 

embed: <script src="https://gist.github.com/anonymous/f501394281085c701491f64111bac1fd.js"></script>

 

CKEDITOR.dialog.add('hola-mundo-dialog', function(editor) {

    return {

        title: 'Hola Mundo',

        minWidth: 400,

        minHeight: 100,

        insertMode: false,

        contents: [

            {

                id: 'tab-basic',

                label: 'Basic Settings',

                elements: [

                    {

                        type: 'text',

                        id: 'name',

                        label: 'Nombre',

                        validate: CKEDITOR.dialog.validate.notEmpty( "This field cannot be empty." ),

                        setup: function( element ) {

                            this.setValue( element.getAttribute('data-name') );

                        },

                        commit: function ( element ) {

                            element.setAttribute('data-name', this.getValue());

                            element.setHtml('Hola ' + this.getValue() + '!');

                        }

                    },

                ]

            }

        ],

        onShow: function() {

            var selection = editor.getSelection();

            var element = selection.getStartElement();

            var pluginName = 'hola-mundo';

            var dataAttribute = 'data-plugin';

 

            if (!element || element.getAttribute(dataAttribute) != pluginName) {

                this.insertMode = true;

                element = editor.document.createElement('p');

                element.setAttribute(dataAttribute, pluginName);    

            } else {

                this.insertMode = false;

                this.setupContent(element);

            }

 

            this.element = element;

        },

        onOk: function() {

            var name = this.getValueOf('tab-basic', 'name');

 

            this.commitContent(this.element);

 

            if (this.insertMode) {

                editor.insertElement(this.element);

            }

        }

    };

});

 

A pesar de que nuestro plugin es muy sencillo, nos permite ver mejor el funcionamiento de los plugins de CKEditor y apreciar las posibilidades que nos ofrecen.

Entendiendo que no debe ser necesario saber HTML para editar el contenido de una web, podemos crear plugins para facilitar la edición y permitir a nuestros clientes crear elementos complejos, con clases o atributos específicos, insertar micro-formatos, widgets o webcomponents con tan solo pulsar un botón.  

Espero que esta información facilite tu trabajo.

 

11 Oct. 2017

Jordi Sala

Head of Development

Agile Digital Intelligence