Cómo incluir un botón personalizado para nuestro CMS en la barra de menú de TinyMCE.
0. Índice de contenidos.
- 1. Introducción.
- 2. Entorno.
- 3. Configuración.
- 4. Personalización de un botón de menú.
- 5. Inclusión de un botón de búsqueda de contenido multimedia.
- 5. Referencias.
- 6. Conclusiones.
1. Introducción
TinyMCE es un editor wysiwyg desarrollado en JavaScript que nos permite, como muchos otros, convetir etiquetas de texto HTML en componentes de texto enriquecido. No es objetivo de este tutorial llevar a cabo una comparativa entre los distintos editores wysiwyg open source que podemos encontrar (CKEditor ,
NicEdit ,
YUI Library Rich Text Editor
Dijit.Editor ,
Cross-Browser Rich Text Editor (RTE),… ), el objetivo es que, una vez hemos elegido uno, como TinyMCE, vamos a ver las posibilidades que tenemos de incluir un botón personalizado en la barra de menú para resolver un formato de edición de texto propio de nuestra aplicación.
Al igual que TinyMCE se puede usar en aplicaciones como WordPress, nosotros vamos a hacer uso del mismo dentro de nuestro propio CMS, para permitir al usuario final insertar dentro de un campo de texto enriquerido un contenido de tipo rich media (imágenes, video, podcast,…) localizado dentro del sistema. La historia de usuario sería la siguiente «Como editor de una noticia quiero incluir dentro del texto de misma contenido multimedia, indexado en la aplicación, directamente desde el editor de texto enriquecido para que la edición de la noticia sea ágil».
En este tutorial vamos a ver primero como incluir el editor de texto enriquecido, como personalizarlo y después vamos a intentar cubrir nuestra historia de usuario.
2. Entorno.
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 17′ (2.93 GHz Intel Core 2 Duo, 8GB DDR3 SDRAM).
- Sistema Operativo: Mac OS X Snow Leopard 10.6.1
- TinyMCE 3.4.6
3. Configuración.
Lo primero que haremos será descargar la última versión estable desde la página de descarga de TinyMCE y, una vez descomprimido el paquete lo incluiremos accesible a nuestro contexto de aplicación, de modo que quede bajo nuestro árbol de directorios como sigue:
El directorio img no es estrictamente necesario, lo hemos usado para incluir imágenes de personalización del editor y, en realidad el contenido de los directorios langs, plugins y themes dependerá del uso que le vayamos a dar al editor (los idiomas que soporte, los plugins que pogamos disponibles al usuario y los temas visuales que usemos para la interfaz).
Una vez hecho esto ya podemos hacer uso del script que carga la librería e incluir en cualquiera de nuestras páginas una función de carga del componente visual que convierta un textarea en un editor, un componente de texto enriquecido con las opciones que necesitemos.
Un ejemplo de uso sería el siguiente:
<html> <head> <title>Hello tinymce! <script type="text/javascript" src="/wp-content/uploads/tutorial-data/tiny_mce/tiny_mce.js"></script> <script type="text/javascript"> tinyMCE.init({ mode : "exact", elements: "contenido", theme : "advanced", theme_advanced_buttons1 : "selectContent,code,bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright, justifyfull,bullist,numlist,undo,redo,link,unlink", theme_advanced_buttons2 : "", theme_advanced_buttons3 : "", theme_advanced_toolbar_location : "top", theme_advanced_toolbar_align : "left", theme_advanced_statusbar_location : "bottom" }); </script> </head> <body> <h2>Hello tinymce! <form method="post" > <textarea name="contenido" ></textarea> </form> </body> </html>
Convirtiendo el textarea en un componente visual como el que sigue:
Interactúa si quieres, no es una imagen 😉
4. Personalización de un botón de menú.
En la propiedad theme_advanced_buttons1 de carga del componente visual se especifican los botones a incluir en las barras de menú. Para incluir un botón personalizado, bastaría con incluir un item más con un nombre propio y configurar el editor para que añada de forma dinámica un comportamiento al mismo.
Para ello, podríamos modificar el script de carga del editor como sigue:
tinyMCE.init({ mode : "exact", elements: "contenido", theme : "advanced", theme_advanced_buttons1 : "miBoton,code,bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright, justifyfull,bullist,numlist,undo,redo,link,unlink", theme_advanced_buttons2 : "", theme_advanced_buttons3 : "", theme_advanced_toolbar_location : "top", theme_advanced_toolbar_align : "left", theme_advanced_statusbar_location : "bottom", setup : function(ed) { ed.addButton('miBoton', { title : 'Un botón personalizado', image : '/tiny_mce/img/miBoton.png', onclick : function() { ed.focus(); ed.selection.setContent('Hola mundo! '); } }); } });
El resultado sería el siguiente:
Pulsando sobre el primer botón ahora incluye un texto de «Hola Mundo!».
5. Inclusión de un botón de búsqueda de contenido multimedia.
Una vez que sabemos incluir el editor y añadir un botón con comportamiento vamos a intentar cumplir los objetivos de nuestra historia de usuario.
Recordemos que tenemos un cms desde el que se pueden catalogar imágenes, videos, podcasts,… y también tenemos noticias, desde cuya edición el usuario quiere seleccionar contenido multimedia para añadirlo al contenido textual de las noticias.
5.1. Elección de las etiquetas personales para el contenido.
Lo primero que deberíamos hacer es pensar quién va a tratar el contenido de las noticias y que formato vamos a soportar.
Incluyendo un editor de texto enriquecido corremos el peligro de que el contenido que se genera no sea muy limpio en cuanto a HTML se refiere o no se genere código XHTML bien formado. Con tinyMCE, por defecto, estamos cubiertos en ese sentido, puesto que genera xhtml strict. Necesitaremos de una configuración «ad hoc» para soportar otras etiquetas fuera del estándar XHTML.
Desde el otro lado, también debemos pensar quién va a ser el encargado de parsear el contenido de la noticia para convertir las etiquetas personalizadas que insertemos en contenido extraido del CMS.
La recomendación es hacer uso de un espacio de nombres propio en XML, de modo que nuestras etiquetas sean conceptualmente fácilmente identificables y, a la hora de tratarlas en la vista, podamos usar algún tipo de tecnología que las convierta en un contenido real traducido a XHTML, como podríamos hacer en JSF con componentes por composición o componentes nativos.
Podríamos tener el siguiente catálogo de etiquetas:
- imágenes: <tnt:image id=»108″ width=»50px» height=»50px»/>
- videos: <tnt:media id=»115″ autoplay=»true»/>
- imágenes: <tnt:podcast id=»40″ />
Los atributos que podrían soportar habría que estudiarlos también pero, a priori, lo que sí haríamos es incluir una referencia al identificador único del contenido a mostrar para que sea la tecnología de la vista que lo presente la encargada que traducir ese 108, 115 o 40 en un path hacía la correspondiente imagen, video o podcast.
5.2. Personalización del botón de búsqueda.
Una vez tenemos claro el contenido personalizado que queremos insertar lo siguiente sería añadir una opción de menú que permitiera mostrar un componente de búsqueda sobre el contenido multimedia a insertar en el texto de las noticias.
Vamos a suponer que queremos mostrar una ventana emergente desde la cual se podrán realizar búsquedas y seleccionar el contenido a insertar.
Para ello, modificaremos el script de carga como sigue:
var selectContentWindow; tinyMCE.init({ mode : "exact", elements: "contenido", theme : "advanced", theme_advanced_buttons1 : "selectContent,code,bold,italic,underline,separator,strikethrough,justifyleft,justifycenter,justifyright, justifyfull,bullist,numlist,undo,redo,link,unlink", theme_advanced_buttons2 : "", theme_advanced_buttons3 : "", theme_advanced_toolbar_location : "top", theme_advanced_toolbar_align : "left", theme_advanced_statusbar_location : "bottom", extended_valid_elements : 'tnt:imagen[id],tnt:podcast[id]', custom_elements : 'tnt:imagen,tnt:podcast', setup : function(ed) { // Add a custom button ed.addButton('selectContent', { title : 'Insertar contenido multimedia', image : '/tiny_mce/img/select_content.png', onclick : function() { if (selectContentWindow && selectContentWindow.open && !selectContentWindow.closed) { selectContentWindow.close(); } selectContentWindow = window.open('/content/search.jsp','selectContentWindow','width=800,height=500,left=100,top=200,scrollbars=yes,help=no,resizable=yes,status=no'); } }); } }); function selectContentCallBack(content){ var editor = tinymce.EditorManager.get("contenido"); editor.focus(); editor.selection.setContent(content); }
Añadimos el comportamiento de apertura de una ventana emergente que cargará una página de búsqueda, cuyo contenido queda fuera del ámbito de este tutorial, y dejamos preparada una función que permita desde la misma ventana emergente insertar contenido en el editor.
Para que el editor soporte la inclusión de las etiquetas personalizadas debemos incluir un catálogo de las mismas y sus atributos en los parámetros extended_valid_elements y custom_elements. Hay una segunda alternativa que permite la configuración del editor para que soporte cualquier tipo de etiquetado, configurando la siguiente propiedad valid_elements: «*[*]», si bien, si permitimos la edición del contenido HTML directamente por el usuario puede ser causa de problemas de formato.
Desde la ventana de búsqueda de contenidos podríamos insertar las etiquetas invocando a la función javascript de callback de la siguiente forma:.
function selectImage(imageId){ window.opener.selectContentCallBack("<tnt:image id=\""+imageId+"\" />"); window.close(); }
Hasta aquí tendríamos la inclusión de nuestro etiquetado funcionando correctamente pero tinyMCE no respetaría nuestro tag como una etiqueta autocontenida, puesto que no forma parte del catálogo que tiene definido por defecto. Lo que nos pasaría es que al insertar <tnt:imagen id=»34″ /> en el editor, visalizando el código fuente, lo veríamos como <tnt:imagen id=»34″ ></tnt:imagen> y así se almacenaría.
No he encontrado un parámetro de inicio del editor en el que se pueda configurar un catálogo distinto de etiquetas autocontenidas, con lo que, revisando la documentación, la única vía que he visto para solventarlo es añadir de forma dinámica un item más a la colección que está predefinida en el esquema interno del editor.
... editor.schema.getShortEndedElements()["tnt:imagen"] = {}; ...
Se puede configurar una única vez en la carga del editor y, con ello, sí quedaría totalmente configurado para soportar nuestras etiquetas.
5.3. Añadiendo estilos a los contenidos dentro del editor.
Hasta aquí todo funcionando, si bien, el editor no mostrará contenido alguno puesto que nuestro etiquetado personalizado no tiene una representación visual en XHTML. Para incluir algún tipo de estilo visual a nuestras etiquetas podríamos hacer uso de CSS añadiendo el siguiente parámetro a la configuración de inicio del editor:
... content_css : "/tiny_mce/style/cms.css", ...
El contenido de la CSS podría ser algo como esto:
tnt0003Aimagen{ width:50px; height:50px; border:1px solid #000; }
Los dos puntos no son un caracter válido para definir los selectores en CSS con lo que debemos escaparlos en hexadecimal: 0003A.
Con ello, ahora sí, tendríamos nuestro editor de contenidos multimedia funcionando.
5.4. Demo.
A continuación un ejemplo de uso, con todo integrado y con el botón del menú de ver el código fuente (HTML) para compronbar la inclusión del etiquetado del CMS:
Sería conveniente que los estilos de las etiquetas del cms incluyesen una imagen de fondo para distinguir entre los distintos tipos de contenidos.
6. Referencias.
- http://www.tinymce.com/tryit/custom_toolbar_button.php
- http://tinymce.moxiecode.com/js/tinymce/docs/api/index.html
- http://www.tinymce.com/wiki.php/Configuration:custom_elements
7. Conclusiones.
No hay nada como leer la documentación y sumergirse en el API 😉
Ahora solo nos faltaría componentizarlo para que pudieramos usar nuestro editor personalizado en cualquier página de edición de nuestro CMS.
Stay tuned!.
Un saludo.
Jose
Saludos, disculpa una pregunta, he estado tratando de hacer un boton que me llame una funcion javascript, el problema esta en que cuando pongo el evento en el boton onclick para llamar a la funcion lo borra autamaticamente del articulo, me podrias dar una idea de lo que estoy haciendo mal. Saludos