Creación de shortcodes para que los use “el Cliente”

natalia shortcodes

Hace unas semanas me liaron para dar una charla así que elegí un tema con el que me había estado peleando unos días antes: Creación de shortcodes para que lo use “El Cliente”.

Lo primero es lo primero, ¿qué son los shortcodes?

Básicamente son “llamadores” de funciones que se utilizan en el contenido de nuestras páginas y entradas. Para eso utilizamos los corchetes como indicadores de que lo que hay dentro es un shortcode:  [soyunshortcode]

Hay varios tipos de shortcodes: simples, con atributos y con contenido.

La creación será básicamente lo mismo (con pequeñas diferencias) en nuestro functions.php mediante la función add_shortcode. Con esta función lo que vamos a hacer va ser definir el nombre del shortcode (lo que irá entre los corchetes) y la función que WP ejecutará cuando se lo encuentre.

En el caso de un shortcode simple, nuestra función no necesitará de ningún parámetro de entrada, y lo que nos devuelva será lo que se “pintará” en nuestra entrada. Mientras que si queremos añadir parámetros, tendremos que especificarlos como atributos de nuestro shrotcode. En nuestro caso, nuestros shortcodes serán [elshortcode] y [conatributos] respectivamente:

add_shortcode('elshortcode', 'elshortcode_shortcode_function');

function elshortcode_shortcode_function() {
   $salida = '<div style="background: red;">Hola que tal…</div>';

   return $salida;
}
add_shortcode('constaributos', 'conatributos_shortcode_function');

function conatributos_shortcode_function($atts, $content = null) {
   extract( shortcode_atts(array('titulo' => '',), $atts ));

   $salida = '<div>';
   if($titulo)
      $salida .= '<h2>'.$titulo.'</h2>';

   $salida .= $content.'</div>';

   return $salida;
}

La función shortcode_atts() es similar a array_merge(): fusiona la segunda lista de argumentos en la primera. La diferencia es que se fusiona sólo las claves presentes en el primer argumento. Extract () toma las claves del array, las define como nombres de variables y sus valores como valores de variable.

Los shortcodes con contenido tienen “cierre” como en las etiquetas HTML
Si queremos que el contenido ejecute posibles shortcodes, lo hacemos con la función do_shortcode($content). En este caso sería de la siguiente forma:

[concontenido]Mi contenido dentro del shortcode[/concontenido]

add_shortcode('concontenido', 'concontenido_shortcode_function');

function concontenido_shortcode_function($atts, $content = null) {
   extract( shortcode_atts(array('titulo' => '',), $atts ));

   $salida = '<div>';
   if($titulo)
      $salida .= '<h2>'.$titulo.'</h2>';

   $salida .= do_shortcode($content).'</div>';

   return $salida;
}

Ahora que ya sabemos lo que son, y como crear los shortcodes, vamos a ver un poquito en profundidad el editor TinyMCE de WordPress, que es el editor WYSIWYG (What you see is what you get) que viene por defecto.

El editor TinyMCE es un plugin que puede usarse fuera de WordPress, que tenemos integrado en el framework. Gracias a este plugin, podemos poner negritas, cambiar alineaciones, etc.

Por defecto, se han eliminado algunos botones del editor.
Si queremos que aparezcan en la primera línea, utilizamos el filtro mce_buttons o ponemos añadir una tercera línea con mce_buttons_3. El máximo de líneas para el TinyMCE en WordPress es de 4
Para el orden podemos utilizar array_unshift, para añadirlos al inicio, array_push para ponerlos al final o incluso usar array_splice.

En nuestro ejemplo vamos a añadir los botones en la segunda línea:

add_filter('mce_buttons_2', 'botones_extra_editor');

function botones_extra_editor( $buttons ) {
   array_unshift( $buttons, 'styleselect' );
   array_unshift( $buttons, 'fontsizeselect' );
   array_push($buttons, 'fontselect');

   return $buttons;
}

Otros botones que se podrían añadir: backcolor, newdocument, charmap, hr, etc… Se puede encontrar más información en la documentación de TinyMCE.

Si quisiéramos eliminar botones utilizaríamos el mismo filtro pero hacemos unset($buttons[3]), siendo3 la posición en la que aparece el botón en el editor que queremos eliminar.

Además de los botones, también podemos añadir estilos personalizados a nuestro editor, como se explica en el Codex. Sería algo así:

add_filter('tiny_mce_before_init', 'mi_mce_mod');

function mi_mce_mod( $init ) {

   $style_formats = array (
     array( 'title' => __('H1 Page Title', 'understrap'), 'block' => 'h1', 'classes' => 'page-title' ),
     array( 'title' => __('Color button', 'understrap'), 'inline' => 'a', 'classes' => 'colorbutton' ),
   );

   $init['style_formats'] = json_encode( $style_formats );

   $init['style_formats_merge'] = true;

   return $init;

}

Le añadimos una función callback a la inicialización del editor.
La opción block, sustituye los bloques del elemento seleccionado mientras que inline, lo añade. Las classes se ponen separadas por comas.

Si ponemos $init[‘style_formats_merge’] a false sólo mostrará los estilos que hemos añadido aquí.

Y después de esta introducción, ya podemos meterno de lleno en la creación de nuestros botones personalizados para el TinyMCE, para ello usamos el hook admin_head y la función par ello sería:

add_action('admin_head', 'mis_botones');

function mis_botones() {
   if ( !current_user_can( 'edit_posts' ) && !current_user_can( 'edit_pages' ) ) {
     return;
   }

   if ( get_user_option( 'rich_editing' ) !== 'true' ) {
     return;
   }

   add_filter( 'mce_external_plugins', 'nuevos_botones' );
   add_filter( 'mce_buttons', 'registrar_botones' );
}

En esta función lo que hacemos es comprobar los permisos del usuario y la configuración. Si se cumplen las condiciones, añadimos los dos filtros:

function nuevos_botones( $plugin_array ) {
   $plugin_array['miboton'] = get_template_directory_uri().'/js/tinymce_buttons.js';
   $plugin_array['miboton2'] = get_template_directory_uri().'/js/tinymce_buttons.js';
   return $plugin_array;
}

function registrar_botones( $buttons ) {
   array_push( $buttons, 'miboton', 'miboton2');
   return $buttons;
}

El primero de los filtros lo utilizamos para especificar la localización del script con nuestro plugin, mientras que el segundo se utiliza para añadir nuestro botón al editor.

El código para crear y definir las acciones de nuestros botones se encuentra en archivo tinymce_buttons.js así que vamos a ver cómo se crearía un botón sencillo:

(function() {
   tinymce.PluginManager.add('miboton', function( editor, url ) {
     editor.addButton( 'miboton', {
       text: 'Mi botón',
       icon: false,
       onclick: function() {
         editor.insertContent('Qué pasa tronquitos!!');
       }
     });
   });
})();

En este caso, lo que mostrará el editor será un botón con una etiqueta de texto, definido en la propiedad “text”. Si lo que queremos es mostrar un icono, sustituimos “text” por “title” y a “icon” le damos el valor que queramos para nuestro icono, que en este caso serán los dashicons de WP. Así si le damos el valor “wp_code” nuestro botón se verá así:  

El texto que hemos definido en “title” aparecerá como tooltip del botón. También podemos empezar a trabajar con los shortcodes, básicamente añadiéndolo al texto que insertamos como contenido.

(function() {
   tinymce.PluginManager.add('miboton', function( editor, url ) {
     editor.addButton( 'miboton', {
       title: 'Mi botón',
       icon:"wp_code",
       onclick: function() {
         editor.insertContent('[elshortcode]');
       }
     });
   });
})();

Hay otras formas de customizar el icono de nuestro botón, podemos utilizar el atributo image o añadirle otras clases (que es el valor de “icon”). Así, si le añadimos el valor “icon mi-icono” y cargamos nuestros estilos en la parte de administración podríamos personalizarlo totalmente:

add_action('admin_enqueue_scripts', 'extra_mce_css');

function extra_mce_css() {

   wp_enqueue_style('extra_mce', get_template_directory_uri().'/css/mimce.css');

}

Y en nuestro CSS le damos los estilos necesarios al botón:

i.mce-i-icon {

    font: 400 20px/1 dashicons;

    padding: 0;

    vertical-align: top;

    speak: none;

    -webkit-font-smoothing: antialiased;

    -moz-osx-font-smoothing: grayscale;

    margin-left: -2px;

    padding-right: 2px

}

i.mi-icono {

    background-image: url('mi-icono-customizado.png');

}

Ahora bien, para poder utilizar nuestros botones y nuestros shortcodes de la forma más óptima vamos a crear un popup de opciones para que el usuario pueda añadirle todos los valores necesarios a nuestro shortcode sin tener que escribirlos, simplemente dándole valores a las opciones que se le muestren en el popup.

(function() {
  tinymce.PluginManager.add('miboton2', function( editor, url ) {
    editor.addButton( 'miboton2', {
      title: 'Mi botón 2',
      icon: 'bubble',
      onclick: function() {
        editor.windowManager.open( {
          title: 'Título del shortcode',
          body: [{
            type: 'textbox',
            name: 'titulo',
            label: 'Atributo titulo'
          }],
          onsubmit: function( e ) {
            editor.insertContent( '[conatributo titulo="' +e.data.titulo+ '"]');
          }
        });
      }
    });
  });
})();

Este código nos mostraría un popup sencillo con un único campo de texto, que se corresponde con uno de los tributos del shortcode y que el usuario rellenará. Así, si tenemos shortcodes mucho más complejos, especificar el valor de estos atributos se realiza de forma rápida y sencilla para el usuario.

minipopup

A continuación os muestro lo que sería un botón más complicado, con muchas más opciones en el popup y la posibilidad de añadir el contenido seleccionado.

(function() {

  tinymce.PluginManager.add('miboton', function( editor, url ) {

    editor.addButton( 'miboton', {

      title: 'Shortcode con atributos',

      icon: 'icon mi-icono-customizado',

      onclick: function (){

            var win = editor.windowManager.open( {

              title: 'Opciones del shortcode',

              body: [

                {

                  type: 'textbox',

                  name: 'imagen',

                  label: 'Imagen',

                  value: '',

                  classes: 'imgblock_input_image',

                },

                {

                  type: 'button',

                  name: 'imgblock_upload_button',

                  label: '',

                  text: 'Subir imagen',

                  classes: 'imgblock_upload_button',

                },

                {

                  type: 'textbox',

                  name: 'multilinea',

                  label: 'Pseudo textarea',

                  value: 'Aquí puedes poner un montón de texto',

                  multiline: true,

                  minWidth: 300,

                  minHeight: 100

                },

                {

                  type: 'colorpicker',

                  label: "Color de fondo",

                  name: "bgcolorpicker",                                   

                  onchange: function(e) {                  

                    win.find('#fondo').value(this._color.toHex());                                      

                  }

                },

                {

                  type: 'textbox',                   

                  name: 'fondo',

                  label: ' ',                  

                  size: 6,

                  maxLength: 6,

                  tooltip: 'Color de fondo en Hexadecimal',

                },

                {

                  type   : 'checkbox',

                  name   : 'miniatura',

                  label  : 'Usar miniatura de la imagen',

                  text   : 'Miniatura',

                  checked : false

                },                

                {

                  type: 'listbox',

                  name: 'posicion',

                  label: 'Posicion de la imagen',

                  'values': [

                    {text: 'Izquierda', value: 'left'},

                    {text: 'Centrada', value: 'center'},

                    {text: 'Derecha', value: 'right'},                    

                  ],

                  value : 'left'

                }

              ],

              onsubmit: function( e ) {                

                var selectedText = tinymce.activeEditor.selection.getContent({format : "html"});

                editor.insertContent( '[imgblock imagen=' + e.data.imagen + ' miniatura="' + e.data.miniatura + '"  posicion="' + e.data.posicion + '" fondo="'+e.data.fondo+'"]'+selectedText+'[/imgblock]');

              }

            });

          }

    });

  });

})();

En el código anterior lo que hemos hecho ha sido añadir muchas más opciones a nuestro popup, de las cuales hay dos que merecen especial consideración ya que requieren un pequeño trabajo extra para que funcionen según nuestras necesidades.

popup

Para el caso de la imagen, lo que hemos hecho ha sido utilizar dos elementos propios del TinyMCE, el textbox llamado “imagen” y el button “imgblock_upload_button”. Por si solos estos dos elementos no nos proporcionarán la posibilidad de utilizar el uploader propio de WordPress, para ello vamos a añadir un poco más de código javascript con lo que podremos obtener, bien la URL de la imagen o bien su ID (según nuestras necesidades):

jQuery(document).ready(function($){

  $(document).on('click', '.mce-imgblock_upload_button', upload_image_tinymce);

  function upload_image_tinymce(e) {

    e.preventDefault();

    var $input_field = $('.mce-imgblock_input_image');

    var custom_uploader =  wp.media({

      title: 'Añadir Imagen',

      button: {

          text: 'Añadir Imagen'

      },

      multiple: false

    });

    custom_uploader.on('select', function() {

      var attachment = custom_uploader.state().get('selection').first().toJSON();

      $input_field.val(attachment.id);

    });

    custom_uploader.open();

  }

});

Con el javascript que hemos añadido lo que hacemos es invocar al uploader propio de WP al hacer click en el botón de subir imagen, una vez seleccionada la imagen lo que le devolveremos a nuestro campo de texto será el id de la imagen subida.

El otro caso es nuestro colorpicker, TinyMCE nos proporciona un colorpicker totalmente funcional para que el usuario pueda seleccionar un color, pero en ningún momento puede ver de qué color se trata, o tiene opción de introducir ese dato manualmente. Por eso en este caso también utilizamos dos campos para realizar el trabajo: “bgcolorpicker” para seleccionar el color y “fondo”, el campo de texto en el que lo mostraremos y podremos modificar. En este caso vamos a utilizar un poco de código extra, ya que al asignarle todo el popup a una variable (win) podremos ver qué valor se ha seleccionado en el colorpicker mediante un evento onchange asociado al mismo. Luego lo único que hacemos es pasar ese valor a Hexadecimal y lo añadimos al campo de texto para que el usuario pueda verlo.

Para poder añadir el texto que se había seleccionado al hacer click en el botón, y poder añadir un shortcode que además de atributos utiliza el contenido hemos utilizado el método de TinyMCE:

tinymce.activeEditor.selection.getContent({format : "text"});

Si en lugar de format html utilizamos “text” obtendríamos el texto plano.

Como veis hay muchísimas opciones para elegir a la hora de darle a nuestros usuarios la posibilidad de seleccionar valores para los atributos de nuestros shortcodes, con un poco de investigación en la documentación de TinyMCE y algo de ayuda extra (sobre todo para esos “truquitos”) podemos presentarle al usuario final un montón de posibilidades a la hora de añadir contenido.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *