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.
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.
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.
En la siguiente entrada mostraré cómo hacer el widget.
Puedes encontrar el código en github