Dans les articles précédents de cette série (Un plugin WordPress avec page de réglage), nousavons construit les fondements d’une extension correcte, avec une page de réglages qui fonctionne correctement. Je veux maintenant modifier trois détails pour que cette extension serve plus facilement de base à d’autres extension :
si un champs est de type « url » ou « email », il faudra qu’il y ait validation que les valeurs saisies sont bien soit des url soit des adresses mail ;
Ajouter un champs de type « date » ;
Les données à modifier pour adapter les bases de l’extension devraient être dans un fichier distinct, avec des commentaires pour faciliter l’adaptation par le développeur.
Nous allons réaliser toutes ces opérations, en commençant par la dernière.
déplacer les données variables dans un autre fichier
Dans clea-add-button.php , ajouter les lignes suivantes :
// the sections and fields data for the settings page.
require_once CLEA_ADD_BUTTON_DIR_PATH . 'admin/clea-add-button-admin-settings.php';
On dit donc au script principal de notre extension qu’il devra utiliser le script contenu dans /admin/clea-add-button-admin-settings.php . Ce script contient deux fonctions qui retournent respectivement le contenu des arrays de définition des sections et des champs. J’y ai simplement copié les deux fonctions clea_add_button_settings_sections_val() et clea_add_button_settings_fields_val() qui étaient auparavant à la fin du fichier clea-add-button-settings-page.php .
Attention : le fichier doit être encodé en utf-8 avant d’y écrire des caractères accentués en français par exemple. Sinon les texte sont affichés avec des � à la place des caractères « spéciaux ».
Maintenant dans clea-add-button-settings-page.php , il faut que je dise comment traiter les champs de type « date-picker ». Dans la fonction clea_add_button_settings_field_callback( $arguments ) , j’ajoute un cas « date-picker » :
A ma grande surprise, je n’ai même pas eu à ajouter quoi que ce soit d’autre. Je m’attendais pourtant à devoir charger jquery-ui-datepicker pour cette page et aussi à devoir créer une fonction…
Si on veut ajouter des options, on pourra s’inspirer des deux articles suivants :
On peut avoir à demander une adresse mail ou le lien d’un site internet. Dans ce cas, la validation et la sanitation du contenu du champs ne se font pas de la même manière que pour un simple texte.
La validation des champs est réalisée lorsqu’on affiche les champs. Jusqu’à présent l’utilisateur n’était pas informé s’il entrait une valeur non valide. C’est pourtant indispensable. Nous allons utiliser la fonction
La sanitation et l’affichage sont faits dans clea-add-button-settings-page.php , par le biais de la fonction clea_add_button_settings_field_callback( $arguments ) .
Commençons par ajouter deux nouveaux champs, de type ‘url’ et ’email’ dans la section 1, définis dans clea-add-button-admin-settings.php :
Il faut maintenant que nous les validions, que les « nettoyons » (sanitation) et que nous les affichions en sécurité. Tout se fait dans une fonction que l’on appelle lors de l’étape « register_setting().
Dans le fichier clea-add-button-settings-page.php , modifier la ligne qui réalise le « register_settings() » pour qu’elle appelle une nouvelle fonction :
Noter que l’affichage se fait en utilisant sanitize_email().
Si je tape une adresse email invalide (par exemple « rgdfghhr »), un message « You have entered an invalid e-mail address. » s’affiche en haut de la page de réglages lorsque je clique sur « enregistrer les modifications ».
Ca fonctionne mais le gros inconvénient est qu’il faudra aller modifier cette fonction à chaque fois qu’on change le nom ou le type d’un champ. Par exemple, si je décide que le champ ‘field-1-6′ s’appellera ’email’, il faudra que j’aille changer le contenu de la fonction clea_add_button_settings_validate_and_sanitize.
Il faut donc que cette sanitation et validation se fasse sans se référer explicitement à un nom de champ. Pour celà, on va utiliser l’array de définition des champs, comme on a fait dans la fonction clea_add_button_admin_init().
function clea_add_button_settings_validate_and_sanitize( $input ) {
$output = (array) get_option( 'my-plugin-settings' );
$set_fields = clea_add_button_settings_fields_val() ;
$types = array() ;
// create an array with field names and field types
foreach ( $set_fields as $fields ) {
foreach( $fields as $field ){
$types[ $field['field_id'] ] = $field['type'] ;
}
}
// now validate and sanitize each field
foreach ( $types as $key => $type ) {
switch( $type ){
case 'wysiwig' :
$output[ $key ] = wp_kses_post( $input[ $key ] ) ;
break ;
case 'email' :
if ( is_email( $input[ $key ] ) ) {
$output[ $key ] = $input[ $key ];
} else {
$message = __( 'You have entered an invalid e-mail address in : ', 'clea-add-button' ) ;
$message .= $key ;
add_settings_error( 'my-plugin-settings', 'invalid-email', $message );
}
break ;
default :
$output[ $key ] = sanitize_text_field( $input[ $key ] ) ;
}
}
return $output;
}
Au départ, je crée un array avec tous les noms de champs et leurs type, tels que définis dans la fonction clea_add_button_settings_fields_val() . Ensuite, pour chaque élément de cet array, nous faisons une sanitation et une validation différente selon son type.
Mettre à jour le numéro de version et le readme
Dans le fichier clea-add-button.php , j’ai modifié @version 0.3.0 . L’extension est maintenant en version 0.3.
Dans le README.md, j’ai ajouté la version 0.3 dans le « changelog » :
== Changelog ==
= 0.3 =
* the definition of sections and fields to display on the settings page is now in /admin/clea-add-button-admin-settings.php
* 3 new types of fields may be processed : date-picker, email and url
* validation and sanitation of user inputs is done and the user gets a notice in case of a bad email
L’extension de base finale, clea-plugin-boilerplate est passée en version 1 et disponible ici sur github. Elle est identique à clea-add-button sauf que les noms de fichiers, de fonctions et de variables ont été changés et qu’un fichier pot a été généré pour les traductions.
Et maintenant
Notre page de réglages fonctionne, quel que soit le type de champs que l’on souhaite afficher. Nous disposons maintenant d’une extension de base, correctement codée, que l’on peut utiliser comme base (boilerplate) n’importe quelle extension. C’est la fin de cette série, Un plugin WordPress avec page de réglage.
Nous allons améliorer la page de réglage de notre extension WordPress, en y ajoutant un champ de type « color picker » et en affichant les données complémentaires définies avec les champs. Dans les articles précédents de cette série (Un plugin WordPress avec page de réglage), nous avons progressivement construit l’extension et sa page de réglage. A la fin du précédent article, l’extension correspondait au contenu de ce fichier zip : clea-add-button avec éditeur wp_editor (zip).
Que voulons-nous faire ?
Notre page de réglages est capable d’afficher tous les types de champs sauf les choix de couleur. Nous voulons donc pouvoir choisir une couleur et même définir son opacité. Et pour simplifier la vie de l’administrateur du site, il faut qu’on lui dise quelles sont les couleurs utilisées par son thème, en les intégrant dans une palette sous le color picker.
Par ailleurs, notre page de réglages devra afficher les éléments complémentaires définis pour chaque champs, tels que « helper » qui contient un texte d’aide complémentaire.
Identifier les couleurs utilisées par le site
Les paramètres du thème courant sont stockés par WordPress et récupérables avec la fonction get_theme_mods(). Cette fonction nous donne un array contenant tous les paramètres, dont certains sont des couleurs.
Nous lisons donc l’array et pour chaque valeur, nous regardons s’il s’agit d’une couleur avec la fonction sanitize_hex_color_no_hash(). Cette fonction renvoie « NULL » si elle ne trouve pas un élément en hexadécimal à 3 ou 6 caractères. A chaque fois qu’elle trouve un élément non NULL, on le place dans l’array qui sera retourné pour former notre palette de couleurs.
/**********************************************************************
* find the colors used in the website's theme
**********************************************************************/
function clea_presentation_get_current_colors() {
// to get all the current theme data in an array
$mods = get_theme_mods();
$color = array() ;
foreach ( $mods as $key => $values ) {
if ( !is_array( $values ) ) {
if ( is_string( $values ) && trim( $values ) != '' ) {
$hex = sanitize_hex_color_no_hash( $values ) ;
if ( trim( $hex ) != '' ) {
$color[ $key ] = $hex ;
}
}
}
}
// remove duplicate colors
$current_colors = array_unique( $current_colors ) ;
return $color ;
}
Pour vérifier que c’est une bonne solution, , nous ajoutons le code suivant à la fonction clea_add_button_options_page() :
case 'color' :
// get the colors used by the theme
$current_colors = clea_presentation_get_current_colors() ;
$data_palette = "";
// the color palette must be a string with colors and | separator
// "#222|#444|#00CC22|rgba(72,168,42,0.4)" would be ok
foreach ( $current_colors as $color ) {
$data_palette .= $color . '|' ;
}
$data_palette = rtrim( $data_palette, '|' ) ;
// uses https://github.com/BraadMartin/components/tree/master/alpha-color-picker
printf( '<input type="text" class="alpha-color-picker" name="%2$s" value="%1$s" data-palette="%5$s" data-default-color="%4$s" data-show-opacity="true" />', sanitize_text_field( $value ), $name, $field, $arguments['default'], $data_palette ) ;
break ;
A ce stade, celà affiche un champs de type ‘text’ avec à l’intérieur la couleur par défaut définie dans le champs. Il nous faut maintenant ajouter la feuille de style et le programme javascript conçus par le créateur de l’Alpha color Picker et un petit script en JQuery.
Les compléments à intégrer dans notre extension
Dans le répertoire « /admin » du plugin, créer les sous-répertoires « /css » et « /js « .
Télécharger le fichier zip du dépôt « components » de Braad Martin ici sur un PC. Décompresser le fichier zip puis dans le sous-répertoire « alpha-color-picker » copier alpha-color-picker.css et alpha-color-picker.js respectivement dans les répertoires /admin/css et /admin/js de l’extension.
Les fichiers à créer
Nous devons créer deux fichiers :
un fichier (clea-add-button-color-trigger.js par exemple) qui contiendra le JQuery nécessaire à l’affichage
un fichier (clea-add-button-admin-enqueue.php par exemple) qui sera appelé par le fichier principal de l’extension et qui chargera la feuille de style et les scripts JavaScript nécessaires.
Le fichier /admin/js/clea-add-button-color-trigger.js contient :
Quant au fichier /admin/js/clea-add-button-admin-enqueue.php , il contient
add_action( 'admin_enqueue_scripts', 'clea_add_button_admin_enqueue_scripts' );
function clea_add_button_admin_enqueue_scripts( $hook ) {
// to find the right name, go to the settings page and inspect it
// the name is somewhere in the <body class="">
// it will always begin with settings_page_
if( 'settings_page_my-plugin' != $hook ) {
// echo "not the right page, this is : " ;
// echo $hook ;
return;
}
// for the alpha color picker
// source : https://github.com/BraadMartin/components/tree/master/alpha-color-picker
wp_enqueue_style(
'alpha-color-picker',
CLEA_ADD_BUTTON_DIR_URL . '/admin/css/alpha-color-picker.css',
array( 'wp-color-picker' ) // You must include these here.
);
wp_enqueue_script(
'alpha-color-picker',
CLEA_ADD_BUTTON_DIR_URL . '/admin/js/alpha-color-picker.js',
array( 'jquery', 'wp-color-picker' ), // You must include these here.
null,
true
);
// This is the JS file that will contain the trigger script.
// Set alpha-color-picker as a dependency here.
wp_enqueue_script(
'clea-add-button-admin-color-js',
CLEA_ADD_BUTTON_DIR_URL . '/admin/js/clea-add-button-color-trigger.js',
array( 'alpha-color-picker' ),
null,
true
);
}
La condition if( ‘settings_page_my-plugin’ != $hook ) permet de ne pas charger le style et le JavaScript si on n’est pas sur la page d’options de notre extension.
Enfin, dans clea-add-button.php , nous ajoutons la ligne qui démarrera clea-add-button-admin-enqueue.php :
// load styles and scripts for the admin
require_once CLEA_ADD_BUTTON_DIR_PATH . 'admin/clea-add-button-admin-enqueue.php';
le résultat
Notre page d’option affiche maintenant la couleur par défaut sous une forme différente :
Le champ « color » affiche bien une couleur maintenant.
Si on clique sur la couleur, un « color picker » s’ouvre. Il affiche la couleur par défaut définie dans le champ 3. On observe qu’il y a une palette de couleur en dessous, qui correspond effectivement aux couleurs du thème sur lequel j’essaie cette extension. Et en dessous, on voit une barre qui permet de gérer la transparence de la couleur choisie.
Le champ « color » affiche un « color picker » complet.
Ici nous sélectionnons la couleur orange du thème et nous demandons à ce que sa transparence soit 71%.
Choix d’une couleur de la palette puis réglage de la transparence
Ca fonctionne parfaitement et la nouvelle couleur s’enregistre correctement.
Afficher les informations complémentaires
Chaque champ affiché dans notre page d’options est défini par un array :
Nous utilisons toutes les clés de cet array sauf deux (‘label’ et ‘helper’). Ces deux clés contiennent respectivement le texte à afficher systématiquement pour expliciter ce qu’il faut faire et un texte d’aide complémentaire.
Pour l’afficher, avant le champs à remplir, j’ajoute le code suivant dans la fonction clea_add_button_settings_field_callback( $arguments ) :
// If there is a help text and / or description
printf( '<span class="field_desc">' ) ;
if( isset( $arguments['helper'] ) && $helper = $arguments['helper'] ){
printf( '<img src="%1$s/images/question-mark.png" class="alignleft" id="helper" alt="help" title="%2$s" data-pin-nopin="true">',CLEA_ADD_BUTTON_DIR_URL, $helper ) ;
}
// If there is a description
if( isset( $arguments['label'] ) && $description = $arguments['label'] ){
printf( ' %s', $description ); // Show it
}
printf( '</span>' ) ;
La condition if( isset( $arguments[‘helper’] ) && $helper = $arguments[‘helper’] ) vérifie que $arguments[‘helper’] est défini ET qu’il n’est pas vide.
J’ai placé un fichier question-mark.png dans le répertoire /images de l’extension.
Le résultat n’est pas très joli :
Il faut donc que j’ajoute une feuille de style pour améliorer la page d’options.
Créer une feuille de style pour la page d’options
Dans le fichier /admin/js/clea-add-button-admin-enqueue.php , nous ajoutons une ligne pour charger la feuille de style de notre page de réglages :
Notre page de réglages fonctionne. Il ne reste plus que quelques améliorations à apporter. Ce sera l’objet du prochain article de cette série, Un plugin WordPress avec page de réglage.
Notre extension a été démarrée dans les premiers articles de cette série (Un plugin WordPress avec page de réglage). A ce stade, elle affiche uniquement des champs de type « texte » et n’affiche pas le contenu des arguments supplémentaires que l’on peut ajouter au champs.
Nous allons donc voir maintenant comment afficher des champs de type textarea, select, checkbox, radio button, wysiwig.
Modifier la fonction de restitution des champs
Dans le fichier « /admin/clea-add-button-settings-page.php « , il faut modifier la fonction clea_add_button_settings_field_callback( $arguments ) pour qu’elle s’adapte à différents types de champs.
Nous utilisons la commande « switch » qui choisira l’action à réaliser selon le ‘type’ de champs à restituer :
switch( $arguments['type'] ){
case 'text' :
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
break ;
default :
printf( esc_html__( 'This field is type <em>%s</em> and could not be rendered.', 'clea-add-button' ), $arguments['type'] );
}
Ca fonctionne : le champs 1-1 est restitué comme un texte mais pas les autres puisque nous n’avons pas défini leur type pour l’instant.
Il va donc falloir que je modifie l’array qui définit les champs, dans la fonction clea_add_button_settings_fields_val() . Je veux que tous les champs soient définis par un array avec la forme suivante :
Noter que l’on utilise la fonction esc_textarea() pour interdire qu’un caractère présent dans la variable $description puisse être interprété comme du HTML.
autres types de champs, probablement avec esc_attr() aussi.
Affichage du « textarea » plus précis
Dans la référence HTML5 du W3C, la page dédiée à <textarea> indique une multitude d’arguments pour définir sa forme. Et les attributs globaux, applicables à tous les éléments HTML5 contiennent entre autre « id » et « value ». Nous allons au minimum vouloir définir « rows » et « cols », respectivement le nombre de lignes et de colonnes de la zone de texte à définir. Au final, l’expression de notre textarea sera sous la forme :
Pour tous nos champs, les attributs devront prendre une valeur liée à la définition du champs. J’utilise printf pour rendre plus simple la définition de ce qui doit être affiché.
Et voici notre fonction modifiée pour utiliser esc_attr pour le cas ‘text’ et pour afficher correctement notre zone textarea :
function clea_add_button_settings_field_callback( $arguments ) {
$settings = (array) get_option( 'my-plugin-settings' );
$field = $arguments['field_id'] ;
$value = $settings[$field] ;
// for development only
if ( ENABLE_DEBUG ) {
echo "<hr /><pre>";
print_r( $arguments ) ;
echo "</pre><hr />";
}
$name = 'my-plugin-settings['. $field . ']' ;
switch( $arguments['type'] ){
case 'text' :
printf( '<input type="text" id="%3$s" name="%2$s" value="%1$s" />', esc_attr( $value ), $name, $field );
break ;
case 'textarea' :
printf( '<textarea name="%2$s" id="%3$s" rows="4" cols="80" value="%1$s">%1$s</textarea>', esc_textarea( $value ), $name, $field );
break ;
default :
printf( esc_html__( 'This field is type <em>%s</em> and could not be rendered.', 'clea-add-button' ), $arguments['type'] );
}
}
A ce stade, notre page de réglages affiche :
Affichage correct d’un champs text et d’un champs textarea
Correction d’une notice PHP
Query Monitor (cf l’article précédent de cette série « Un plugin WordPress avec page de réglage » indique 3 notices PHP. Ce sont des erreurs bénignes qu’il faut corriger.
Les trois notices portent sur des index indéfinis pour field-2-1, field-2-2 et field-2-3, sous la forme « Undefined index: field-2-1 ». Ces trois champs n’ont jamais été définis puisqu’on n’a pas encore pu leur attribuer une valeur. Cependant, il n’est pas normal que notre code traite des valeurs indéfinies sans « s’en rendre compte ».
Dans cet article de code.tutsplus, je vois qu’ils font deux vérifications avant de lire la valeur de l’option. La première consiste à vérifier qu’il existe bien des options pour notre réglage (‘my-plugin-settings’ pour moi, ‘sandbox_theme_display_options’ pour eux :
La seconde vérification porte sur l’option du champs en question :
// First, we read the social options collection
$options = get_option( 'sandbox_theme_social_options' );
// Next, we need to make sure the element is defined in the options. If not, we'll set an empty string.
$url = '';
if( isset( $options['twitter'] ) ) {
$url = $options['twitter'];
} // end if
Cette partie là est réalisée dans la fonction « callback » du champs ‘twitter’.
Là dessus, j’ai longtemps bloqué car je dois vérifier si $settings[ $field ] est « isset » et pour une raison que j’ignorais ce n’était jamais le cas, même lorsqu’il y avait bien une valeur… Et puis je suis tombée sur cette question dans wordpress.stackexchange et j’ai enfin compris comment faire (mais pas franchement pourquoi !).
Ma fonction clea_add_button_settings_field_callback( $arguments ) contient maintenant une vérification au début. Si $options[ « $field » ] est « set », $value prend la valeur de $settings[ $field ] . Dans le cas contraire, elle prend la valeur par défaut du champs.
function clea_add_button_settings_field_callback( $arguments ) {
$settings = (array) get_option( "my-plugin-settings" );
$field = $arguments[ 'field_id' ] ;
// for development only
if ( ENABLE_DEBUG ) {
echo "<hr /><p>Arguments</p><pre>";
print_r( $arguments ) ;
echo "</pre><hr />";
echo "<hr /><p>Options</p><pre>";
print_r( $settings ) ;
echo "</pre><hr />";
}
// set a $options array with the field id as it's key
if ( !empty( $settings ) ) {
foreach ( $settings as $key => $option ) {
$options[$key] = $option;
}
}
// now check if $options[ $field ] is set
if( isset( $options[ "$field" ] ) ) {
$value = $settings[ $field ] ;
} else {
// set the value to the default value
$value = $arguments[ 'default' ] ;
}
$name = 'my-plugin-settings['. $field . ']' ;
switch( $arguments['type'] ){
case 'text' :
printf( '<input type="text" id="%3$s" name="%2$s" value="%1$s" />', esc_attr( $value ), $name, $field );
break ;
case 'textarea' :
printf( '<textarea name="%2$s" id="%3$s" rows="4" cols="80" value="%1$s">%1$s</textarea>', esc_textarea( $value ), $name, $field );
break ;
default :
printf( esc_html__( 'This field is type <em>%s</em> and could not be rendered.', 'clea-add-button' ), $arguments['type'] );
}
}
Les notices PHP pour les champs qui n’ont pas encore pu être saisis ont maintenant disparu.
Si les options ne sont pas fixes (cas où il y a plusieurs champs select), il faut pouvoir les ajouter dans l’array de définition du champs. Je crée un troisième champs dans la section 1 avec les caractéristiques suivantes :
Si les options ne sont pas fixes, il faut pouvoir les ajouter dans l’array de définition du champs. Je modifie le premier champ de la section 2 comme pour le champs de type radio, en y ajoutant des options.
Pour le visualiser, j’ajoute le cas ‘radio’ comme suit :
Et voilà maintenant la section 2 de ma page de réglage contient un premier champs radio correctement présenté :
Affichage d’un champs « radio »
Afficher des champs « wysiwig «
Notre champs wisiwig correspond à un éditeur, un peu comme lorsqu’on crée un article dans WordPress. Il est restitué par la fonction wp_editor de WordPress. Ici j’ai choisi de ne pas ajouter d’arguments spécifiques.
Pour le visualiser, j’ajoute le cas ‘wysiwig’ comme suit :
case 'wysiwig' :
// sanitize data for the wp_editor
$content = wp_kses_post( $value ) ;
$args = array(
'textarea_name' => $name
) ;
wp_editor( $content, $field, $args );
break ;
Le Codex pour wp_editor() indique que l’ID de l’éditeur ne peut contenir que des minuscules et des tirets bas (underscores). J’ai donc modifié le ‘field_id’ pour qu’il devienne field_2_2.
On notera que $value est nettoyé avec la fonction wp_kses_post(), qui permet entre autres d’avoir des images dans le texte.
Et voilà maintenant la section 2 contient un éditeur de texte :
Affichage d’un champs « wp-editor »
Et maintenant
Nous verrons dans le prochain article de cette série (Un plugin WordPress avec page de réglage) comment restituer le dernier champs comme un color picker, et aussi comment afficher les arguments supplémentaires.
Lorsqu’on crée une extension (ou un thème) WordPress, il est indispensable de prévoir des moyens de déboguer efficacement. Nous allons voir ici quelques méthodes applicables, telles que l’affichage du contenu de variables dans une page du tableau de bord WordPress, l’utilisation de debug.log de WordPress ou l’utilisation d’extensions WordPress telles que Debug Bar et Query Monitor.
Sur un site de développement (mais jamais sur un site de « production » utilisable par nos internautes cibles), on modifie wp-config.php pour que le debogage soit réalisé et que les erreurs s’affichent lors de l’affichage des pages du site (front ou back). Par défaut, les paramètres suivants sont réglés sur « false » :
Dans ce cas, s’il y a une erreur importante, notre page web affiche un contenu blanc, sans aucune indication du type d’erreur rencontré. Si je met ces paramètres à ‘true’, j’aurai un affichage de l’erreur à l’écran et je pourrai y remédier :
Exemple : je crée volontairement une erreur (par exemple en enlevant un « ; » à la ligne 45 de clea-add-button-settings-page.php. Je recharge la page d’options ou je vais sur le site et le site a disparu. A la place, il y a une page blanche, avec le message « Fatal error: syntax error, unexpected ‘$set_sections’ (T_VARIABLE) in /home/cecilebo/test1/wp-content/plugins/clea-add-button/admin/clea-add-button-settings-page.php on line 47″. Je sais immédiatement dans quel fichier aller corriger l’erreur. Je regarde les lignes qui précèdent la ligne 47 : la 46 est vide et c’est plutôt simple d’ajouter le « ; » manquant en ligne 45.
Donc, dès qu’on est sur un site de développement, on régle les paramètres de wp-config.php pour afficher les erreurs :
On peut aussi régler la ligne suivante à « true » :
define('WP_DEBUG_LOG', true);
Dans ce cas, les erreurs sont stockées dans un fichier debug.log situé dans le répertoire /wp-content. C’est pratique si on doit absolument intervenir sur un site de production et qu’on ne peut pas avoir WP_DEBUG_DISPLAY réglé à « true ». Mais ça oblige à aller sans arrêt éditer le contenu de debug.log pour voir ce qui se passe. Je n’aime pas beaucoup, sauf cas particuliers comme lorsque je veux afficher des contenus de variables dans un fichier (voir plus loin).
Utilisation d’extensions spécialisées
l’extension « Debug Bar »
j’utilise depuis longtemps l’extension ‘Debug Bar‘. Elle ajoute un élément « debug » à gauche du nom Jde l’utilisateur dans le tableau de bord WordPress. Si on clique dessus, et que WP_DEBUG est réglé à ‘true’, on voit s’afficher la liste des notices et warnings auxquels il faut qu’on fasse attention. Lorsqu’il y a des warnings, l’élément « debug » devient rouge pour nous alerter.
Avantages :
de nombreuses autres extensions d’aide au développement s’appuient sur Debug Bar. J’utilise par exemple Debug Bar Post Types lorsque je crée des custom post types. Le « Plugin Handbook » de WordPress contient une page dédiée à ces extensions complémentaires. Je remarque en particulier Debug Bar Shortcodes, que je n’ai jamais utilisé mais qui pourrait être utile.
Inconvénients :
on doit jongler entre différents écrans pour visualiser ce qui se passe dans notre site.
Debug Bar est très bien, mais il y a encore mieux !
La solution idéale : l’extension ‘Query Monitor’
Depuis que j’ai découvert cette extension, je ne lui ai pas trouvé un seul défaut. Elle permet de recueillir toutes les informations nécessaires au développement tout en étant pratique. C’est surtout l’interface utilisateur qui fait la différence.
Pour plus d’informations, voir sur WordPress.org, ici. Je note que les FAQ indiquent que les compléments de Debug Bar puvent être utilisés avec Query Monitor, sans que Debug Bar soit présent. Et il existe aussi des compléments pour Query Monitor. Je note en particulier les compléments suivants, que je n’ai jamais essayés :
Query Monitor Extend qui permet de faire des « var_dump » visible dans l’écran Query Monitor et de suivre les conditions de WooCommerce.
Il arrive que j’utilise « User Switching » lorsque j’ai besoin de tester avec différents rôles d’utilisateur.
Afficher le contenu d’une variable pour aider au développement / débogage
Ce n’est pas toujours simple de savoir ce que contient une variable définie par notre programme ou par WordPress. C’est en particulier difficile lorsque la variable est un array, dont on ne connait pas la structure, ou pire une chaîne de caractères « sérialisée » (« serialized data » en anglais) comme le contenu de get_option .
Pour afficher des variables, on a plusieurs options :
Afficher la variable directement dans la page que l’on crée ;
Afficher la variable directement dans la page que l’on crée
C’est une solution simple. Par exemple, dans clea-add-button-settings-page.php, pour définir le contenu de la fonction clea_add_button_settings_field_callback( $arguments ), qui affiche les différents champs, j’ai affiché le contenu de l’array $arguments :
l’ajout de <pre> et </pre> permet un affichage propre de l’array.
Mais l’inconvénient d’un tel choix est qu’on doit ensuite commenter toutes les lignes de débogage. J’ai donc fait le choix de définir une constante en début de fichier :
/**********************************************************************
* DEBUG ?
***********************************************************************/
define('ENABLE_DEBUG', true); // if true, the script will echo debug data
et ma fonction function clea_add_button_settings_field_callback( $arguments ) contient :
function clea_add_button_settings_field_callback( $arguments ) {
$settings = (array) get_option( 'my-plugin-settings' );
$field = $arguments['field_id'] ;
$value = esc_attr( $settings[$field] );
// for development only
if ( ENABLE_DEBUG ) {
echo "<hr /><pre>";
print_r( $arguments ) ;
echo "</pre><hr />";
}
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
Comme ça, si j’ai réglé ENABLE_DEBUG à true, je voies le contenu de l’array et sinon je suis en mode utilisation normale de l’extension :
C’est une bonne solution, que je n’ai trouvée que récemment. Mais elle ne fonctionne pas si je n’ai pas une page dédiée à l’extension dans le tableau de bord WordPress. Il peut donc être utile d’afficher ailleurs le contenu des variables. Dans ce cas, on utilisera une extension spécialisée.
Une page de réglage « check variables » apparaît dans « Réglages ».
Pour les deux exemples de réglage qui suivent, j’ai ajouté une ligne à la fonction clea_add_button_settings_section_callback( $args ) :
console( $args );
On peut faire apparaître les variables dans le pied de page comme dans le réglage qui suit :
On peut aussi préférer afficher les variables dans Query Monitor comme dans le réglage qui suit :
C’est pratique comme système.
Et maintenant ?
Une fois qu’on sait ce que contiennent nos variables, tout devient plus simple !
Nous allons poursuivre le développement de l’extension « Add Button » et améliorer la page de réglage pour qu’elle puisse afficher des formats de champs autres que texte. Ce sera l’objet du prochain article de cette série Un plugin WordPress avec page de réglage.
Je poursuis le travail sur la création d’une extension « best practice » généré dans l’article précédent (Créer une extension WordPress ; Partie 1) de cette série. Une extension sur laquelle le webmaster ne peut pas intervenir n’est pas très intéressante puisqu’elle oblige à modifier le code à chaque fois qu’un paramètre change. J’explore donc ici la manière de créer une page de réglage pour l’extension. J’utilise l’API « settings » de WordPress car c’est une méthode plus sûre (la « sanitation » des données est assurée par WordPress).
L’extension est au stade où je l’ai laissée dans le premier article de cette série :
Premier essai
J’ai fait plusieurs essais avant celui-ci mais à chaque fois j’avais un « petit » problème dont je n’arrivais pas trouver la solution. Je préfère donc repartir de zéro pour comprendre. J’ai tout simplement copié le contenu du « gist » de Anna et je l’ai placé dans un fichier « clea-add-button-settings-page.php » du répertoire /admin. Dans le fichier principal de l’extension (clea-add-button.php), j’ai ajouté la ligne require_once CLEA_ADD_BUTTON_DIR_PATH . ‘admin/clea-add-button-settings-page.php’; et j’ai rechargé la fenêtre de mon navigateur.
Une page de réglage « My Plugin Options » apparaît sous « réglages » (la copie d’écran correspond à ce qui est affiché une fois que j’ai enregistré une première fois des données) :
Essai 1 de page de réglage d’une extension
Et dans la base de données, la table « prefix-options » contient une nouvelle ligne (option_name = my-plugin-settings), qui contient :
C’est donc exactement ce que je cherche à faire. Reste maintenant à le faire mieux !
Qu’est-ce que je veux faire ?
Je veux obtenir une page de réglage avec plusieurs sections et des champs qui pourraient être présentés sous forme de texte court, de texte long (textarea), de cases à cocher (radio ou checkbox), de liste déroulante, d’un éditeur wysiwig pour des textes sophistiqués et d’un sélecteur de couleur (color picker).
Je voudrais aussi pouvoir définir les sections et les champs dans des « arrays » (tableaux) et générer automatiquement la page de réglages.
Enfin, je voudrais qu’une case à cocher permette d’afficher des éléments de débogage à la fin de la page de réglages si je le souhaite.
Etape 1 : générer automatiquement l’exemple ci-dessus
Je vais modifier le gist de Anna pour générer automatiquement sa page de réglages avec moins de code.
Respecter les bonnes pratiques :
La première chose que je fais, c’est de remplacer ‘textdomain’ par ‘clea-add-button’ et de mettre un préfixe clea_add_button_ devant toutes les fonctions.
regrouper la définition des sections et champs, automatiser la génération
J’ai regroupé la définition des sections à la fin, dans la fonction clea_add_button_settings_sections_val() . Idem pour la définition des champs, dans la fonction clea_add_button_settings_sections_val() .
Ainsi la fonction clea_add_button_admin_init() ne contient plus qu’une boucle « foreach », qui lit les arrays de sections ou champs à générer et les génère.
<?php
/**
*
* Créer une page de settings pour l'extension
*
* @link
* @since 0.2.0
*
* @package clea-add-button
* @subpackage clea-add-button/includes
* Text Domain: clea-add-button
*/
// Based on Anna's gist https://gist.github.com/annalinneajohansson/5290405
// http://codex.wordpress.org/Settings_API
/**********************************************************************
* to set the title of the setting page see -- clea_add_button_options_page()
* to set the sections see -- clea_add_button_settings_sections_val()
* to set the fields see -- clea_add_button_settings_fields_val()
**********************************************************************/
// create the settings page and it's menu
add_action( 'admin_menu', 'clea_add_button_admin_menu' );
// set the content of the admin page
add_action( 'admin_init', 'clea_add_button_admin_init' );
function clea_add_button_admin_menu() {
add_options_page(
__('Options de Clea Add Button', 'clea-add-button' ), // page title (H1)
__('Options', 'clea-add-button' ), // menu title
'manage_options', // required capability
'my-plugin', // menu slug (unique ID)
'clea_add_button_options_page' ); // callback function
}
function clea_add_button_admin_init() {
register_setting( 'my-settings-group', 'my-plugin-settings' );
$set_sections = clea_add_button_settings_sections_val() ;
// add_settings_section
foreach( $set_sections as $section ) {
add_settings_section(
$section[ 'section_name' ],
$section[ 'section_title' ] ,
$section[ 'section_callbk' ],
$section[ 'menu_slug' ]
);
}
$set_fields = clea_add_button_settings_fields_val() ;
// add the fields
foreach ( $set_fields as $section_field ) {
foreach( $section_field as $field ){
add_settings_field(
$field['field_id'],
$field['label'],
$field['field_callbk'],
$field['menu_slug'],
$field['section_name']
);
}
}
}
/**********************************************************************
* The actual page
**********************************************************************/
function clea_add_button_options_page() {
?>
<div class="wrap">
<h2><?php _e('My Plugin Options', 'clea-add-button'); ?></h2>
<form action="options.php" method="POST">
<?php settings_fields('my-settings-group'); ?>
<?php do_settings_sections('my-plugin'); ?>
<?php submit_button(); ?>
</form>
</div>
<?php }
function section_1_callback() {
_e( 'Some help text regarding Section One goes here.', 'clea-add-button' );
}
function section_2_callback() {
_e( 'Some help text regarding Section Two goes here.', 'clea-add-button' );
}
function field_1_1_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_1_1";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
function field_1_2_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_1_2";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
function field_2_1_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_2_1";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
function field_2_2_callback() {
$settings = (array) get_option( 'my-plugin-settings' );
$field = "field_2_2";
$value = esc_attr( $settings[$field] );
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
}
/*
* INPUT VALIDATION:
* */
function clea_add_button_settings_validate_and_sanitize( $input ) {
$settings = (array) get_option( 'my-plugin-settings' );
if ( $some_condition == $input['field_1_1'] ) {
$output['field_1_1'] = $input['field_1_1'];
} else {
add_settings_error( 'my-plugin-settings', 'invalid-field_1_1', 'You have entered an invalid value into Field One.' );
}
if ( $some_condition == $input['field_1_2'] ) {
$output['field_1_2'] = $input['field_1_2'];
} else {
add_settings_error( 'my-plugin-settings', 'invalid-field_1_2', 'You have entered an invalid value into Field One.' );
}
// and so on for each field
return $output;
}
/**********************************************************************
* THE SECTIONS
**********************************************************************/
function clea_add_button_settings_sections_val() {
$sections = array(
array(
'section_name' => 'section-1',
'section_title' => __( 'Section One', 'clea-add-button' ),
'section_callbk'=> 'section_1_callback',
'menu_slug' => 'my-plugin' ,
),
array(
'section_name' => 'section-2',
'section_title' => __( 'Section Two', 'clea-add-button' ),
'section_callbk'=> 'section_2_callback' ,
'menu_slug' => 'my-plugin'
),
);
return $sections ;
}
/**********************************************************************
* THE FIELDS
**********************************************************************/
function clea_add_button_settings_fields_val() {
$section_1_fields = array (
array(
'field_id' => 'field-1-1',
'label' => __( 'Field One', 'clea-add-button' ),
'field_callbk' => 'field_1_1_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-1',
),
array(
'field_id' => 'field-1-2',
'label' => __( 'Field Two', 'clea-add-button' ),
'field_callbk' => 'field_1_2_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-1'
),
);
$section_2_fields = array (
array(
'field_id' => 'field-2-1',
'label' => __( 'Field One', 'clea-add-button' ),
'field_callbk' => 'field_2_1_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-2'
),
array(
'field_id' => 'field-2-2',
'label' => __( 'Field Two', 'clea-add-button' ),
'field_callbk' => 'field_2_2_callback',
'menu_slug' => 'my-plugin',
'section_name' => 'section-2'
),
);
$section_fields = array(
'section-1' => $section_1_fields,
'section-2' => $section_2_fields
) ;
return $section_fields ;
}
Ajouter d’autres arguments pour les champs
Il faut que j’ajoute des arguments à chaque champs :
‘type’ : dans l’exemple dont je pars, tous les champs sont des textes courts. Comme je ne veux pas avoir autant de fonctions que de champs pour les afficher, il faut que le programme sache quel est le type du champs pour savoir comment le restituer.
‘helper’ : un texte qui aidera l’utilisateur à comprendre comment faire son réglage.
‘default’ : qui permettra de définir une valeur si l’utilisateur n’en définit pas.
Chaque champs doit pouvoir être défini par un array contenant les 5 éléments obligatoires et les trois nouveaux.
Dans la fonction qui génère les champs, j’ajoute une dernière valeur qui correspondra à $args, le 6ème élément de la fonction add_settings_field.
// add the fields
foreach ( $set_fields as $section_field ) {
foreach( $section_field as $field ){
add_settings_field(
$field['field_id'],
$field['label'],
$field['field_callbk'],
$field['menu_slug'],
$field['section_name'],
$field
);
}
}
Et dans la fonction field_1_1_callback() , j’ajoute $argument comme élément reçu, ainsi que l’affichage du contenu de cette variable (c’est un array, j’utilise print_r) :
Page de réglage d’une extension WordPress : affichage des arguments de add_settings_field
Regrouper les fonctions « callback » pour les sections
Il n’y a pas beaucoup de sections en général mais ce n’est pas la peine de créer autant de fonctions que de sections.
Je crée donc une fonction clea_add_button_settings_section_callback( $args ) (que je n’oublie pas de déclarer comme ‘section_callbk’ dans la définition des sections) :
Cette ligne va chercher la valeur enregistrée pour toutes les options d’un nom défini lorsque nous avons exécuté la commande ‘register_setting’, au début de la fonction clea_add_button_admin_init() . C’est une valeur fixe que je laisse telle quelle pour l’instant (ce n’est certainement pas une bonne idée de laisser un nom si générique, sans préfixe spécifique).
$field = "field_1_1";
Là on voit que « field_1_1 » correspond au contenu de $args[field_id].
$value = esc_attr( $settings[$field] );
Cette ligne lit la valeur attribuée à l’option et la met dans la variable « value ». On ne la change pas puisque $field n’est plus fixe.
Ne change pas non plus. Ma fonction clea_add_button_settings_field_callback( $args ) va donc contenir quasiment la même chose qu’avant mais elle sera utilisable par tous les champs :
Maintenant je supprime les 4 fonctions callback des 4 champs définis et dans la définition des champs, je remplace le contenu de ‘field_callbk’ par le nom de la fonction collective.
J’obtiens bien l’affichage de mon formulaire, mais pas des valeurs déjà enregistrées. Et il y a 4 PHP notices. La première notification signale Undefined index: field-1-1 en ligne 128 de clea-add-button-settings-page.php. Les trois autres sont identiques et portent sur les 3 autres champs.
C’est en fait « normal » : je me suis trompée en définissant l’array des champs. J’ai mis des id de type ‘field-1-1’ alors que Anna, l’auteure du Gist dont je m’inspire utilisais field_1_1… Il m’a suffi d’enregistrer une valeur pour que tout rentre dans l’ordre.
Et voilà, j’obtiens exactement le même résultat qu’Anna mais même si j’ajoute des dizaines de paramètres, les fonctions qui les restituent resteront uniques. C’est plus « élégant », mais surtout ça réduit fortement le risque d’erreur si j’ajoute un nouveau paramètre car le code est beaucoup plus lisible.
Dans des articles à venir, il faudra voir comment ajouter d’autres types de champs, comment charger des feuilles de style, comment faire utiliser un template de l’extension…
Mais d’abord, il faut voir comment on peut plus facilement déboguer. C’est l’objet du prochain article de cette série [the-series).
Commentaires récents