Pour tous les utilisateurs de WordPress, les mises à jour automatiques constituent un réel progrès, puisqu’elles font disparaître les opérations fastidieuses de téléchargement, de décompression, puis d’upload via FTP, de la nouvelle version. Suite à une mise à jour cauchemardesque de mon plugin EG-Series, je me suis intéressé aux mécanismes de mise à jour de WordPress, et au moyen de les personnaliser sur les plateformes de développement.
Fonctionnement des mises à jour
Avant d’effectuer les modifications, il faut comprendre comment fonctionne WordPress:
- Régulièrement, au fil des requêtes, WordPress lance une fonction pour vérifier les mises à jour (via un système de cron),
- Cette fonction parcourt la liste des plugins,
- Pour chaque plugin, elle lance une requête sur wordpress.org, puis mémorise
- La date de la vérification,
- La liste des plugins testés,
- Ceux dont il existe une mise à jour.
- WordPress affiche ensuite, dans les rubriques correspondantes (menu « Mises à jour », menu « Extension », …)
Ces informations sont stockées dans la table wp_options de la base de données, dans un enregistrement appelé _site_transient_update_plugins. On peut accéder à cet enregistrement dans un plugin avec la fonction get_site_transient( 'update_plugins' ).
« Réinitialiser » la mise à jour
Si vous souhaitez déclencher plus rapidement la recherche de mise à jour, ou si vous avez des problèmes de « faux » messages, il suffit d’effacer l’enregistrement _site_transient_update_plugins:
- A partir de PHP, dans un plugin, avec la commande
delete_site_transient( 'update_plugins' ), - A partir de la base de données, avec la requête
Delete from wp_options where option_name="_site_transient_update_plugins".
Assez fréquents avec les plus anciennes versions, les problèmes de mises à jour des plugins, sont devenus beaucoup plus rares aujourd’hui.
Exclure des plugins de la mise à jour
L’astuce vient de Mark Jaquith.
Il n’existe pas de hook, ou de filtre, dans les fonctions de mise à jour, permettant d’effectuer cette exclusion. L’astuce consiste donc à intercepter les requêtes HTTP de WordPress vers l’extérieur, de vérifier qu’il s’agit bien d’une requête de mise à jour, puis de supprimer le plugin de la liste
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function exclude_plugin_xxx( $r, $url ) { if ( 0 !== strpos( $url, 'http://api.wordpress.org/plugins/update-check' ) ) return $r; // Il ne s'agit pas d'une requête d'update. Nous sortons immédiatement $plugins = unserialize( $r['body']['plugins'] ); // On efface le plugin de la liste des plugins unset( $plugins->plugins[ plugin_basename( __FILE__ ) ] ); // On efface le plugin de la liste des plugins actifs unset( $plugins->active[ array_search( plugin_basename( __FILE__ ), $plugins->active ) ] ); $r['body']['plugins'] = serialize( $plugins ); return $r; } add_filter( 'http_request_args', 'exclude_plugin_xxx', 5, 2 ); |
Avec cette méthode, il faut créer une fonction par plugin à exclure. Notez que la méthode fonctionne également pour la mise à jour des thèmes.
Simuler localement, les mises à jour
Pour gérer les mises à jour, en local, nous avons deux parties distinctes:
- Une zone ou déposer les nouvelles versions, accessible en http,
- Un mécanisme qui fait croire à WordPress, que notre plugin a effectivement besoin d’une mise à jour.
Etape 1: création du dépôt pour les fichiers ZIP
Pour la première partie, quel que soit le serveur Web utilisé, la méthode la plus simple est de créer un répertoire à la racine de votre installation WordPress.
Par exemple: Si WordPress se trouve dans /temp/dev/htdocs/wp330, et que ce répertoire est visible avec l’URL http://localhost/wp330, alors il suffit de créer un répertoire /temp/dev/htdocs/wp330/repository, qui sera visible avec l’url http://localhost/wp330/repository.
Vous pouvez déposer ici, la nouvelle version de vos plugins, sous la forme de fichiers ZIP, sous la forme [nom du plugin].[nouvelle version].zip.
Etape 2: Indiquer les mises à jour à WordPress
Pour cette étape, nous devons analyser ce que contient l’enregistrement _site_transient_update_plugins. La structure de cet enregistrement est la suivante:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | stdClass { "last_checked" => 1323347150, "checked" => array( "akismet/akismet.php" => "2.5.3", "hello.php" => "1.6", 'contact-form-7/wp-contact-form-7.php' => "3.0.1", 'wordpress-seo/wp-seo.php' => "1.0.3", 'wp-super-cache/wp-cache.php' => "0.0.9", "wp-weather/wp-weather.php" => "0.3.11" ), "response" => array( "wp-super-cache/wp-cache.php" => stdClass { "id" => 400, "slug" => "wp-wp-cache", "new_version" => "1.0", "url" => "http://wordpress.org/extend/plugins/wp-super-cache/", "package" => "http://downloads.wordpress.org/plugin/wp-super-cache.1.0.zip"; } ) } |
Donc, nous avons bien la date de la dernière vérification, la liste des extensions vérifiées, et la liste des extensions qui nécessitent une mise à jour (WP-Super-Cache dans notre cas).
Pour simuler la mise à jour localement, il suffit donc de modifier la valeur de l’enregistrement _site_transient_update_plugins, en y incluant les bons paramètres. Par exemple, nous pourrions créer une entrée supplémentaire dans response, avec
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | "response" => array( "wp-super-cache/wp-cache.php" => stdClass { "id" => 400, "slug" => "WP-Super-Cache", "new_version" => "1.0", "url" => "http://wordpress.org/extend/plugins/wp-super-cache/", "package" => "http://downloads.wordpress.org/plugin/wp-super-cache.1.0.zip"; }, "eg-series/eg-series.php"=> stdClass { "id" => 1000, "slug" => "EG-Series", "new_version" => "2.0.4", "url" => " http://localhost/wp330/extend/plugins/eg-series", "package" => " http://localhost/wp330/repository/eg-series.2.0.4.zip"; } ) |
Etape 3: le code
Voici donc un exemple complet d’utilisation de cette astuce, sous forme de plugin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | <?php /* Plugin Name: EG-Update Plugin URI: Description: Simulate the update process Version: 1.0.0 Author: Emmanuel GEORJON Author URI: http://www.emmanuelgeorjon.com/ */ if ( is_admin() ) { if (! class_exists('EG_Update')) { Class EG_Update { function load() { // Add action to create the menu Tools/EG-Update add_action('admin_menu', array(&$this, 'admin_menu')); } function admin_menu() { // Add the menu add_submenu_page( 'tools.php', 'EG-Update Plugin' , 'EG-Update', 'manage_options', 'egu_tools', array(&$this, 'update_plugin')); } // Update the option "update_plugins" to add the plugin function update_new_version($plugin, $new_version) { // Get the option $current = get_site_transient( 'update_plugins' ); if (! isset($current->response)) $current->response = array(); // Fill the new record $plugin_info = new stdClass; $plugin_info->id = 999 + rand(1, 500); $plugin_info->new_version = $new_version; $plugin_info->slug = $plugin; $plugin_info->url = 'http://localhost/wp330/my_repository/'.$plugin.'/'; $plugin_info->package = 'http://localhost/wp330/my_repository/'.$plugin.'.'.$new_version.'.zip'; // Save the option $current->response[$plugin] = $plugin_info; set_site_transient('update_plugins', $current ); } // End of get_new_version // Page EG-Update: form to select the plugin and enter the new version function update_plugin() { if (isset($_POST['egu_plugin']) && $_POST['egu_plugin'] != 'none' && isset($_POST['egu_new_version']) && $_POST['egu_new_version']!='') $this->update_new_version($_POST['egu_plugin'], $_POST['egu_new_version']); ?> <div class="wrap"> <?php screen_icon(); ?> <h2><?php echo 'EG-Update Plugin'; ?></h2><br /> <form action="" method="POST"> Select the plugin to update: <select id="egu_plugin" name="egu_plugin"> <option value="none"> </option> <?php // Get the list of installed plugins $plugins = get_plugins(); // File the select html tag foreach ($plugins as $file => $p) { echo '<option value="'.$file.'">'.$p['Name'].'</option>'; } ?> </select> <br />Enter the new version: <input type="text" id="egu_new_version" name="egu_new_version" value="" /> <?php echo submit_button(); ?> </form> </div> <?php } // End of update_plugin } // End of Class } // End of if class_exists $eg_update = new EG_Update(); $eg_update->load(); } // If is_admin ?> |
- En ligne 20, permet de spéficier la fonction qui sera utilisée pour la création du menu,
- En ligne 25 est crée le menu « Outils / EG-Update » proprement dit,
- La fonction qui débute en ligne 50, récupère la liste des plugins (ligne 66), boucle sur cette liste pour constuire la combo-box (lignes 68 à 70), et construit le formulaire complet. Elle permet également d’intercepter les informations saisies par l’utilisateur (lignes 52 à 55),
- La fonction
update_new_versionqui commence en ligne 29, effectue la modification de l’option_site_transient_update_plugins, à partir des informations fournies par le formulaire.
Il ne vous reste plus qu’a déposer la nouvelle version de votre plugin, au bon endroit. Dans cet exemple, le fichier eg-series.2.0.5.zip sera déposé dans le répertoire /temp/dev/htdocs/wp330/repository.
En sélectionnant le plugin EG-Series dans le formulaire, ainsi qu’une version supérieure à la version actuelle, nous voyons bien apparaître les notifications de mise à jour, comme prévu.
Vous pouvez télécharger le code complet ICI:
Pour qu’il fonctionne sur votre propre installation, il vous faudra éditer le plugin, pour y modifier notamment les chemins d’accès aux packages.
Conclusion
Pour un développeur, il est relativement simple de modifier le comportement du système de mise à jour, pour l’adapter à ses besoins, notamment:
- Exclure les plugins en cours de développement, de la liste des plugins dont il faut chercher une mise à jour
- Ou forcer la mise à jour d’un plugin, avec une nouvelle version se trouvant sur un serveur de développement
