Cómo crear un plugin para WordPress I. Página de configuración

Inicio una serie de entradas para mostrar como desarrollar un plugin de WordPress. Las primeras entradas se centrarán en implementar las características del plugin, pasando por las funciones y hooks que nos da WordPress para facilitarnos el trabajo. Las siguientes entradas servirán para mejorar la calidad y mantenibilidad del código y por último haremos las modificaciones necesarias para hacer que el plugin sea multilenguaje.

Voy a inventarme unas características que sirvan como base del desarrollo.

Características del plugin

El plugin se llama Time Difference Cities y va a mostrar la diferencia horaria entre dos ciudades:

Diferencia horaria entre Madrid y New York: -6 horas

Para mostrar esta información el plugin dispone de un widget. La primera ciudad (que llamaremos local) se podrá seleccionar en la página de configuración del plugin y la segunda (que llamaremos remota) se podrá seleccionar en el propio widget.

En esta entrada mostraré cómo crear el archivo principal y cómo crear la página de configuración. En la siguiente crearé el widget.

Cómo crear el archivo principal

Primero necesito un nuevo directorio en la ruta de los plugins:

my-site/wp-content/plugins/time-difference-cities

A continuación creo un archivo php con el mismo nombre que el directorio. Éste es el archivo principal.

time-difference-cities/time-difference-cities.php

La guía de estilo de programación para WordPress recomienda nombrar los archivos separando las palabras con guión alto (en otra entrada hablaré de la guía de estilo de WordPress).

El archivo principal de todo plugin debe tener una cabecera especial con cierta información que necesita WordPress.

<?php
/**
 * Plugin Name: Time Difference Cities
 * Plugin URI: https://wooenvio.es
 * Description: It add a widget to display time difference between two cities in hours.
 * Version: 1.0.0
 * Author: Pedro Pinto
 * Author URI: http://wooenvio.es
 * Text Domain: tdc
 * Domain Path: /languages
 */

De los parámetros expuestos arriba sólo es obligatorio el nombre del plugin pero es recomendable incluir todos estos.

La mayoría de los parámetros son auto explicativos pero los dos últimos, que pueden generar algo de confusión, son usados para las traducciones del plugin:

  • Text Domain es el dominio usado en las funciones de traducción
  • Domain Path es la ruta donde se encuentran las traducciones.

Con esta cabecera dentro del archivo principal, WordPress tiene que detectarlo como un plugin más dentro de la página de plugins.

Activar plugin de WordPress

Lo activo y obviamente no hace nada, así que debo crear algo y comienzo con su página de configuración.

Cómo crear la página de configuración

Una página de configuración debe empezar con un enlace dentro del menú de administración de WordPress. Y para crear ese enlace me valgo del hook admin_menu.

Los hooks de WordPress son puntos de acceso para modificar su comportamiento

WordPress proporciona una serie de funciones para manejar hooks y en este caso necesito la función add_action que tiene dos parámetros:

  • Nombre del hook: admin_menu
  • Nombre de la función que se engancha al hook: tdc_display_settings_page (En realidad en un "callable" y permite más cosas a parte del nombre de una función)

Esta función enganchada debe a su vez llamar a alguna de la funciones que proporciona WordPress para la construcción de los menús. En este caso necesito add_menu_page

<?php
/**
 * Plugin Name: Time Difference Cities
 * Plugin URI: https://wooenvio.es
 * Description: It add a widget to display time difference between two cities in hours.
 * Version: 1.0.0
 * Author: Pedro Pinto
 * Author URI: http://wooenvio.es
 * Text Domain: tdc
 * Domain Path: /languages
 */

add_action( 'admin_menu', 'tdc_add_menu_page' );

function tdc_add_menu_page() {
    add_menu_page(
        'Time Difference Cities Settings',  // Page title
        'TDC Settings',                     // Menu title
        'manage_options',               // Capability
        'tdc-settings',                 // Menu slug
        'tdc_display_settings_page'     // Callable
    );
}

function tdc_display_settings_page() {
    ?>
    <h1>Time Difference Cities Settings</h1>
    <?php
}

La función add_menu_page tiene varios parámetros, los dos primeros son el título de la página y el texto del menú.

El tercer parámetro son los permisos que debe tener un usuario para entrar en la página de configuración. En este caso uso manage_options, que es un tipo de permiso que tienen los roles Administrador y Super Administrador.

El cuarto parámetro es el texto usado para construir la url de la página de configuración, de forma que la url quedaría así http://mysite.com/wp-admin/tdc-setting

Y el quinto parámetro es otra función que finalmente es la que pinta la página de configuración (el código html de la página). Uso la función tdc_display_settings_page que de momento sólo muestra un título H1.

Prefijando las funciones

Para evitar colisiones con las funciones de otros plugins todas las nuestras funciones deben estar prefijadas con algún texto único, normalmente se usa el nombre del plugin o las iniciales. Aquí voy a usar tdc como prefijo (esto es mejorable y lo haremos más adelante).

El resumen de llamadas para crear la página de configuración sería este:

admin_menu (hook de WordPress)
    |
    └── tdc_add_menu_page (función de nuestro plugin)
        |
        └── add_menu_page (función de WordPress)
            |
            └── tdc_display_settings_page (función de nuestro plugin)

Compruebo que funciona buscando el enlace al final del menú de administración y pinchando para mostrar el título H1.

Menu Pagina Configuracion WordPress

Separando el código en varios archivos

Antes de continuar voy a separar el código en varios archivos. Por ahora no son más que un puñado de líneas, pero es mejor acostumbrarse a separar el código desde el principio y así evitamos que el código se embarre.

Mejor aún sería separar en código en responsabilidades, pero eso también lo veremos más adelante.

De momento creamos el directorio includes y dentro un archivo settings.php con el código anterior sin la cabecera del plugin. De esta forma tenemos agrupado todo lo relacionado con la página de configuración en un archivo independiente del archivo principal del plugin.

<?php

add_action( 'admin_menu', 'tdc_add_menu_page' );

function tdc_add_menu_page() {
    add_menu_page(
        'Time Difference Cities Settings',  // Page title
        'TDC Settings',                     // Menu title
        'manage_options',               // Capability
        'tdc-settings',                 // Menu slug
        'tdc_display_settings_page'     // Callable
    );
}

function tdc_display_settings_page() {
    ?>
    <h1>Time Difference Cities Settings</h1>
    <?php
}

Y en el archivo principal time-difference-cities.php incluimos la referencia a settings.php:

<?php
/**
 * Plugin Name: Time Difference Cities
 * Plugin URI: https://wooenvio.es
 * Description: It add a widget to display time difference between two cities in hours.
 * Version: 1.0.0
 * Author: Pedro Pinto
 * Author URI: http://wooenvio.es
 * Text Domain: tdc
 * Domain Path: /languages
 */

require_once __DIR__ . '/includes/settings.php';

El plugin me queda con esta estructura:

├── includes
│   └── settings.php
└── time-difference-cities.php

Crear un formulario para la página de configuración

Continuamos con las características del plugin y me indican que la página de configuración debe tener un campo para seleccionar la ciudad local. WordPress llama a estos campos de configuración options.

Me valgo de la Setting API de WordPress para construir el formulario de la página.

Primero necesito un nombre para el campo: tdc_local_city (es el nombre usado en la base de datos y para evitar colisiones con otros plugins también necesito prefijarlo).

Para que WordPress se entere de que voy a usar el campo (option) necesito registrarlo con la función register_setting dentro del hook admin_init. Añado el siguiente código en settings.php y lo explico a continuación:

add_action( 'admin_init', 'tdc_register_settings' );

function tdc_register_settings() {

    register_setting(
        'tdc_settings_group',   // Settings group name
        'tdc_local_city',       // Option name
        'sanitize_text_field'   // Arguments or sanitize funcion
    );
}

La función register_setting tiene tres parámetros:

  • Nombre del grupo al que pertenece el campo (sirve para crear secciones). También lo dejo prefijado con tdc
  • Nombre del campo (option) tdc_local_city
  • Y el tercer parámetro puede ser un array con más parámetros o un callable o función para sanear el valor y evitar fallos y problemas de seguridad. En este caso uso la función global sanitize_text_field proporcionada por WordPress para sanear cadenas peligrosas.

Con esto sólo hemos registrado el campo, el siguiente paso es pintar el formulario que lo contiene, pero antes de eso hago otro inciso para mejorar la legibilidad del código. En este caso voy a modificar la función tdc_display_settings_page que pinta la página.

Separar la vista de la página en un archivo nuevo

Una buena práctica es separar la vista a otro archivo. Así que separo el html a un archivo nuevo llamado template/settings-page.php. El archivo (la vista) queda de esta forma:

<h1>Time Difference Cities Settings</h1>

Actualizo la función tdc_display_settings_page para incluir la vista:

function tdc_display_settings_page() {

    require_once __DIR__ . '/template/settings-page.php';
}

Y ahora a estructura del plugin queda así:

├── includes
│   ├── settings.php
│   └── template
│       └── settings-page.php
└── time-difference-cities.php

Vuelvo con el formulario

Construir el formulario

El campo tdc_local_city del formulario es un elemento select y para construirlo necesito un listado de ciudades y recuperar el valor de la base de datos.

Para el listado de ciudades sólo uso tres valores:

  • Madrid
  • New York
  • London

Y con la función get_option obtengo el valor del campo en la base de datos. Ésta función tiene dos parámetros:

  • Nombre del campo
  • Valor por defecto devuelto si no existe en al base de datos. Usaré Madrid.
function tdc_display_settings_page() {

    $local_city = get_option( 'tdc_local_city', 'Madrid' );

    $available_cities = [
        'Madrid',
        'New York',
        'London',
    ];

    require_once __DIR__ . '/template/settings-page.php';
}

La vista del formulario

Ahora toca el turno de la vista settings-page.php donde pintamos el formulario (a continuación explico el código):

<?php settings_errors(); ?>

<h1>Time Difference Cities Settings</h1>

<form method="post" action="options.php">

    <?php settings_fields( 'tdc_settings_group' ); ?>

    <table class="form-table">
        <tr valign="top">
            <th scope="row">Local City</th>
            <td>
                <select name="tdc_local_city">

                    <?php foreach ( $available_cities as $city ) : ?>

                        <option value="<?php echo esc_attr( $city ); ?>"
                                <?php selected( $local_city, $city ); ?>>

                            <?php echo esc_html( $city ); ?>

                        </option>

                    <?php endforeach 

                </select>
            </td>
        </tr>
    </table>

    <?php submit_button(); ?>

</form>

La etiqueta form debe tener el atributo action con el valor option.php, de esta forma WordPress se entera de que es un formulario para guardar ajustes. Dentro del formulario creamos el elemento select donde usamos las variables $available_cities y $local_city iniciadas previamente en la función tdc_display_settings_page.

Explico cada funcion de WordPress usada en la vista:

settings_errors

Muestra los mensajes de éxito o de error cuando se envía el formulario Documentación.

settings_fields

Genera varios campos ocultos necesarios para que WordPress maneje el formulario correctamente. Como parámetro necesita el grupo usado en register_setting de la función tdc_register_settings Documentación.

esc_attr

Escapa caracteres para los valores de los atributos que tienen las etiquetas html. Sirve para evitar problemas de seguridad y de fallos malintencionados. Documentación.

selected

Genera el atributo selected para una etiqueta option cuando coinciden los parámetros pasados. De esta forma queda seleccionado un elemento. Documentación.

esc_html

Escapa caracteres para los bloques de html. Al igual que esc_attr sirve para evitar problemas de seguridad Documentación

submit_button

Genera el botón de envío para el formulario Documentación.

Formulario funcionando

Con este formulario ya puedo guardar el campo en la base de datos de WordPress.

Formulario Pagina Configuracion WordPress

En la siguiente entrada mostraré cómo hacer el widget.

Puedes encontrar el código en github

Deja un comentario