PluginUtil: Add method to get active valid plugins (#48709)
* PluginUtil: Add method to get active valid plugins When getting a list of active plugins directly from the options table, you can run into an error if you try to then access one of the plugin files if it doesn't actually exist. WP Core has a method that verifies that the files exist before returning the list. But it's marked as a "private" method, which means it could change and break backcompat. That seems very unlikely, though, so we're using the method (it was already in use actually), but putting in some safeguards so we can detect if a backcompat issue arises. Fixes #48132 * Modify to accommodate multisite
This commit is contained in:
parent
79786c5443
commit
4d68cd486e
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: update
|
||||
|
||||
Ensure that active plugins shown in the System Status api endpoint actually exist
|
|
@ -17,7 +17,7 @@ use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Synchro
|
|||
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
|
||||
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper;
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
use Automattic\WooCommerce\Utilities\{ OrderUtil, PluginUtil };
|
||||
use Automattic\WooCommerce\Internal\Utilities\PluginInstaller;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
@ -1264,7 +1264,8 @@ class WC_Install {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( in_array( $legacy_api_plugin, wp_get_active_and_valid_plugins(), true ) ) {
|
||||
$active_valid_plugins = wc_get_container()->get( PluginUtil::class )->get_all_active_valid_plugins();
|
||||
if ( in_array( $legacy_api_plugin, $active_valid_plugins, true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ defined( 'ABSPATH' ) || exit;
|
|||
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper;
|
||||
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer as Order_DataSynchronizer;
|
||||
use Automattic\WooCommerce\Utilities\{ LoggingUtil, OrderUtil };
|
||||
use Automattic\WooCommerce\Utilities\{ LoggingUtil, OrderUtil, PluginUtil };
|
||||
|
||||
/**
|
||||
* System status controller class.
|
||||
|
@ -1044,16 +1044,11 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
|||
return array();
|
||||
}
|
||||
|
||||
$active_plugins = (array) get_option( 'active_plugins', array() );
|
||||
if ( is_multisite() ) {
|
||||
$network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
|
||||
$active_plugins = array_merge( $active_plugins, $network_activated_plugins );
|
||||
}
|
||||
$active_valid_plugins = wc_get_container()->get( PluginUtil::class )->get_all_active_valid_plugins();
|
||||
$active_plugins_data = array();
|
||||
|
||||
$active_plugins_data = array();
|
||||
|
||||
foreach ( $active_plugins as $plugin ) {
|
||||
$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
|
||||
foreach ( $active_valid_plugins as $plugin ) {
|
||||
$data = get_plugin_data( $plugin );
|
||||
$active_plugins_data[] = $this->format_plugin_data( $plugin, $data );
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Automattic\WooCommerce\Internal\Utilities;
|
|||
|
||||
use Automattic\WooCommerce\Internal\RegisterHooksInterface;
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
use Automattic\WooCommerce\Utilities\StringUtil;
|
||||
use Automattic\WooCommerce\Utilities\{ PluginUtil, StringUtil };
|
||||
|
||||
/**
|
||||
* This class allows installing a plugin programmatically.
|
||||
|
@ -206,7 +206,14 @@ class PluginInstaller implements RegisterHooksInterface {
|
|||
* @return bool True if WooCommerce is installed and active in the current blog, false otherwise.
|
||||
*/
|
||||
private static function woocommerce_is_active_in_current_site(): bool {
|
||||
return ! empty( array_filter( wp_get_active_and_valid_plugins(), fn( $plugin ) => substr_compare( $plugin, '/woocommerce.php', -strlen( '/woocommerce.php' ) ) === 0 ) );
|
||||
$active_valid_plugins = wc_get_container()->get( PluginUtil::class )->get_all_active_valid_plugins();
|
||||
|
||||
return ! empty(
|
||||
array_filter(
|
||||
$active_valid_plugins,
|
||||
fn( $plugin ) => substr_compare( $plugin, '/woocommerce.php', -strlen( '/woocommerce.php' ) ) === 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -67,6 +67,34 @@ class PluginUtil {
|
|||
require_once ABSPATH . WPINC . '/plugin.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for WP's private `wp_get_active_and_valid_plugins` and `wp_get_active_network_plugins` functions.
|
||||
*
|
||||
* This combines the results of the two functions to get a list of all plugins that are active within a site.
|
||||
* It's more useful than just retrieving the option values because it also validates that the plugin files exist.
|
||||
* This wrapper is also a hedge against backward-incompatible changes since both of the WP methods are marked as
|
||||
* being "@access private", so if need be we can update our methods here to preserve functionality.
|
||||
*
|
||||
* Note that the doc block for `wp_get_active_and_valid_plugins` says it returns "Array of paths to plugin files
|
||||
* relative to the plugins directory", but it actually returns absolute paths.
|
||||
*
|
||||
* @return string[] Array of absolute paths to plugin files.
|
||||
*/
|
||||
public function get_all_active_valid_plugins() {
|
||||
$local = wp_get_active_and_valid_plugins();
|
||||
|
||||
if ( is_multisite() ) {
|
||||
require_once ABSPATH . WPINC . '/ms-load.php';
|
||||
$network = wp_get_active_network_plugins();
|
||||
} else {
|
||||
$network = array();
|
||||
}
|
||||
|
||||
$all = array_merge( $local, $network );
|
||||
|
||||
return array_unique( $all );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list with the names of the WordPress plugins that are WooCommerce aware
|
||||
* (they have a "WC tested up to" header).
|
||||
|
|
|
@ -32,6 +32,65 @@ class PluginUtilTests extends \WC_Unit_Test_Case {
|
|||
$this->sut = $this->get_instance_of( PluginUtil::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox `get_all_active_valid_plugins` gets the active plugins *that actually exist* and returns them
|
||||
* as a list of absolute file paths.
|
||||
*
|
||||
* The tested function is just a wrapper around two core WP functions that are marked as "private" so this is
|
||||
* mostly just to ensure that there haven't been any breaking changes to those functions.
|
||||
*/
|
||||
public function test_get_all_active_valid_plugins() {
|
||||
self::touch( WP_PLUGIN_DIR . '/test1/test1.php' );
|
||||
self::touch( WP_PLUGIN_DIR . '/test2/test2_x.php' );
|
||||
self::touch( WP_PLUGIN_DIR . '/test3/test3.php' );
|
||||
|
||||
$orig_local_plugins = get_option( 'active_plugins' );
|
||||
$orig_network_plugins = get_site_option( 'active_sitewide_plugins' );
|
||||
|
||||
update_option(
|
||||
'active_plugins',
|
||||
array(
|
||||
'test1/test1.php',
|
||||
'test2/test2.php',
|
||||
)
|
||||
);
|
||||
|
||||
update_site_option(
|
||||
'active_sitewide_plugins',
|
||||
array( 'test3/test3.php' )
|
||||
);
|
||||
|
||||
$active_valid_plugins = $this->sut->get_all_active_valid_plugins();
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$this->assertCount( 2, $active_valid_plugins );
|
||||
$this->assertContains( WP_PLUGIN_DIR . '/test3/test3.php', $active_valid_plugins );
|
||||
} else {
|
||||
$this->assertCount( 1, $active_valid_plugins );
|
||||
}
|
||||
|
||||
$this->assertContains( WP_PLUGIN_DIR . '/test1/test1.php', $active_valid_plugins );
|
||||
|
||||
if ( false === $orig_local_plugins ) {
|
||||
delete_option( 'active_plugins' );
|
||||
} else {
|
||||
update_option( 'active_plugins', $orig_local_plugins );
|
||||
}
|
||||
|
||||
if ( false === $orig_network_plugins ) {
|
||||
delete_site_option( 'active_sitewide_plugins' );
|
||||
} else {
|
||||
update_site_option( 'active_sitewide_plugins', $orig_network_plugins );
|
||||
}
|
||||
|
||||
$this->rmdir( WP_PLUGIN_DIR . '/test1' );
|
||||
$this->rmdir( WP_PLUGIN_DIR . '/test2' );
|
||||
$this->rmdir( WP_PLUGIN_DIR . '/test3' );
|
||||
$this->delete_folders( WP_PLUGIN_DIR . '/test1' );
|
||||
$this->delete_folders( WP_PLUGIN_DIR . '/test2' );
|
||||
$this->delete_folders( WP_PLUGIN_DIR . '/test3' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox 'get_woocommerce_aware_plugins' properly gets the names of all the existing WooCommerce aware plugins.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue