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).
Mes sources d’information
- Codex : Settings API
- Plugin Handbook : Settings API
- un « gist » de Anna (annalinneajohansson) qui m’a semblé contenir tout ce qui est nécessaire à la compréhension du fonctionnement d’une page de settings.
L’extension au départ
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) :
Et dans la base de données, la table « prefix-options » contient une nouvelle ligne (option_name = my-plugin-settings), qui contient :
a:4:{s:9:"field_1_1";s:4:"zzzz";s:9:"field_1_2";s:8:"ttttyyyy";s:9:"field_2_1";s:5:"hhhhh";s:9:"field_2_2";s:6:"mmmmmm";}
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.
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', 'type' => 'text', 'helper' => __( 'help 1', 'clea-presentation' ), 'default' => '' ),
Je me suis inspirée de cet article de Smashing Magazine pour faire ça.
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) :
function field_1_1_callback( $arguments ) { $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' />"; echo "<hr /><pre>"; print_r( $arguments ) ; echo "</pre><hr />"; }
Et ça fonctionne !
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) :
/********************************************************************** * Section callback **********************************************************************/ function clea_add_button_settings_section_callback( $args ) { $sect_descr = array( 'section-1' => __( 'Text regarding Section One goes here.', 'clea-presentation' ), 'section-2' => __( 'Text regarding Section Two goes here.', 'clea-presentation' ) ); $description = $sect_descr[ $args['id'] ] ; printf( '<span class="section-description">%s<span>', $description ); }
Et mes sections s’affichent correctement.
Regrouper les fonctions « callback » pour les champs
Je vais faire la même chose maintenant pour les champs, en remplaçant les 4 fonctions correspondant aux 4 champs par une seule.
En faisant afficher l’array des arguments transmis à la fonction « callback » des champs, j’ai vu que cet array est composé comme suit :
Array ( [field_id] => field-1-1 [label] => Field One [field_callbk] => field_1_1_callback [menu_slug] => my-plugin [section_name] => section-1 [type] => text [helper] => help 1 [default] => )
une fonction « callback » pour les champs définis actuellement (des textes) contient les 4 lignes suivantes :
$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' />";
Il va falloir que je remplace tout ce qui est spécifique au champs 1-1 par des variables, pour que ça s’applique à tous les champs :
$settings = (array) get_option( 'my-plugin-settings' );
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.
echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />";
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 :
function clea_add_button_settings_field_callback( $args ) { $settings = (array) get_option( 'my-plugin-settings' ); $field = $args[field_id] ; $value = esc_attr( $settings[$field] ); echo "<input type='text' name='my-plugin-settings[$field]' value='$value' />"; }
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.
le fichier ‘clea-add-button-settings-page.php’ (le seul modifié) est dans ce « commit », sur github. Vous pouvez également récupérer l’extension à ce stade dans ce fichier zip : clea-add-button V0.1 (zip).
Et maintenant ?
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).