TGM Plugin Activation (TGMPA) is a discontinued PHP library that some WordPress authors use to handle distributing theme-required plugins. TGMPA was last updated in March of 2016, and with that age comes a bit of baggage. The main issue comes with management. They have a separate page to manage and run updates. This is super annoying when managing many WordPress websites as it requires manual effort to run TGM plugin updates per each site.
Injecting TGMPA into WordPress updates
Their approach is overly complex. Instead of a separate page to manage updates TGMPA should inject its customized updates into WordPress through two filter hooks plugins_api
and site_transient_update_plugins
. That would allow these plugins to be updated from /wp-admin/plugins.php
and also from WP-CLI. So after a bit of trial and error, I came up with the following code to do just that.
<?php
class CaptainCoreInjectTGMPAUpdater {
public function __construct() {
if ( class_exists("TGM_Plugin_Activation") && has_action("tgmpa_register") ) {
add_filter( 'plugins_api', [ $this, 'info' ], 20, 3 );
add_filter( 'site_transient_update_plugins', [ $this, 'update' ] );
}
}
public function plugins() {
// fetch tgmpa plugins with external downloads
do_action("tgmpa_register");
$tgmpa = \TGM_Plugin_Activation::get_instance();
$tgmpa->populate_file_path();
if ( empty( $tgmpa->plugins ) ) {
return [];
}
foreach( $tgmpa->plugins as $key => $plugin ) {
if ( $plugin["source_type"] != "external" ) {
unset( $tgmpa->plugins[ $key ] );
}
}
return $tgmpa->plugins;
}
function info( $response, $action, $args ) {
// do nothing if you're not getting plugin information right now
if ( 'plugin_information' !== $action || empty( $response ) ) {
return $response;
}
// get updates
$plugins = $this->plugins();
if ( empty( $plugins ) ) {
return $response;
}
// do nothing if plugin not in TGM Plugin Activation
if ( empty( $args->slug ) || ! in_array( $args->slug, array_keys( $plugins ) ) ) {
return $response;
}
$response->version = $plugins[ $args->slug ]["version"];
$response->download_link = $plugins[ $args->slug ]["source"];
$response->trunk = $plugins[ $args->slug ]["source"];
return $response;
}
public function update( $transient ) {
if ( empty( $transient->checked ) ) {
return $transient;
}
$plugins = $this->plugins();
$installed_plugins = get_plugins();
$installed_plugins_file_paths = array_keys( $installed_plugins );
foreach( $plugins as $plugin ) {
$plugin = (object) $plugin;
// Skip if TGMPA plugin not installed
if ( ! in_array( $plugin->file_path, $installed_plugins_file_paths ) ) {
continue;
}
$response = new \stdClass();
$response->slug = $plugin->slug;
$response->plugin = $plugin->file_path;
$response->new_version = $plugin->version;
$response->package = $plugin->source;
if ( version_compare( $installed_plugins[ $plugin->file_path ]["Version"], $plugin->version, '<' ) ) {
$transient->response[ $plugin->file_path ] = $response;
}
}
return $transient;
}
}
$tgmpa_updater = new CaptainCoreInjectTGMPAUpdater();
add_action( 'wp_loaded', [ $tgmpa_updater, '__construct' ] );
My initial attempts had issues running after the TGM_Plugin_Activation class was loaded. While hooking into the wp_loaded
might not be the most efficient hook, it was the first hook I found that seemed to work consistently. With the above code loaded into a must-use plugin, both WP-CLI and WordPress updates on /wp-admin/
now work with any TGM plugins.