PluginUtil: Add method to get active valid plugins (2nd attempt) (#50974)

* Restore changes from 48709

* Update the PluginUtil method and related unit test

* Update API endpoint, fix schemas

* Improve unit tests for API controllers
This commit is contained in:
Corey McKrill 2024-08-27 23:44:17 -07:00 committed by GitHub
parent 4dbc8aa74b
commit eb868090b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 126 additions and 25 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Ensure that active plugins shown in the System Status API endpoint actually exist

View File

@ -17,7 +17,7 @@ use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Synchro
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil; use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper; use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper;
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods; use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
use Automattic\WooCommerce\Utilities\OrderUtil; use Automattic\WooCommerce\Utilities\{ OrderUtil, PluginUtil };
use Automattic\WooCommerce\Internal\Utilities\PluginInstaller; use Automattic\WooCommerce\Internal\Utilities\PluginInstaller;
defined( 'ABSPATH' ) || exit; defined( 'ABSPATH' ) || exit;
@ -1283,7 +1283,8 @@ class WC_Install {
return; 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; return;
} }

View File

@ -13,7 +13,7 @@ defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper; use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper;
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories; use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories;
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer as Order_DataSynchronizer; 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. * System status controller class.
@ -373,7 +373,41 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
'context' => array( 'view' ), 'context' => array( 'view' ),
'readonly' => true, 'readonly' => true,
'items' => array( 'items' => array(
'type' => 'string', 'type' => 'object',
'properties' => array(
'plugin' => array(
'description' => __( 'Plugin basename. The path to the main plugin file relative to the plugins directory.', 'woocommerce' ),
'type' => 'string',
),
'name' => array(
'description' => __( 'Name of the plugin.', 'woocommerce' ),
'type' => 'string',
),
'version' => array(
'description' => __( 'Current plugin version.', 'woocommerce' ),
'type' => 'string',
),
'version_latest' => array(
'description' => __( 'Latest available plugin version.', 'woocommerce' ),
'type' => 'string',
),
'url' => array(
'description' => __( 'Plugin URL.', 'woocommerce' ),
'type' => 'string',
),
'author_name' => array(
'description' => __( 'Plugin author name.', 'woocommerce' ),
'type' => 'string',
),
'author_url' => array(
'description' => __( 'Plugin author URL.', 'woocommerce' ),
'type' => 'string',
),
'network_activated' => array(
'description' => __( 'Whether the plugin can only be activated network-wide.', 'woocommerce' ),
'type' => 'boolean',
),
),
), ),
), ),
'inactive_plugins' => array( 'inactive_plugins' => array(
@ -382,7 +416,41 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
'context' => array( 'view' ), 'context' => array( 'view' ),
'readonly' => true, 'readonly' => true,
'items' => array( 'items' => array(
'type' => 'string', 'type' => 'object',
'properties' => array(
'plugin' => array(
'description' => __( 'Plugin basename. The path to the main plugin file relative to the plugins directory.', 'woocommerce' ),
'type' => 'string',
),
'name' => array(
'description' => __( 'Name of the plugin.', 'woocommerce' ),
'type' => 'string',
),
'version' => array(
'description' => __( 'Current plugin version.', 'woocommerce' ),
'type' => 'string',
),
'version_latest' => array(
'description' => __( 'Latest available plugin version.', 'woocommerce' ),
'type' => 'string',
),
'url' => array(
'description' => __( 'Plugin URL.', 'woocommerce' ),
'type' => 'string',
),
'author_name' => array(
'description' => __( 'Plugin author name.', 'woocommerce' ),
'type' => 'string',
),
'author_url' => array(
'description' => __( 'Plugin author URL.', 'woocommerce' ),
'type' => 'string',
),
'network_activated' => array(
'description' => __( 'Whether the plugin can only be activated network-wide.', 'woocommerce' ),
'type' => 'boolean',
),
),
), ),
), ),
'dropins_mu_plugins' => array( 'dropins_mu_plugins' => array(
@ -1044,15 +1112,10 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
return array(); return array();
} }
$active_plugins = (array) get_option( 'active_plugins', array() ); $active_valid_plugins = wc_get_container()->get( PluginUtil::class )->get_all_active_valid_plugins();
if ( is_multisite() ) { $active_plugins_data = array();
$network_activated_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
$active_plugins = array_merge( $active_plugins, $network_activated_plugins );
}
$active_plugins_data = array(); foreach ( $active_valid_plugins as $plugin ) {
foreach ( $active_plugins as $plugin ) {
$data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); $data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
$active_plugins_data[] = $this->format_plugin_data( $plugin, $data ); $active_plugins_data[] = $this->format_plugin_data( $plugin, $data );
} }

View File

@ -4,7 +4,7 @@ namespace Automattic\WooCommerce\Internal\Utilities;
use Automattic\WooCommerce\Internal\RegisterHooksInterface; use Automattic\WooCommerce\Internal\RegisterHooksInterface;
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods; use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
use Automattic\WooCommerce\Utilities\StringUtil; use Automattic\WooCommerce\Utilities\{ PluginUtil, StringUtil };
/** /**
* This class allows installing a plugin programmatically. * 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. * @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 { 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
)
);
} }
/** /**

View File

@ -78,7 +78,7 @@ class PluginUtil {
* Note that the doc block for `wp_get_active_and_valid_plugins` says it returns "Array of paths to plugin files * 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. * relative to the plugins directory", but it actually returns absolute paths.
* *
* @return string[] Array of absolute paths to plugin files. * @return string[] Array of plugin basenames (paths relative to the plugin directory).
*/ */
public function get_all_active_valid_plugins() { public function get_all_active_valid_plugins() {
$local = wp_get_active_and_valid_plugins(); $local = wp_get_active_and_valid_plugins();
@ -91,8 +91,11 @@ class PluginUtil {
} }
$all = array_merge( $local, $network ); $all = array_merge( $local, $network );
$all = array_unique( $all );
$all = array_map( 'plugin_basename', $all );
sort( $all );
return array_unique( $all ); return $all;
} }
/** /**

View File

@ -154,9 +154,8 @@ class WC_Tests_REST_System_Status_V2 extends WC_REST_Unit_Test_Case {
* @since 3.0.0 * @since 3.0.0
*/ */
public function test_get_system_status_info_active_plugins() { public function test_get_system_status_info_active_plugins() {
$this->skip_on_php_8_1();
wp_set_current_user( self::$administrator_user ); wp_set_current_user( self::$administrator_user );
delete_transient( 'wc_system_status_active_plugins' );
$actual_plugins = array( 'hello.php' ); $actual_plugins = array( 'hello.php' );
update_option( 'active_plugins', $actual_plugins ); update_option( 'active_plugins', $actual_plugins );
@ -165,9 +164,20 @@ class WC_Tests_REST_System_Status_V2 extends WC_REST_Unit_Test_Case {
$data = $response->get_data(); $data = $response->get_data();
$plugins = (array) $data['active_plugins']; $plugins = (array) $data['active_plugins'];
$this->assertEquals( 1, count( $plugins ) ); $this->assertEquals( 1, count( $plugins ) );
$this->assertEquals( 'Hello Dolly', $plugins[0]['name'] );
$plugin = reset( $plugins );
$this->assertArrayHasKey( 'plugin', $plugin );
$this->assertEquals( 'hello.php', $plugin['plugin'] );
$this->assertArrayHasKey( 'name', $plugin );
$this->assertEquals( 'Hello Dolly', $plugin['name'] );
$this->assertArrayHasKey( 'version', $plugin );
$this->assertArrayHasKey( 'version_latest', $plugin );
$this->assertArrayHasKey( 'url', $plugin );
$this->assertArrayHasKey( 'author_name', $plugin );
$this->assertArrayHasKey( 'author_url', $plugin );
$this->assertArrayHasKey( 'network_activated', $plugin );
$this->assertEquals( false, $plugin['network_activated'] );
} }
/** /**

View File

@ -183,6 +183,8 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case {
*/ */
public function test_get_system_status_info_active_plugins() { public function test_get_system_status_info_active_plugins() {
wp_set_current_user( self::$administrator_user ); wp_set_current_user( self::$administrator_user );
delete_transient( 'wc_system_status_active_plugins' );
$actual_plugins = array( 'hello.php' ); $actual_plugins = array( 'hello.php' );
update_option( 'active_plugins', $actual_plugins ); update_option( 'active_plugins', $actual_plugins );
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/system_status' ) ); $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/system_status' ) );
@ -190,9 +192,20 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case {
$data = $response->get_data(); $data = $response->get_data();
$plugins = (array) $data['active_plugins']; $plugins = (array) $data['active_plugins'];
$this->assertEquals( 1, count( $plugins ) ); $this->assertEquals( 1, count( $plugins ) );
$this->assertEquals( 'Hello Dolly', $plugins[0]['name'] );
$plugin = reset( $plugins );
$this->assertArrayHasKey( 'plugin', $plugin );
$this->assertEquals( 'hello.php', $plugin['plugin'] );
$this->assertArrayHasKey( 'name', $plugin );
$this->assertEquals( 'Hello Dolly', $plugin['name'] );
$this->assertArrayHasKey( 'version', $plugin );
$this->assertArrayHasKey( 'version_latest', $plugin );
$this->assertArrayHasKey( 'url', $plugin );
$this->assertArrayHasKey( 'author_name', $plugin );
$this->assertArrayHasKey( 'author_url', $plugin );
$this->assertArrayHasKey( 'network_activated', $plugin );
$this->assertEquals( false, $plugin['network_activated'] );
} }
/** /**

View File

@ -64,12 +64,12 @@ class PluginUtilTests extends \WC_Unit_Test_Case {
if ( is_multisite() ) { if ( is_multisite() ) {
$this->assertCount( 2, $active_valid_plugins ); $this->assertCount( 2, $active_valid_plugins );
$this->assertContains( WP_PLUGIN_DIR . '/test3/test3.php', $active_valid_plugins ); $this->assertContains( 'test3/test3.php', $active_valid_plugins );
} else { } else {
$this->assertCount( 1, $active_valid_plugins ); $this->assertCount( 1, $active_valid_plugins );
} }
$this->assertContains( WP_PLUGIN_DIR . '/test1/test1.php', $active_valid_plugins ); $this->assertContains( 'test1/test1.php', $active_valid_plugins );
if ( false === $orig_local_plugins ) { if ( false === $orig_local_plugins ) {
delete_option( 'active_plugins' ); delete_option( 'active_plugins' );