WordPress: Personnaliser la mise à jour des plugins

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"		=> "https://wordpress.org/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"		=> "https://wordpress.org/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_version qui 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:


Plugin EG-Update
Titre: Plugin EG-Update (202 clics)
Légende: Plugin EG-Update
Nom du fichier: eg-update.zip
Taille: 1 KB

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

3 thoughts on “WordPress: Personnaliser la mise à jour des plugins”

  1. Bonjour,
    Auteur d’un petit plugin personnel qui ne présente pas d’intérêt général pour être intégré à WordPress.org, je cherchais un mécanisme de mise à jour, car je l’utilise sur plusieurs sites.
    Ma recherche sur google m’a donné votre page en première position.
    Cela correspond tout à fait à ce que je cherchais et je vais le mettre en oeuvre sans délai.
    Je vous tiendrai au courant du résultat mais je tenais à vous remercier pour cet article très clair et très complet.
    Une question cependant : pourquoi ne pas avoir versé ce plugin dans WordPress.org ?

    Il ne m’a pas échappé que le post date de 2012, mais il semble encore adapté.
    Merci

  2. Bonjour,

    Pour le changement du nom du plugin: pour moi, cela va empêcher WordPress de trouver les bonnes mises à jour, mais cela n’empêchera pas WordPress de faire les requêtes pour rechercher des mises a jour.

    Pour l’exclusion des plugins, la réponse est oui: il faut mettre le code dans chacun des plugins a exclure.

    Concernant les thèmes, oui également, mais la fonction est légèrement différente. L’exemple est donné dans l’article de Mark Jaquith.

  3. Bonjour,

    Merci pour cette astuce très pratique et le code du plugin.

    Si je change simplement le nom du plugin (« plugin name » dans l’en tête des commentaires du plugin), WordPress ne le trouvera plus dans son dépôt et ne proposera plus sa mise à jour.

    Cette solution fonctionne aussi non ?

    Il y a une chose poure laquelle je souhaiterais une confirmation :

    Le premier code « Exclure des plugins de la mise à jour », il se place dans le code du plugin que l’on veut exclure n’est ce pas ?

    Si on veut appliquer la méthode sur les thèmes, le code se place alors dans functions.php du thème ?

    Merci d’avance pour ces précisions.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *